From 1ac0c1479200c2a798b245756a372a652af20e7b Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 11 Apr 2019 22:29:45 +0200 Subject: [PATCH 001/681] Fixed issue #895: Dialog after editing leaves the editor blank --- Source/VirtualTrees.pas | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index dd740f984..d686d3056 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -3283,6 +3283,7 @@ TVTEdit = class(TCustomEdit) function CalcMinHeight: Integer; virtual; procedure CreateParams(var Params: TCreateParams); override; function GetTextSize: TSize; virtual; + procedure KeyPress(var Key: Char); override; public constructor Create(Link: TStringEditLink); reintroduce; @@ -32890,6 +32891,13 @@ function TVTEdit.GetTextSize: TSize; end; end; +procedure TVTEdit.KeyPress(var Key: Char); +begin + if (Key = #13) and not (vsMultiline in FLink.FNode.States) then + Key := #0; // Filter out return keys as they will be added to the text, avoids #895 + inherited; +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TVTEdit.Release; From f701819e496d058e8d4c9a617b6e6d792011ddf4 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 12 Apr 2019 16:31:46 +0200 Subject: [PATCH 002/681] Added assign check. Issue #895 --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index d686d3056..233e107bb 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -32893,7 +32893,7 @@ function TVTEdit.GetTextSize: TSize; procedure TVTEdit.KeyPress(var Key: Char); begin - if (Key = #13) and not (vsMultiline in FLink.FNode.States) then + if (Key = #13) and Assigned(FLink) and not (vsMultiline in FLink.FNode.States) then Key := #0; // Filter out return keys as they will be added to the text, avoids #895 inherited; end; From 2a958b6b4790463a2f22ff669407a380c4478d61 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 18 Apr 2019 17:25:06 +0200 Subject: [PATCH 003/681] Improved instructions. https://stackoverflow.com/questions/55741919/how-to-install-virtual-treeview-in-delphi-10-3-rio --- INSTALL.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/INSTALL.txt b/INSTALL.txt index 22f3b1790..f63785f9f 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -12,6 +12,7 @@ Delphi / RAD Studio XE3 and higher Installation 2. Right click on "VirtualTreesD*.bpl" and click "Install" 3. Go to "Tools > Options > Environment Options > Delphi Options > Library > Library Path > [...]" Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK", "OK" + Do this for both Win32 and Win64 platform, which you can choose in the dropdown box. 4. C++ Builder users only: Go to "Tools > Options > Environment Options > C++ Options > Paths and Directories" a) Click "Library Path > [...]" From d1cdca2c7edc275bd575d7faee1e87f23daac08a Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 18 Apr 2019 20:36:12 +0200 Subject: [PATCH 004/681] Updated installation instructions. #899 --- INSTALL.txt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/INSTALL.txt b/INSTALL.txt index f63785f9f..ef02125fc 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,24 +1,22 @@ Supported Delphi version: RAD Studio XE3 and higher -Supported Windows Versions: Windows XP and higher +Supported Windows Versions: Windows Vista and higher - -The former installer of V4 is no longer maintained, but installation is quite easy: Extract the entire(!) ZIP file and follow the instructions below. - Delphi / RAD Studio XE3 and higher Installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. Open the project group "Packages\RAD Studio XE*\VirtualTreeView.groupproj" +1. Open the project group "Packages\RAD Studio *\VirtualTreeView.groupproj" 2. Right click on "VirtualTreesD*.bpl" and click "Install" 3. Go to "Tools > Options > Environment Options > Delphi Options > Library > Library Path > [...]" - Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK", "OK" - Do this for both Win32 and Win64 platform, which you can choose in the dropdown box. + Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" + Do this for both Win32 and Win64 platform, which you can choose in the dropdown box. 4. C++ Builder users only: - Go to "Tools > Options > Environment Options > C++ Options > Paths and Directories" + In the Options dialog go to "Environment Options > C++ Options > Paths and Directories" a) Click "Library Path > [...]" Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" b) Click "System Include path > [...]" - Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK", "OK"" + Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK", +5. Close the RAD Studio Options dialog by clicking "OK" / Save". Troubleshooting @@ -35,6 +33,7 @@ I recommand using UltraSearch for this task: http://www.jam-software.de/ultrasea Please send comments and suggestions regarding the packages and the install instructions to joachim.marder@gmail.com or open an issue. + C++ Builder XE3 and higher Installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Open the project group "Packages\CBullder XE*\VirtualTreeView.groupproj" From 09482f4a707d3fc51b44b5acb11212fa10bea1b9 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 20 Apr 2019 23:15:02 +0200 Subject: [PATCH 005/681] Updated installation instrcutions for 10.3. #899 --- INSTALL.txt | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/INSTALL.txt b/INSTALL.txt index ef02125fc..e1fad1c5b 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -3,7 +3,23 @@ Supported Windows Versions: Windows Vista and higher Extract the entire(!) ZIP file and follow the instructions below. -Delphi / RAD Studio XE3 and higher Installation +Delphi / RAD Studio 10.3 and higher Installation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. Open the project group "Packages\RAD Studio *\VirtualTreeView.groupproj" +2. Right click on "VirtualTreesD*.bpl" and click "Install" +3. Go to "Tools > Options > Language > Delphi Options > Library > Library Path > [...]" + Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" + Do this for both Win32 and Win64 platform, which you can choose in the dropdown box. +4. C++ Builder users only: + In the Options dialog go to "Environment Options > C++ Options > Paths and Directories" + a) Click "Library Path > [...]" + Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" + b) Click "System Include path > [...]" + Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" +5. Close the RAD Studio Options dialog by clicking "Save". + + +Delphi / RAD Studio XE3 - 10.2 Installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Open the project group "Packages\RAD Studio *\VirtualTreeView.groupproj" 2. Right click on "VirtualTreesD*.bpl" and click "Install" @@ -15,8 +31,8 @@ Delphi / RAD Studio XE3 and higher Installation a) Click "Library Path > [...]" Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" b) Click "System Include path > [...]" - Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK", -5. Close the RAD Studio Options dialog by clicking "OK" / Save". + Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" +5. Close the RAD Studio Options dialog by clicking "OK". Troubleshooting From 03d56e234d9fe835062b83b17ea478faccfe2607 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 22 Apr 2019 22:33:51 +0200 Subject: [PATCH 006/681] Fixed #893: Odd behaviour of TAB when stringeditor is active --- Source/VirtualTrees.pas | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 233e107bb..eae2137f8 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -32791,12 +32791,17 @@ procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown); if Tree.IsEditing then begin Tree.InvalidateNode(FLink.FNode); - NextNode := Tree.GetNextVisible(FLink.FNode, True); + if ssShift in KeyDataToShiftState(Message.KeyData) then + NextNode := Tree.GetPreviousVisible(FLink.FNode, True) // Shift+Tab goes to previous mode + else + NextNode := Tree.GetNextVisible(FLink.FNode, True); Tree.EndEditNode; // check NextNode, otherwise we got AV if NextNode <> nil then begin - Tree.FocusedNode := NextNode; + // Continue editing next node + ClearSelection; + Tree.Selected[NextNode] := True; if Tree.CanEdit(Tree.FocusedNode, Tree.FocusedColumn) then Tree.DoEdit; end; From 1f14857b3f5db14787853fd8cc4099eb31798a04 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 28 Apr 2019 14:36:09 +0200 Subject: [PATCH 007/681] Chnaged member names of TVclStyleScrollBarsHook and TVclStyleScrollBarWindow to match base class. Related to issue #390. --- Source/VirtualTrees.StyleHooks.pas | 148 ++++++++++++++--------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index e47bc47d6..97cf20d5d 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -47,23 +47,23 @@ interface TVclStyleScrollBarsHook = class(TScrollingStyleHook) strict private type {$REGION 'TVclStyleScrollBarWindow'} - TVclStyleScrollBarWindow = class(TWinControl) + TScrollWindow = class(TWinControl) strict private - FScrollBarWindowOwner: TVclStyleScrollBarsHook; - FScrollBarVertical: Boolean; + FStyleHook: TVclStyleScrollBarsHook; + FVertical: Boolean; procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST; procedure WMEraseBkgnd(var Msg: TMessage); message WM_ERASEBKGND; procedure WMPaint(var Msg: TWMPaint); message WM_PAINT; public constructor Create(AOwner: TComponent); override; - property ScrollBarWindowOwner: TVclStyleScrollBarsHook read FScrollBarWindowOwner write FScrollBarWindowOwner; - property ScrollBarVertical: Boolean read FScrollBarVertical write FScrollBarVertical; + property StyleHook: TVclStyleScrollBarsHook read FStyleHook write FStyleHook; + property Vertical: Boolean read FVertical write FVertical; end; {$ENDREGION} private - FHorzScrollBarWindow: TVclStyleScrollBarWindow; + FHorzScrollWnd: TScrollWindow; FLeftMouseButtonDown: Boolean; - FVertScrollBarWindow: TVclStyleScrollBarWindow; + FVertScrollWnd: TScrollWindow; function NCMousePosToClient(const P: TPoint): TPoint; @@ -93,7 +93,7 @@ TVclStyleScrollBarWindow = class(TWinControl) procedure MouseLeave; override; procedure PaintScroll; override; function PointInTreeHeader(const P: TPoint): Boolean; - procedure UpdateScrollBarWindow; + procedure UpdateScroll; public constructor Create(AControl: TWinControl); override; destructor Destroy; override; @@ -127,8 +127,8 @@ procedure TVclStyleScrollBarsHook.CalcScrollBarsRect; begin BarInfo.cbSize := SizeOf(BarInfo); Ret := GetScrollBarInfo(Handle, Integer(OBJID_VSCROLL), BarInfo); - FVertScrollBarWindow.Visible := (seBorder in Control.StyleElements) and Ret and (not (STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0)); - FVertScrollBarWindow.Enabled := FVertScrollBarWindow.Visible and (not (STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0)); + FVertScrollWnd.Visible := (seBorder in Control.StyleElements) and Ret and (not (STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0)); + FVertScrollWnd.Enabled := FVertScrollWnd.Visible and (not (STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0)); end; procedure CalcHorizontalRects; @@ -138,8 +138,8 @@ procedure TVclStyleScrollBarsHook.CalcScrollBarsRect; begin BarInfo.cbSize := SizeOf(BarInfo); Ret := GetScrollBarInfo(Handle, Integer(OBJID_HSCROLL), BarInfo); - FHorzScrollBarWindow.Visible := (seBorder in Control.StyleElements) and Ret and (not (STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0)); - FHorzScrollBarWindow.Enabled := FHorzScrollBarWindow.Visible and (not (STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0)); + FHorzScrollWnd.Visible := (seBorder in Control.StyleElements) and Ret and (not (STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0)); + FHorzScrollWnd.Enabled := FHorzScrollWnd.Visible and (not (STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0)); end; begin @@ -150,12 +150,12 @@ procedure TVclStyleScrollBarsHook.CalcScrollBarsRect; constructor TVclStyleScrollBarsHook.Create(AControl: TWinControl); begin inherited; - FVertScrollBarWindow := TVclStyleScrollBarWindow.CreateParented(GetParent(Control.Handle)); - FVertScrollBarWindow.ScrollBarWindowOwner := Self; - FVertScrollBarWindow.ScrollBarVertical := True; + FVertScrollWnd := TScrollWindow.CreateParented(GetParent(Control.Handle)); + FVertScrollWnd.StyleHook := Self; + FVertScrollWnd.Vertical := True; - FHorzScrollBarWindow := TVclStyleScrollBarWindow.CreateParented(GetParent(Control.Handle)); - FHorzScrollBarWindow.ScrollBarWindowOwner := Self; + FHorzScrollWnd := TScrollWindow.CreateParented(GetParent(Control.Handle)); + FHorzScrollWnd.StyleHook := Self; VertSliderState := tsThumbBtnVertNormal; VertUpState := tsArrowBtnUpNormal; @@ -167,10 +167,10 @@ constructor TVclStyleScrollBarsHook.Create(AControl: TWinControl); destructor TVclStyleScrollBarsHook.Destroy; begin - FVertScrollBarWindow.ScrollBarWindowOwner := nil; - FreeAndNil(FVertScrollBarWindow); - FHorzScrollBarWindow.ScrollBarWindowOwner := nil; - FreeAndNil(FHorzScrollBarWindow); + FVertScrollWnd.StyleHook := nil; + FreeAndNil(FVertScrollWnd); + FHorzScrollWnd.StyleHook := nil; + FreeAndNil(FHorzScrollWnd); inherited; end; @@ -183,7 +183,7 @@ procedure TVclStyleScrollBarsHook.DrawHorzScrollBar(DC: HDC); if ((Handle = 0) or (DC = 0)) then Exit; - if FHorzScrollBarWindow.Visible and StyleServices.Available and (seBorder in Control.StyleElements) then + if FHorzScrollWnd.Visible and StyleServices.Available and (seBorder in Control.StyleElements) then begin B := TBitmap.Create; try @@ -197,17 +197,17 @@ procedure TVclStyleScrollBarsHook.DrawHorzScrollBar(DC: HDC); Details := StyleServices.GetElementDetails(tsUpperTrackHorzNormal); StyleServices.DrawElement(B.Canvas.Handle, Details, R); - if FHorzScrollBarWindow.Enabled then + if FHorzScrollWnd.Enabled then Details := StyleServices.GetElementDetails(HorzSliderState); StyleServices.DrawElement(B.Canvas.Handle, Details, HorzSliderRect); - if FHorzScrollBarWindow.Enabled then + if FHorzScrollWnd.Enabled then Details := StyleServices.GetElementDetails(HorzUpState) else Details := StyleServices.GetElementDetails(tsArrowBtnLeftDisabled); StyleServices.DrawElement(B.Canvas.Handle, Details, HorzUpButtonRect); - if FHorzScrollBarWindow.Enabled then + if FHorzScrollWnd.Enabled then Details := StyleServices.GetElementDetails(HorzDownState) else Details := StyleServices.GetElementDetails(tsArrowBtnRightDisabled); @@ -231,13 +231,13 @@ procedure TVclStyleScrollBarsHook.DrawVertScrollBar(DC: HDC); if ((Handle = 0) or (DC = 0)) then Exit; - if FVertScrollBarWindow.Visible and StyleServices.Available and (seBorder in Control.StyleElements) then + if FVertScrollWnd.Visible and StyleServices.Available and (seBorder in Control.StyleElements) then begin B := TBitmap.Create; try R := VertScrollRect; B.Width := R.Width; - B.Height := FVertScrollBarWindow.Height; // <> R.Height + B.Height := FVertScrollWnd.Height; // <> R.Height MoveWindowOrg(B.Canvas.Handle, -R.Left, -R.Top); R.Bottom := B.Height + R.Top; @@ -249,17 +249,17 @@ procedure TVclStyleScrollBarsHook.DrawVertScrollBar(DC: HDC); Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal); StyleServices.DrawElement(B.Canvas.Handle, Details, R); - if FVertScrollBarWindow.Enabled then + if FVertScrollWnd.Enabled then Details := StyleServices.GetElementDetails(VertSliderState); StyleServices.DrawElement(B.Canvas.Handle, Details, VertSliderRect); - if FVertScrollBarWindow.Enabled then + if FVertScrollWnd.Enabled then Details := StyleServices.GetElementDetails(VertUpState) else Details := StyleServices.GetElementDetails(tsArrowBtnUpDisabled); StyleServices.DrawElement(B.Canvas.Handle, Details, VertUpButtonRect); - if FVertScrollBarWindow.Enabled then + if FVertScrollWnd.Enabled then Details := StyleServices.GetElementDetails(VertDownState) else Details := StyleServices.GetElementDetails(tsArrowBtnDownDisabled); @@ -313,15 +313,15 @@ function TVclStyleScrollBarsHook.NCMousePosToClient(const P: TPoint): TPoint; procedure TVclStyleScrollBarsHook.PaintScroll; begin - if FVertScrollBarWindow.HandleAllocated then + if FVertScrollWnd.HandleAllocated then begin - FVertScrollBarWindow.Repaint; - RedrawWindow(FVertScrollBarWindow.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 + FVertScrollWnd.Repaint; + RedrawWindow(FVertScrollWnd.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 end; - if FHorzScrollBarWindow.HandleAllocated then + if FHorzScrollWnd.HandleAllocated then begin - FHorzScrollBarWindow.Repaint; - RedrawWindow(FHorzScrollBarWindow.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 + FHorzScrollWnd.Repaint; + RedrawWindow(FHorzScrollWnd.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 end; end; @@ -330,7 +330,7 @@ function TVclStyleScrollBarsHook.PointInTreeHeader(const P: TPoint): Boolean; Result := TBaseVirtualTree(Control).Header.InHeader(P); end; -procedure TVclStyleScrollBarsHook.UpdateScrollBarWindow; +procedure TVclStyleScrollBarsHook.UpdateScroll; var R: TRect; HeaderHeight: Integer; @@ -358,41 +358,41 @@ procedure TVclStyleScrollBarsHook.UpdateScrollBarWindow; Inc(BorderSize, GetSystemMetrics(SM_CYEDGE)); // VertScrollBarWindow - if FVertScrollBarWindow.Visible then + if FVertScrollWnd.Visible then begin R := VertScrollRect; if Control.UseRightToLeftScrollBar then OffsetRect(R, -R.Left + BorderSize, 0); - ShowWindow(FVertScrollBarWindow.Handle, SW_SHOW); - SetWindowPos(FVertScrollBarWindow.Handle, HWND_TOP, + ShowWindow(FVertScrollWnd.Handle, SW_SHOW); + SetWindowPos(FVertScrollWnd.Handle, HWND_TOP, Control.Left + R.Left + PaddingSize, Control.Top + R.Top + HeaderHeight + PaddingSize, R.Width, Control.Height - HeaderHeight - ((PaddingSize + BorderSize) * 2), // <> R.Height SWP_SHOWWINDOW); end else - ShowWindow(FVertScrollBarWindow.Handle, SW_HIDE); + ShowWindow(FVertScrollWnd.Handle, SW_HIDE); // HorzScrollBarWindow - if FHorzScrollBarWindow.Visible then + if FHorzScrollWnd.Visible then begin R := HorzScrollRect; if Control.UseRightToLeftScrollBar then OffsetRect(R, VertScrollRect.Width, 0); - ShowWindow(FHorzScrollBarWindow.Handle, SW_SHOW); - SetWindowPos(FHorzScrollBarWindow.Handle, HWND_TOP, + ShowWindow(FHorzScrollWnd.Handle, SW_SHOW); + SetWindowPos(FHorzScrollWnd.Handle, HWND_TOP, Control.Left + R.Left + PaddingSize, Control.Top + R.Top + HeaderHeight + PaddingSize, R.Width, R.Height, SWP_SHOWWINDOW); end else - ShowWindow(FHorzScrollBarWindow.Handle, SW_HIDE); + ShowWindow(FHorzScrollWnd.Handle, SW_HIDE); end; procedure TVclStyleScrollBarsHook.WMCaptureChanged(var Msg: TMessage); begin - if FVertScrollBarWindow.Visible and FVertScrollBarWindow.Enabled then + if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then begin if VertUpState = tsArrowBtnUpPressed then begin @@ -407,7 +407,7 @@ procedure TVclStyleScrollBarsHook.WMCaptureChanged(var Msg: TMessage); end; end; - if FHorzScrollBarWindow.Visible and FHorzScrollBarWindow.Enabled then + if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then begin if HorzUpState = tsArrowBtnLeftPressed then begin @@ -435,7 +435,7 @@ procedure TVclStyleScrollBarsHook.WMHScroll(var Msg: TWMHScroll); begin CallDefaultProc(TMessage(Msg)); if not (Msg.ScrollCode in [SB_THUMBTRACK, SB_THUMBPOSITION]) then - UpdateScrollBarWindow + UpdateScroll else PaintScroll; Handled := True; @@ -450,7 +450,7 @@ procedure TVclStyleScrollBarsHook.CMUpdateVclStyleScrollbars(var Msg: TMessage); procedure TVclStyleScrollBarsHook.WMKeyDown(var Msg: TMessage); begin CallDefaultProc(TMessage(Msg)); - UpdateScrollBarWindow; + UpdateScroll; Handled := True; end; @@ -464,7 +464,7 @@ procedure TVclStyleScrollBarsHook.WMKeyUp(var Msg: TMessage); procedure TVclStyleScrollBarsHook.WMLButtonDown(var Msg: TWMMouse); begin CallDefaultProc(TMessage(Msg)); - UpdateScrollBarWindow; + UpdateScroll; Handled := True; end; @@ -476,7 +476,7 @@ procedure TVclStyleScrollBarsHook.WMLButtonUp(var Msg: TWMMouse); ScreenToClient(Handle, P); if not PointInTreeHeader(P) then begin - if FVertScrollBarWindow.Visible then + if FVertScrollWnd.Visible then begin if VertSliderState = tsThumbBtnVertPressed then begin @@ -494,7 +494,7 @@ procedure TVclStyleScrollBarsHook.WMLButtonUp(var Msg: TWMMouse); VertDownState := tsArrowBtnDownNormal; end; - if FHorzScrollBarWindow.Visible then + if FHorzScrollWnd.Visible then begin if HorzSliderState = tsThumbBtnHorzPressed then begin @@ -624,7 +624,7 @@ procedure TVclStyleScrollBarsHook.WMNCLButtonDown(var Msg: TWMMouse); P := NCMousePosToClient(Point(Msg.XPos, Msg.YPos)); if not PointInTreeHeader(P) then begin - if FVertScrollBarWindow.Visible and FVertScrollBarWindow.Enabled then + if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then begin if PtInRect(VertSliderRect, P) then begin @@ -647,7 +647,7 @@ procedure TVclStyleScrollBarsHook.WMNCLButtonDown(var Msg: TWMMouse); VertUpState := tsArrowBtnUpPressed; end; - if FHorzScrollBarWindow.Visible and FHorzScrollBarWindow.Enabled then + if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then begin if PtInRect(HorzSliderRect, P) then begin @@ -681,7 +681,7 @@ procedure TVclStyleScrollBarsHook.WMNCLButtonUp(var Msg: TWMMouse); P := NCMousePosToClient(Point(Msg.XPos, Msg.YPos)); if not PointInTreeHeader(P) then begin - if FVertScrollBarWindow.Visible and FVertScrollBarWindow.Enabled then + if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then begin if VertSliderState = tsThumbBtnVertPressed then begin @@ -703,7 +703,7 @@ procedure TVclStyleScrollBarsHook.WMNCLButtonUp(var Msg: TWMMouse); VertUpState := tsArrowBtnUpNormal; end; - if FHorzScrollBarWindow.Visible and FHorzScrollBarWindow.Enabled then + if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then begin if HorzSliderState = tsThumbBtnHorzPressed then begin @@ -726,7 +726,7 @@ procedure TVclStyleScrollBarsHook.WMNCLButtonUp(var Msg: TWMMouse); end; CallDefaultProc(TMessage(Msg)); - if (FHorzScrollBarWindow.Visible) or (FVertScrollBarWindow.Visible) then + if (FHorzScrollWnd.Visible) or (FVertScrollWnd.Visible) then PaintScroll; end; @@ -750,7 +750,7 @@ procedure TVclStyleScrollBarsHook.WMNCMouseMove(var Msg: TWMMouse); end; MustUpdateScroll := False; - if FVertScrollBarWindow.Visible and FVertScrollBarWindow.Enabled then + if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then begin B := PtInRect(VertSliderRect, P); if B and (VertSliderState = tsThumbBtnVertNormal) then @@ -789,7 +789,7 @@ procedure TVclStyleScrollBarsHook.WMNCMouseMove(var Msg: TWMMouse); end; end; - if FHorzScrollBarWindow.Visible and FHorzScrollBarWindow.Enabled then + if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then begin B := PtInRect(HorzSliderRect, P); if B and (HorzSliderState = tsThumbBtnHorzNormal) then @@ -842,7 +842,7 @@ procedure TVclStyleScrollBarsHook.WMNCPaint(var Msg: TMessage); procedure TVclStyleScrollBarsHook.WMSize(var Msg: TMessage); begin CallDefaultProc(TMessage(Msg)); - UpdateScrollBarWindow; + UpdateScroll; PaintScroll; Handled := True; end; @@ -852,7 +852,7 @@ procedure TVclStyleScrollBarsHook.WMMove(var Msg: TMessage); CallDefaultProc(TMessage(Msg)); if not(tsWindowCreating in TBaseVirtualTree(Control).TreeStates) then begin - UpdateScrollBarWindow; + UpdateScroll; PaintScroll; end; Handled := True; @@ -867,7 +867,7 @@ procedure TVclStyleScrollBarsHook.WMVScroll(var Msg: TWMVScroll); begin CallDefaultProc(TMessage(Msg)); if not (Msg.ScrollCode in [SB_THUMBTRACK, SB_THUMBPOSITION]) then - UpdateScrollBarWindow + UpdateScroll else PaintScroll; Handled := True; @@ -875,25 +875,25 @@ procedure TVclStyleScrollBarsHook.WMVScroll(var Msg: TWMVScroll); { TVclStyleScrollBarsHook.TVclStyleScrollBarWindow } -constructor TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.Create(AOwner: TComponent); +constructor TVclStyleScrollBarsHook.TScrollWindow.Create(AOwner: TComponent); begin inherited; ControlStyle := ControlStyle + [csOverrideStylePaint]; - FScrollBarWindowOwner := nil; - FScrollBarVertical := False; + FStyleHook := nil; + FVertical := False; end; -procedure TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.WMEraseBkgnd(var Msg: TMessage); +procedure TVclStyleScrollBarsHook.TScrollWindow.WMEraseBkgnd(var Msg: TMessage); begin Msg.Result := 1; end; -procedure TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.WMNCHitTest(var Msg: TWMNCHitTest); +procedure TVclStyleScrollBarsHook.TScrollWindow.WMNCHitTest(var Msg: TWMNCHitTest); begin Msg.Result := HTTRANSPARENT; end; -procedure TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.WMPaint(var Msg: TWMPaint); +procedure TVclStyleScrollBarsHook.TScrollWindow.WMPaint(var Msg: TWMPaint); var PS: TPaintStruct; DC: HDC; @@ -901,20 +901,20 @@ procedure TVclStyleScrollBarsHook.TVclStyleScrollBarWindow.WMPaint(var Msg: TWMP begin BeginPaint(Handle, PS); try - if FScrollBarWindowOwner <> nil then + if FStyleHook <> nil then begin DC := GetWindowDC(Handle); try - if FScrollBarVertical then + if FVertical then begin - R := FScrollBarWindowOwner.VertScrollRect; + R := FStyleHook.VertScrollRect; MoveWindowOrg(DC, -R.Left, -R.Top); - FScrollBarWindowOwner.DrawVertScrollBar(DC); + FStyleHook.DrawVertScrollBar(DC); end else begin - R := FScrollBarWindowOwner.HorzScrollRect; + R := FStyleHook.HorzScrollRect; MoveWindowOrg(DC, -R.Left, -R.Top); - FScrollBarWindowOwner.DrawHorzScrollBar(DC); + FStyleHook.DrawHorzScrollBar(DC); end; finally ReleaseDC(Handle, DC); From 5b9fb1d8675aae17fba996778fab9f3b7b4d24c0 Mon Sep 17 00:00:00 2001 From: Stephan Plath Date: Wed, 1 May 2019 11:25:14 +0200 Subject: [PATCH 008/681] fixed invalid JSON format commas after "project" entries were missing --- Delphinus.Install.json | 117 +++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/Delphinus.Install.json b/Delphinus.Install.json index c5d4fc524..a193c6c82 100644 --- a/Delphinus.Install.json +++ b/Delphinus.Install.json @@ -1,63 +1,56 @@ { - "search_pathes": - [ - { - "pathes": "Source", - "platforms": "Win32;Win64" - } - ], - - "browsing_pathes": - [ - { - "pathes": "Source", - "platforms": "Win32;Win64" - } - ], - - "source_folders": - [ - { - "folder": "source", - "base": "", - "recursive": true, - "filter": "*;*.*" - }, - { - "folder": "Packages", - "base": "", - "recursive": true, - "filter": "*;*.*" - }, - { - "folder": "Design", - "base": "", - "recursive": true, - "filter": "*;*.*" - } - ], - - "projects": - [ - { - "project": "Packages\\RAD Studio XE3\\VirtualTreeView.groupproj" - "compiler_max": 26 - }, - { - "project": "Packages\\RAD Studio XE6\\VirtualTreeView.groupproj" - "compiler": 27 - }, - { - "project": "Packages\\RAD Studio XE7\\VirtualTreeView.groupproj" - "compiler": 28 - }, - { - "project": "Packages\\RAD Studio XE8\\VirtualTreeView.groupproj" - "compiler": 29 - }, - { - "project": "Packages\\RAD Studio 10\\VirtualTreeView.groupproj" - "compiler_min": 30 - } - ] -} \ No newline at end of file + "search_pathes": [ + { + "pathes": "Source", + "platforms": "Win32;Win64" + } + ], + "browsing_pathes": [ + { + "pathes": "Source", + "platforms": "Win32;Win64" + } + ], + "source_folders": [ + { + "folder": "source", + "base": "", + "recursive": true, + "filter": "*;*.*" + }, + { + "folder": "Packages", + "base": "", + "recursive": true, + "filter": "*;*.*" + }, + { + "folder": "Design", + "base": "", + "recursive": true, + "filter": "*;*.*" + } + ], + "projects": [ + { + "project": "Packages\\RAD Studio XE3\\VirtualTreeView.groupproj", + "compiler_max": 26 + }, + { + "project": "Packages\\RAD Studio XE6\\VirtualTreeView.groupproj", + "compiler": 27 + }, + { + "project": "Packages\\RAD Studio XE7\\VirtualTreeView.groupproj", + "compiler": 28 + }, + { + "project": "Packages\\RAD Studio XE8\\VirtualTreeView.groupproj", + "compiler": 29 + }, + { + "project": "Packages\\RAD Studio 10\\VirtualTreeView.groupproj", + "compiler_min": 30 + } + ] +} From 48968e26c835df09cb9cefcedb331d23de23208a Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 7 May 2019 15:23:55 +0200 Subject: [PATCH 009/681] TBaseVirtualTree.DoCancelEdit(): Move the call to SetFocus() into if-then-statement so that we do not run into an error if DoCancelEdit() is called from SetMainColumn() while the tree is not yet visible. --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index eae2137f8..eb9dca68e 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -19238,7 +19238,7 @@ procedure TBaseVirtualTree.DoBeforePaint(Canvas: TCanvas); //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoCancelEdit: Boolean; +function TBaseVirtualTree.DoCancelEdit(): Boolean; // Called when the current edit action or a pending edit must be cancelled. @@ -19253,8 +19253,8 @@ function TBaseVirtualTree.DoCancelEdit: Boolean; FOnEditCancelled(Self, FEditColumn); if not (csDestroying in ComponentState) then FEditLink := nil; + SetFocus(); end; - SetFocus(); end; //---------------------------------------------------------------------------------------------------------------------- From de211eddddabd569cdc2fc6195d6bde04a4296d1 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 28 May 2019 15:20:32 +0200 Subject: [PATCH 010/681] Fixed issue #906: Exception when deleting clicked node OnNodeClick event --- Source/VirtualTrees.pas | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index eb9dca68e..8b99e5741 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -22582,8 +22582,10 @@ procedure TBaseVirtualTree.HandleMouseUp(var Message: TWMMouse; const HitInfo: T if FLastHitInfo.HitNode <> nil then begin // Use THitInfo of mouse down here, see issue #692 DoNodeClick(FLastHitInfo); - InvalidateNode(FLastHitInfo.HitNode); - FLastHitInfo.HitNode := nil; // prevent firing the event again + if Assigned(FLastHitInfo.HitNode) then begin + InvalidateNode(FLastHitInfo.HitNode); + FLastHitInfo.HitNode := nil; // prevent firing the event again + end;//if end; // handle a pending edit event From c41f9ac4eed569bb3c44db01b76a9b4f8fae34ec Mon Sep 17 00:00:00 2001 From: KostovP Date: Thu, 27 Jun 2019 18:21:49 +0300 Subject: [PATCH 011/681] Fixed issue #909: Argument out of range @ TVTHeader.PrepareDrag --- Source/VirtualTrees.pas | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 8b99e5741..aafd339a4 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -10414,7 +10414,11 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; if Message.Msg = WM_LBUTTONDOWN then // Coordinates are already client area based. with TWMLButtonDown(Message) do - P := Point(XPos, YPos) + begin + P := Point(XPos, YPos); + // #909 + FDragStart := Treeview.ClientToScreen(p); + end else with TWMNCLButtonDown(Message) do begin @@ -17156,6 +17160,9 @@ procedure TBaseVirtualTree.WMLButtonDblClk(var Message: TWMLButtonDblClk); // Call inherited after doing our standard handling, as the event handler may close the form or re-fill the control, so our clicked node would be no longer valid. // Our standard handling does not do that. inherited; + // #909 + // if we show a modal form in the HandleMouseDblClick(), the mouse capture wont be released + if csCaptureMouse in ControlStyle then MouseCapture := False; finally DoStateChange([], [tsLeftDblClick]); end; @@ -22112,7 +22119,7 @@ procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitI DoColumnDblClick(HitInfo.HitColumn, KeysToShiftState(Message.Keys)); if HitInfo.HitNode <> nil then - DoNodeDblClick(HitInfo); + DoNodeDblClick(HitInfo); Node := nil; if (hiOnItem in HitInfo.HitPositions) and (HitInfo.HitColumn > NoColumn) and From 2feaa4eaa36de24fe43a2714e02da972f5460c2c Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 28 Jul 2019 17:03:23 +0200 Subject: [PATCH 012/681] Possible fix for #913: * Extracted method AutoScale() from TVTHeader.FontChanged() * Calling AutoScale() also from ChangeScale() --- Source/VirtualTrees.pas | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index aafd339a4..ab6ca48f5 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1332,6 +1332,7 @@ TVTHeader = class(TPersistent) FDoingAutoFitColumns: boolean; // Flag to avoid using the stored width for Main column procedure FontChanged(Sender: TObject); virtual; + procedure AutoScale(); virtual; function CanSplitterResize(P: TPoint): Boolean; function CanWriteColumns: Boolean; virtual; procedure ChangeScale(M, D: TDimension); virtual; @@ -9445,6 +9446,12 @@ destructor TVTHeader.Destroy; //---------------------------------------------------------------------------------------------------------------------- procedure TVTHeader.FontChanged(Sender: TObject); +begin + inherited; + AutoScale(); +end; + +procedure TVTHeader.AutoScale(); var I: Integer; lMaxHeight: Integer; @@ -9468,7 +9475,6 @@ procedure TVTHeader.FontChanged(Sender: TObject); // Set the calculated size Self.SetHeight(lMaxHeight); end; - Invalidate(nil); end; //---------------------------------------------------------------------------------------------------------------------- @@ -9790,6 +9796,7 @@ procedure TVTHeader.ChangeScale(M, D: Integer); begin Self.FColumns[I].Width := MulDiv(Self.FColumns[I].Width, M, D); end;//for I + AutoScale(); end; //---------------------------------------------------------------------------------------------------------------------- From 1781e8255a540a5f189fc71b4157de819b953fa7 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 22 Aug 2019 09:27:06 +0200 Subject: [PATCH 013/681] Fixed missing border of hint window if VCL styles are used. --- Source/VirtualTrees.pas | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ab6ca48f5..a641a058b 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -5484,6 +5484,7 @@ procedure TVirtualTreeHintWindow.Paint(); begin if Tree.VclStyleEnabled then begin + InflateRect(R, -1, -1); // Fixes missing border when VCL styles are used LDetails := StyleServices.GetElementDetails(thHintNormal); if StyleServices.GetElementColor(LDetails, ecGradientColor1, LColor) and (LColor <> clNone) then LGradientStart := LColor @@ -20854,7 +20855,7 @@ function TBaseVirtualTree.DoValidateCache: Boolean; // New cache entry to set up. with FPositionCache[Index] do begin - Node := CurrentNode; // EAccessViolation seen here in TreeSize V4.3.1 + Node := CurrentNode; // 2 EAccessViolation seen here in TreeSize V4.3.1 (Write of address 00000000) AbsoluteTop := CurrentTop; end; Inc(Index); From 2c1fa8ee7fa3b315da7abdfd70f44a4dbdecce3b Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 23 Aug 2019 21:29:49 +0200 Subject: [PATCH 014/681] Fixed issue #917: Inconsistent line ending style --- Packages/RAD Studio 10.2/VirtualTreesD.dpk | 14 +- Packages/RAD Studio 10.3/VirtualTreesD.dpk | 14 +- Source/VirtualTrees.Actions.pas | 638 +- Source/VirtualTrees.ClipBoard.pas | 812 +- Source/VirtualTrees.Utils.pas | 2558 ++-- Source/VirtualTrees.dtx | 12939 ++++++++++--------- 6 files changed, 8488 insertions(+), 8487 deletions(-) diff --git a/Packages/RAD Studio 10.2/VirtualTreesD.dpk b/Packages/RAD Studio 10.2/VirtualTreesD.dpk index c6250eea2..f621bc032 100644 --- a/Packages/RAD Studio 10.2/VirtualTreesD.dpk +++ b/Packages/RAD Studio 10.2/VirtualTreesD.dpk @@ -1,6 +1,6 @@ -package VirtualTreesD; - -{$R *.res} +package VirtualTreesD; + +{$R *.res} {$R '..\..\Design\VirtualTrees.dcr'} {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} {$ALIGN 8} @@ -31,12 +31,12 @@ package VirtualTreesD; {$DESIGNONLY} {$IMPLICITBUILD OFF} -requires +requires DesignIDE, VirtualTreesR; -contains +contains VirtualTreesReg in '..\..\Design\VirtualTreesReg.pas'; -end. - +end. + diff --git a/Packages/RAD Studio 10.3/VirtualTreesD.dpk b/Packages/RAD Studio 10.3/VirtualTreesD.dpk index d4cff33ac..eb9f5e095 100644 --- a/Packages/RAD Studio 10.3/VirtualTreesD.dpk +++ b/Packages/RAD Studio 10.3/VirtualTreesD.dpk @@ -1,6 +1,6 @@ -package VirtualTreesD; - -{$R *.res} +package VirtualTreesD; + +{$R *.res} {$R '..\..\Design\VirtualTrees.dcr'} {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} {$ALIGN 8} @@ -31,12 +31,12 @@ package VirtualTreesD; {$DESIGNONLY} {$IMPLICITBUILD OFF} -requires +requires DesignIDE, VirtualTreesR; -contains +contains VirtualTreesReg in '..\..\Design\VirtualTreesReg.pas'; -end. - +end. + diff --git a/Source/VirtualTrees.Actions.pas b/Source/VirtualTrees.Actions.pas index 0a74ac618..7951199b3 100644 --- a/Source/VirtualTrees.Actions.pas +++ b/Source/VirtualTrees.Actions.pas @@ -1,163 +1,163 @@ -unit VirtualTrees.Actions; - -interface - -uses - System.Classes, - System.Actions, - Vcl.Controls, - Vcl.ActnList, - VirtualTrees; - -type - TVirtualTreeAction = class(TCustomAction) - strict private - fTree: TBaseVirtualTree; // Member variable for the property "Control" - fTreeAutoDetect: Boolean; // True if a potential Virtual TreeView should be detected automatically, false if a specific Tree was assigned to the property "Tree" - fOnAfterExecute: TNotifyEvent; // Member variable for the OnAfterExecute event - function GetSelectedOnly: Boolean; // Setter for the property "SelectedOnly" - procedure SetSelectedOnly(const Value: Boolean); // Getter for the property "SelectedOnly" - strict protected - fFilter: TVirtualNodeStates; // Apply only of nodes which match these states - procedure SetControl(Value: TBaseVirtualTree); // Setter for the property "Control" - procedure Notification(AComponent: TComponent; Operation: TOperation); override; - procedure DoAfterExecute; virtual;// Fires the event "OnAfterExecute" - property SelectedOnly: Boolean read GetSelectedOnly write SetSelectedOnly default False; - public - function HandlesTarget(Target: TObject): Boolean; override; - procedure UpdateTarget(Target: TObject); override; - procedure ExecuteTarget(Target: TObject); override; - published - constructor Create(AOwner: TComponent); override; - function Update: Boolean; override; - property Control: TBaseVirtualTree read fTree write SetControl; - property OnAfterExecute: TNotifyEvent read fOnAfterExecute write fOnAfterExecute; // Executed after the action was performed - property Caption; - property Enabled; - property HelpContext; - property HelpType; - property Hint; - property ImageIndex; - property ShortCut; - property SecondaryShortCuts; - property Visible; - property OnHint; - end; - - TVirtualTreePerItemAction = class(TVirtualTreeAction) - strict private - fOnBeforeExecute: TNotifyEvent; - fOldCursor: TCursor; - strict protected - fToExecute: TVTGetNodeProc; // method which is executed per item to perform this action - procedure DoBeforeExecute(); - procedure DoAfterExecute(); override;// Fires the event "OnAfterExecute" - public - constructor Create(AOwner: TComponent); override; - procedure ExecuteTarget(Target: TObject); override; - published - property OnBeforeExecute: TNotifyEvent read fOnBeforeExecute write fOnBeforeExecute; - end; - - // A standard action which checkmarks nodes in a virtual treeview - TVirtualTreeCheckAll = class(TVirtualTreePerItemAction) - protected - fDesiredCheckState: TCheckState; - public - constructor Create(AOwner: TComponent); override; - published - property SelectedOnly; - property OnUpdate; - end; - - // A standard action which unchecks nodes in a virtual treeview - TVirtualTreeUncheckAll = class(TVirtualTreeCheckAll) - public - constructor Create(AOwner: TComponent); override; - end; - - TVirtualTreeSelectAll = class(TVirtualTreeAction) - public - procedure UpdateTarget(Target: TObject); override; - procedure ExecuteTarget(Target: TObject); override; - end; - - // Base class for actions that are applied to selected nodes only - TVirtualTreeForSelectedAction = class(TVirtualTreeAction) - public - constructor Create(AOwner: TComponent); override; - end; - - TVirtualTreeCopy = class(TVirtualTreeForSelectedAction) - public - procedure ExecuteTarget(Target: TObject); override; - end; - - TVirtualTreeCut = class(TVirtualTreeForSelectedAction) - public - procedure ExecuteTarget(Target: TObject); override; - end; - - TVirtualTreePaste = class(TVirtualTreeForSelectedAction) - public - procedure ExecuteTarget(Target: TObject); override; - end; - - TVirtualTreeDelete = class(TVirtualTreeForSelectedAction) - public - procedure ExecuteTarget(Target: TObject); override; - end; - -procedure Register; - - -implementation - -uses - WinApi.Windows, - Vcl.Forms; - -procedure Register; -begin - RegisterActions('VirtualTree', [TVirtualTreeCheckAll, TVirtualTreeUncheckAll, TVirtualTreeSelectAll, TVirtualTreeCopy, TVirtualTreeCut, TVirtualTreePaste, TVirtualTreeDelete], nil); -end; - -{ TVirtualTreeAction } - -constructor TVirtualTreeAction.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - fTree := nil; - fFilter := []; - fOnAfterExecute := nil; - fTreeAutoDetect := True; -end; - -function TVirtualTreeAction.GetSelectedOnly: Boolean; -begin - exit(TVirtualNodeState.vsSelected in fFilter); -end; - -procedure TVirtualTreeAction.SetSelectedOnly(const Value: Boolean); -begin - if Value then - Include(fFilter, TVirtualNodeState.vsSelected) - else - Exclude(fFilter, TVirtualNodeState.vsSelected); -end; - -procedure TVirtualTreeAction.DoAfterExecute; -begin - if Assigned(fOnAfterExecute) then - fOnAfterExecute(Self); -end; - -function TVirtualTreeAction.HandlesTarget(Target: TObject): Boolean; -begin - Result := (Target is TBaseVirtualTree); -end; - -function TVirtualTreeAction.Update(): Boolean; +unit VirtualTrees.Actions; + +interface + +uses + System.Classes, + System.Actions, + Vcl.Controls, + Vcl.ActnList, + VirtualTrees; + +type + TVirtualTreeAction = class(TCustomAction) + strict private + fTree: TBaseVirtualTree; // Member variable for the property "Control" + fTreeAutoDetect: Boolean; // True if a potential Virtual TreeView should be detected automatically, false if a specific Tree was assigned to the property "Tree" + fOnAfterExecute: TNotifyEvent; // Member variable for the OnAfterExecute event + function GetSelectedOnly: Boolean; // Setter for the property "SelectedOnly" + procedure SetSelectedOnly(const Value: Boolean); // Getter for the property "SelectedOnly" + strict protected + fFilter: TVirtualNodeStates; // Apply only of nodes which match these states + procedure SetControl(Value: TBaseVirtualTree); // Setter for the property "Control" + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + procedure DoAfterExecute; virtual;// Fires the event "OnAfterExecute" + property SelectedOnly: Boolean read GetSelectedOnly write SetSelectedOnly default False; + public + function HandlesTarget(Target: TObject): Boolean; override; + procedure UpdateTarget(Target: TObject); override; + procedure ExecuteTarget(Target: TObject); override; + published + constructor Create(AOwner: TComponent); override; + function Update: Boolean; override; + property Control: TBaseVirtualTree read fTree write SetControl; + property OnAfterExecute: TNotifyEvent read fOnAfterExecute write fOnAfterExecute; // Executed after the action was performed + property Caption; + property Enabled; + property HelpContext; + property HelpType; + property Hint; + property ImageIndex; + property ShortCut; + property SecondaryShortCuts; + property Visible; + property OnHint; + end; + + TVirtualTreePerItemAction = class(TVirtualTreeAction) + strict private + fOnBeforeExecute: TNotifyEvent; + fOldCursor: TCursor; + strict protected + fToExecute: TVTGetNodeProc; // method which is executed per item to perform this action + procedure DoBeforeExecute(); + procedure DoAfterExecute(); override;// Fires the event "OnAfterExecute" + public + constructor Create(AOwner: TComponent); override; + procedure ExecuteTarget(Target: TObject); override; + published + property OnBeforeExecute: TNotifyEvent read fOnBeforeExecute write fOnBeforeExecute; + end; + + // A standard action which checkmarks nodes in a virtual treeview + TVirtualTreeCheckAll = class(TVirtualTreePerItemAction) + protected + fDesiredCheckState: TCheckState; + public + constructor Create(AOwner: TComponent); override; + published + property SelectedOnly; + property OnUpdate; + end; + + // A standard action which unchecks nodes in a virtual treeview + TVirtualTreeUncheckAll = class(TVirtualTreeCheckAll) + public + constructor Create(AOwner: TComponent); override; + end; + + TVirtualTreeSelectAll = class(TVirtualTreeAction) + public + procedure UpdateTarget(Target: TObject); override; + procedure ExecuteTarget(Target: TObject); override; + end; + + // Base class for actions that are applied to selected nodes only + TVirtualTreeForSelectedAction = class(TVirtualTreeAction) + public + constructor Create(AOwner: TComponent); override; + end; + + TVirtualTreeCopy = class(TVirtualTreeForSelectedAction) + public + procedure ExecuteTarget(Target: TObject); override; + end; + + TVirtualTreeCut = class(TVirtualTreeForSelectedAction) + public + procedure ExecuteTarget(Target: TObject); override; + end; + + TVirtualTreePaste = class(TVirtualTreeForSelectedAction) + public + procedure ExecuteTarget(Target: TObject); override; + end; + + TVirtualTreeDelete = class(TVirtualTreeForSelectedAction) + public + procedure ExecuteTarget(Target: TObject); override; + end; + +procedure Register; + + +implementation + +uses + WinApi.Windows, + Vcl.Forms; + +procedure Register; +begin + RegisterActions('VirtualTree', [TVirtualTreeCheckAll, TVirtualTreeUncheckAll, TVirtualTreeSelectAll, TVirtualTreeCopy, TVirtualTreeCut, TVirtualTreePaste, TVirtualTreeDelete], nil); +end; + +{ TVirtualTreeAction } + +constructor TVirtualTreeAction.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + fTree := nil; + fFilter := []; + fOnAfterExecute := nil; + fTreeAutoDetect := True; +end; + +function TVirtualTreeAction.GetSelectedOnly: Boolean; +begin + exit(TVirtualNodeState.vsSelected in fFilter); +end; + +procedure TVirtualTreeAction.SetSelectedOnly(const Value: Boolean); +begin + if Value then + Include(fFilter, TVirtualNodeState.vsSelected) + else + Exclude(fFilter, TVirtualNodeState.vsSelected); +end; + +procedure TVirtualTreeAction.DoAfterExecute; +begin + if Assigned(fOnAfterExecute) then + fOnAfterExecute(Self); +end; + +function TVirtualTreeAction.HandlesTarget(Target: TObject): Boolean; +begin + Result := (Target is TBaseVirtualTree); +end; + +function TVirtualTreeAction.Update(): Boolean; begin Result := inherited; // If an OnUpdate event handler is assigned, TBasicAction.Update() will return True and so TBasicAction.UpdateTarget() will not be called. @@ -166,162 +166,162 @@ function TVirtualTreeAction.Update(): Boolean; SendAppMessage(CM_ACTIONUPDATE, 0, LPARAM(Self)) end; -procedure TVirtualTreeAction.UpdateTarget(Target: TObject); -begin - if fTreeAutoDetect and (Target is TBaseVirtualTree) then - fTree := (Target as TBaseVirtualTree); - Enabled := Assigned(Control) and not Control.IsEmpty and (not SelectedOnly or (Control.SelectedCount > 0)) -end; - -procedure TVirtualTreeAction.ExecuteTarget(Target: TObject); -begin - DoAfterExecute(); -end; - -procedure TVirtualTreeAction.Notification(AComponent: TComponent; Operation: TOperation); -begin - inherited Notification(AComponent, Operation); - if (Operation = opRemove) and (AComponent = FTree) then - FTree := nil; -end; - -procedure TVirtualTreeAction.SetControl(Value: TBaseVirtualTree); -begin - if Value <> fTree then begin - fTree := Value; - if Assigned(fTree) then begin - fTree.FreeNotification(Self);// register Self as a component that should be notified when fTree is about to be destroyed. - end;//if - // Do not update the target of this action if it wa set explicitely by the developer - fTreeAutoDetect := not Assigned(fTree); - end;//if -end; - - -{ TVirtualTreePerItemAction } - -constructor TVirtualTreePerItemAction.Create(AOwner: TComponent); -begin - inherited; - fToExecute := nil; - fOnBeforeExecute := nil; - fOldCursor := crNone; -end; - -procedure TVirtualTreePerItemAction.DoAfterExecute; -begin - inherited; - if fOldCursor <> crNone then - Screen.Cursor := fOldCursor; -end; - -procedure TVirtualTreePerItemAction.DoBeforeExecute; -begin - if Screen.Cursor <> crHourGlass then begin - fOldCursor := Screen.Cursor; - Screen.Cursor := crHourGlass; - end;//if - if Assigned(fOnBeforeExecute) then - fOnBeforeExecute(Self); -end; - -procedure TVirtualTreePerItemAction.ExecuteTarget(Target: TObject); -begin - DoBeforeExecute(); - Control.BeginUpdate(); - try - Control.IterateSubtree(nil, Self.fToExecute, nil, fFilter, true); - finally - Control.EndUpdate(); - DoAfterExecute(); - end; -end; - -{ TVirtualTreeCheckAll } - -constructor TVirtualTreeCheckAll.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - Hint := 'Check all items in the list'; - Caption := 'Check &All'; - fDesiredCheckState := csCheckedNormal; - fToExecute := procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean) - begin - if not Control.CheckState[Node].IsDisabled then - Control.CheckState[Node] := fDesiredCheckState; - end; -end; - - -{ TVirtualTreeUncheckAll } - -constructor TVirtualTreeUncheckAll.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - Hint := 'Uncheck all items in the list'; - Caption := '&Uncheck All'; - fDesiredCheckState := csUncheckedNormal; -end; - - -{ TVirtualStringSelectAll } - -procedure TVirtualTreeSelectAll.UpdateTarget(Target: TObject); -begin - Inherited; - //Enabled := Enabled and (toMultiSelect in Control.TreeOptions.SelectionOptions) // TreeOptions is protected :-( -end; - -procedure TVirtualTreeSelectAll.ExecuteTarget(Target: TObject); -begin - Control.SelectAll(False); - inherited; -end; - - -{ TVirtualTreeForSelectedAction } - -constructor TVirtualTreeForSelectedAction.Create(AOwner: TComponent); -begin - inherited; - SelectedOnly := True; -end; - - -{ TVirtualTreeCopy } - -procedure TVirtualTreeCopy.ExecuteTarget(Target: TObject); -begin - Control.CopyToClipboard(); - Inherited; -end; - - -{ TVirtualTreeCut } - -procedure TVirtualTreeCut.ExecuteTarget(Target: TObject); -begin - Control.CutToClipboard(); - Inherited; -end; - - -{ TVirtualTreePaste } - -procedure TVirtualTreePaste.ExecuteTarget(Target: TObject); -begin - Control.PasteFromClipboard(); - Inherited; -end; - - -{ TVirtualTreeDelete } - -procedure TVirtualTreeDelete.ExecuteTarget(Target: TObject); -begin - Control.DeleteSelectedNodes(); - Inherited; -end; - - -end. +procedure TVirtualTreeAction.UpdateTarget(Target: TObject); +begin + if fTreeAutoDetect and (Target is TBaseVirtualTree) then + fTree := (Target as TBaseVirtualTree); + Enabled := Assigned(Control) and not Control.IsEmpty and (not SelectedOnly or (Control.SelectedCount > 0)) +end; + +procedure TVirtualTreeAction.ExecuteTarget(Target: TObject); +begin + DoAfterExecute(); +end; + +procedure TVirtualTreeAction.Notification(AComponent: TComponent; Operation: TOperation); +begin + inherited Notification(AComponent, Operation); + if (Operation = opRemove) and (AComponent = FTree) then + FTree := nil; +end; + +procedure TVirtualTreeAction.SetControl(Value: TBaseVirtualTree); +begin + if Value <> fTree then begin + fTree := Value; + if Assigned(fTree) then begin + fTree.FreeNotification(Self);// register Self as a component that should be notified when fTree is about to be destroyed. + end;//if + // Do not update the target of this action if it wa set explicitely by the developer + fTreeAutoDetect := not Assigned(fTree); + end;//if +end; + + +{ TVirtualTreePerItemAction } + +constructor TVirtualTreePerItemAction.Create(AOwner: TComponent); +begin + inherited; + fToExecute := nil; + fOnBeforeExecute := nil; + fOldCursor := crNone; +end; + +procedure TVirtualTreePerItemAction.DoAfterExecute; +begin + inherited; + if fOldCursor <> crNone then + Screen.Cursor := fOldCursor; +end; + +procedure TVirtualTreePerItemAction.DoBeforeExecute; +begin + if Screen.Cursor <> crHourGlass then begin + fOldCursor := Screen.Cursor; + Screen.Cursor := crHourGlass; + end;//if + if Assigned(fOnBeforeExecute) then + fOnBeforeExecute(Self); +end; + +procedure TVirtualTreePerItemAction.ExecuteTarget(Target: TObject); +begin + DoBeforeExecute(); + Control.BeginUpdate(); + try + Control.IterateSubtree(nil, Self.fToExecute, nil, fFilter, true); + finally + Control.EndUpdate(); + DoAfterExecute(); + end; +end; + +{ TVirtualTreeCheckAll } + +constructor TVirtualTreeCheckAll.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + Hint := 'Check all items in the list'; + Caption := 'Check &All'; + fDesiredCheckState := csCheckedNormal; + fToExecute := procedure(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: Pointer; var Abort: Boolean) + begin + if not Control.CheckState[Node].IsDisabled then + Control.CheckState[Node] := fDesiredCheckState; + end; +end; + + +{ TVirtualTreeUncheckAll } + +constructor TVirtualTreeUncheckAll.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + Hint := 'Uncheck all items in the list'; + Caption := '&Uncheck All'; + fDesiredCheckState := csUncheckedNormal; +end; + + +{ TVirtualStringSelectAll } + +procedure TVirtualTreeSelectAll.UpdateTarget(Target: TObject); +begin + Inherited; + //Enabled := Enabled and (toMultiSelect in Control.TreeOptions.SelectionOptions) // TreeOptions is protected :-( +end; + +procedure TVirtualTreeSelectAll.ExecuteTarget(Target: TObject); +begin + Control.SelectAll(False); + inherited; +end; + + +{ TVirtualTreeForSelectedAction } + +constructor TVirtualTreeForSelectedAction.Create(AOwner: TComponent); +begin + inherited; + SelectedOnly := True; +end; + + +{ TVirtualTreeCopy } + +procedure TVirtualTreeCopy.ExecuteTarget(Target: TObject); +begin + Control.CopyToClipboard(); + Inherited; +end; + + +{ TVirtualTreeCut } + +procedure TVirtualTreeCut.ExecuteTarget(Target: TObject); +begin + Control.CutToClipboard(); + Inherited; +end; + + +{ TVirtualTreePaste } + +procedure TVirtualTreePaste.ExecuteTarget(Target: TObject); +begin + Control.PasteFromClipboard(); + Inherited; +end; + + +{ TVirtualTreeDelete } + +procedure TVirtualTreeDelete.ExecuteTarget(Target: TObject); +begin + Control.DeleteSelectedNodes(); + Inherited; +end; + + +end. diff --git a/Source/VirtualTrees.ClipBoard.pas b/Source/VirtualTrees.ClipBoard.pas index 197eed237..b406f0da8 100644 --- a/Source/VirtualTrees.ClipBoard.pas +++ b/Source/VirtualTrees.ClipBoard.pas @@ -1,407 +1,407 @@ -unit VirtualTrees.ClipBoard; - -// The contents of this file are subject to the Mozilla Public License -// Version 1.1 (the "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ -// -// Alternatively, you may redistribute this library, use and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation; -// either version 2.1 of the License, or (at your option) any later version. -// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/. -// -// Software distributed under the License is distributed on an "AS IS" basis, -// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the -// specific language governing rights and limitations under the License. -// -// The original code is VirtualTrees.pas, released September 30, 2000. -// -// The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de), -// written by Mike Lischke (public@soft-gems.net, www.soft-gems.net). -// -// Portions created by digital publishing AG are Copyright -// (C) 1999-2001 digital publishing AG. All Rights Reserved. -//---------------------------------------------------------------------------------------------------------------------- - - -interface - -{$WARN UNSAFE_TYPE OFF} +unit VirtualTrees.ClipBoard; + +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ +// +// Alternatively, you may redistribute this library, use and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation; +// either version 2.1 of the License, or (at your option) any later version. +// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/. +// +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +// specific language governing rights and limitations under the License. +// +// The original code is VirtualTrees.pas, released September 30, 2000. +// +// The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de), +// written by Mike Lischke (public@soft-gems.net, www.soft-gems.net). +// +// Portions created by digital publishing AG are Copyright +// (C) 1999-2001 digital publishing AG. All Rights Reserved. +//---------------------------------------------------------------------------------------------------------------------- + + +interface + +{$WARN UNSAFE_TYPE OFF} {$WARN UNSAFE_CAST OFF} - -uses - Winapi.Windows, - Winapi.ActiveX, - System.Classes, - VirtualTrees; - -type - TClipboardFormatEntry = record - ID: Word; - Description: string; - end; - -var - ClipboardDescriptions: array [1..CF_MAX - 1] of TClipboardFormatEntry = ( - (ID: CF_TEXT; Description: 'Plain text'), // Do not localize - (ID: CF_BITMAP; Description: 'Windows bitmap'), // Do not localize - (ID: CF_METAFILEPICT; Description: 'Windows metafile'), // Do not localize - (ID: CF_SYLK; Description: 'Symbolic link'), // Do not localize - (ID: CF_DIF; Description: 'Data interchange format'), // Do not localize - (ID: CF_TIFF; Description: 'Tiff image'), // Do not localize - (ID: CF_OEMTEXT; Description: 'OEM text'), // Do not localize - (ID: CF_DIB; Description: 'DIB image'), // Do not localize - (ID: CF_PALETTE; Description: 'Palette data'), // Do not localize - (ID: CF_PENDATA; Description: 'Pen data'), // Do not localize - (ID: CF_RIFF; Description: 'Riff audio data'), // Do not localize - (ID: CF_WAVE; Description: 'Wav audio data'), // Do not localize - (ID: CF_UNICODETEXT; Description: 'Unicode text'), // Do not localize - (ID: CF_ENHMETAFILE; Description: 'Enhanced metafile image'), // Do not localize - (ID: CF_HDROP; Description: 'File name(s)'), // Do not localize - (ID: CF_LOCALE; Description: 'Locale descriptor'), // Do not localize - (ID: CF_DIBV5; Description: 'DIB image V5') // Do not localize - ); - - -// OLE Clipboard and drag'n drop helper -procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; const List: TStrings); overload; -procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray); overload; -function GetVTClipboardFormatDescription(AFormat: Word): string; -procedure RegisterVTClipboardFormat(AFormat: Word; TreeClass: TVirtualTreeClass; Priority: Cardinal); overload; -function RegisterVTClipboardFormat(const Description: string; TreeClass: TVirtualTreeClass; Priority: Cardinal; - tymed: Integer = TYMED_HGLOBAL; ptd: PDVTargetDevice = nil; - dwAspect: Integer = DVASPECT_CONTENT; lindex: Integer = -1): Word; overload; - -//----------------- TClipboardFormats ---------------------------------------------------------------------------------- - -type - TClipboardFormatListEntry = class - public - Description: string; // The string used to register the format with Winapi.Windows. - TreeClass: TVirtualTreeClass; // The tree class which supports rendering this format. - Priority: Cardinal; // Number which determines the order of formats used in IDataObject. - FormatEtc: TFormatEtc; // The definition of the format in the IDataObject. - end; - - TClipboardFormatList = class - strict private - class function GetList(): TList; static; - class property List: TList read GetList; - protected - class procedure Sort; - public - class procedure Add(const FormatString: string; AClass: TVirtualTreeClass; Priority: Cardinal; AFormatEtc: TFormatEtc); - class procedure Clear; - class procedure EnumerateFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray; const AllowedFormats: TClipboardFormats = nil); overload; - class procedure EnumerateFormats(TreeClass: TVirtualTreeClass; const Formats: TStrings); overload; - class function FindFormat(const FormatString: string): TClipboardFormatListEntry; overload; - class function FindFormat(const FormatString: string; var Fmt: Word): TVirtualTreeClass; overload; - class function FindFormat(Fmt: Word; var Description: string): TVirtualTreeClass; overload; - end; - - -implementation - -uses - System.SysUtils; - -var - _List: TList = nil; //Note - not using class constructors as they are not supported on C++ Builder. See also issue # - -procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; const List: TStrings); - -begin - TClipboardFormatList.EnumerateFormats(TreeClass, List); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray); - -begin - TClipboardFormatList.EnumerateFormats(TreeClass, Formats); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function GetVTClipboardFormatDescription(AFormat: Word): string; - -begin - if TClipboardFormatList.FindFormat(AFormat, Result) = nil then - Result := ''; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure RegisterVTClipboardFormat(AFormat: Word; TreeClass: TVirtualTreeClass; Priority: Cardinal); - -// Registers the given clipboard format for the given TreeClass. - -var - I: Integer; - Buffer: array[0..2048] of Char; - FormatEtc: TFormatEtc; - -begin - - // Assumes a HGlobal format. - FormatEtc.cfFormat := AFormat; - FormatEtc.ptd := nil; - FormatEtc.dwAspect := DVASPECT_CONTENT; - FormatEtc.lindex := -1; - FormatEtc.tymed := TYMED_HGLOBAL; - - // Determine description string of the given format. For predefined formats we need the lookup table because they - // don't have a description string. For registered formats the description string is the string which was used - // to register them. - if AFormat < CF_MAX then - begin - for I := 1 to High(ClipboardDescriptions) do - if ClipboardDescriptions[I].ID = AFormat then - begin - TClipboardFormatList.Add(ClipboardDescriptions[I].Description, TreeClass, Priority, FormatEtc); - Break; - end; - end - else - begin - GetClipboardFormatName(AFormat, Buffer, Length(Buffer)); - TClipboardFormatList.Add(Buffer, TreeClass, Priority, FormatEtc); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function RegisterVTClipboardFormat(const Description: string; TreeClass: TVirtualTreeClass; Priority: Cardinal; - tymed: Integer = TYMED_HGLOBAL; ptd: PDVTargetDevice = nil; dwAspect: Integer = DVASPECT_CONTENT; - lindex: Integer = -1): Word; - -// Alternative method to register a certain clipboard format for a given tree class. Registration with the -// clipboard is done here too and the assigned ID returned by the function. -// tymed may contain or'ed TYMED constants which allows to register several storage formats for one clipboard format. - -var - FormatEtc: TFormatEtc; - -begin - Result := RegisterClipboardFormat(PChar(Description)); - FormatEtc.cfFormat := Result; - FormatEtc.ptd := ptd; - FormatEtc.dwAspect := dwAspect; - FormatEtc.lindex := lindex; - FormatEtc.tymed := tymed; - TClipboardFormatList.Add(Description, TreeClass, Priority, FormatEtc); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -class procedure TClipboardFormatList.Sort; - -// Sorts all entry for priority (increasing priority value). - - //--------------- local function -------------------------------------------- - procedure QuickSort(L, R: Integer); - - var - I, J: Integer; - P, T: TClipboardFormatListEntry; - - begin - repeat - I := L; - J := R; - P := _List[(L + R) shr 1]; - repeat - while TClipboardFormatListEntry(_List[I]).Priority < P.Priority do - Inc(I); - while TClipboardFormatListEntry(_List[J]).Priority > P.Priority do - Dec(J); - if I <= J then - begin - T := List[I]; - _List[I] := _List[J]; - _List[J] := T; - Inc(I); - Dec(J); - end; - until I > J; - if L < J then - QuickSort(L, J); - L := I; - until I >= R; - end; - //--------------- end local function ---------------------------------------- - -begin - if List.Count > 1 then - QuickSort(0, List.Count - 1); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -class procedure TClipboardFormatList.Add(const FormatString: string; AClass: TVirtualTreeClass; Priority: Cardinal; AFormatEtc: TFormatEtc); - -// Adds the given data to the internal list. The priority value is used to sort formats for importance. Larger priority -// values mean less priority. - -var - Entry: TClipboardFormatListEntry; - -begin - Entry := TClipboardFormatListEntry.Create; - Entry.Description := FormatString; - Entry.TreeClass := AClass; - Entry.Priority := Priority; - Entry.FormatEtc := AFormatEtc; - List.Add(Entry); - - Sort; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -class procedure TClipboardFormatList.Clear; - -var - I: Integer; - -begin - if Assigned(_List) then begin - for I := 0 to _List.Count - 1 do - TClipboardFormatListEntry(List[I]).Free; - _List.Clear; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -class procedure TClipboardFormatList.EnumerateFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray; const AllowedFormats: TClipboardFormats = nil); - -// Returns a list of format records for the given class. If assigned the AllowedFormats is used to limit the -// enumerated formats to those described in the list. - -var - I, Count: Integer; - Entry: TClipboardFormatListEntry; - -begin - SetLength(Formats, List.Count); - Count := 0; - for I := 0 to List.Count - 1 do - begin - Entry := List[I]; - // Does the tree class support this clipboard format? - if TreeClass.InheritsFrom(Entry.TreeClass) then - begin - // Is this format allowed to be included? - if (AllowedFormats = nil) or (AllowedFormats.IndexOf(Entry.Description) > -1) then - begin - // The list could change before we use the FormatEtc so it is best not to pass a pointer to the true FormatEtc - // structure. Instead make a copy and send that. - Formats[Count] := Entry.FormatEtc; - Inc(Count); - end; - end; - end; - SetLength(Formats, Count); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -class procedure TClipboardFormatList.EnumerateFormats(TreeClass: TVirtualTreeClass; const Formats: TStrings); - -// Returns a list of format descriptions for the given class. - -var - I: Integer; - Entry: TClipboardFormatListEntry; - -begin - for I := 0 to List.Count - 1 do - begin - Entry := List[I]; - if TreeClass.InheritsFrom(Entry.TreeClass) then - Formats.Add(Entry.Description); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -class function TClipboardFormatList.FindFormat(const FormatString: string): TClipboardFormatListEntry; - -var - I: Integer; - Entry: TClipboardFormatListEntry; - -begin - Result := nil; - for I := List.Count - 1 downto 0 do - begin - Entry := List[I]; - if CompareText(Entry.Description, FormatString) = 0 then - begin - Result := Entry; - Break; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -class function TClipboardFormatList.FindFormat(const FormatString: string; var Fmt: Word): TVirtualTreeClass; - -var - I: Integer; - Entry: TClipboardFormatListEntry; - -begin - Result := nil; - for I := List.Count - 1 downto 0 do - begin - Entry := List[I]; - if CompareText(Entry.Description, FormatString) = 0 then - begin - Result := Entry.TreeClass; - Fmt := Entry.FormatEtc.cfFormat; - Break; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -class function TClipboardFormatList.FindFormat(Fmt: Word; var Description: string): TVirtualTreeClass; - -var - I: Integer; - Entry: TClipboardFormatListEntry; - -begin - Result := nil; - for I := List.Count - 1 downto 0 do - begin - Entry := List[I]; - if Entry.FormatEtc.cfFormat = Fmt then - begin - Result := Entry.TreeClass; - Description := Entry.Description; - Break; - end; - end; -end; - - -class function TClipboardFormatList.GetList: TList; -begin - if not Assigned(_List) then - _List := TList.Create; - Exit(_List); -end; - -initialization - -finalization - - TClipboardFormatList.Clear; - FreeAndNil(_List); - -end. + +uses + Winapi.Windows, + Winapi.ActiveX, + System.Classes, + VirtualTrees; + +type + TClipboardFormatEntry = record + ID: Word; + Description: string; + end; + +var + ClipboardDescriptions: array [1..CF_MAX - 1] of TClipboardFormatEntry = ( + (ID: CF_TEXT; Description: 'Plain text'), // Do not localize + (ID: CF_BITMAP; Description: 'Windows bitmap'), // Do not localize + (ID: CF_METAFILEPICT; Description: 'Windows metafile'), // Do not localize + (ID: CF_SYLK; Description: 'Symbolic link'), // Do not localize + (ID: CF_DIF; Description: 'Data interchange format'), // Do not localize + (ID: CF_TIFF; Description: 'Tiff image'), // Do not localize + (ID: CF_OEMTEXT; Description: 'OEM text'), // Do not localize + (ID: CF_DIB; Description: 'DIB image'), // Do not localize + (ID: CF_PALETTE; Description: 'Palette data'), // Do not localize + (ID: CF_PENDATA; Description: 'Pen data'), // Do not localize + (ID: CF_RIFF; Description: 'Riff audio data'), // Do not localize + (ID: CF_WAVE; Description: 'Wav audio data'), // Do not localize + (ID: CF_UNICODETEXT; Description: 'Unicode text'), // Do not localize + (ID: CF_ENHMETAFILE; Description: 'Enhanced metafile image'), // Do not localize + (ID: CF_HDROP; Description: 'File name(s)'), // Do not localize + (ID: CF_LOCALE; Description: 'Locale descriptor'), // Do not localize + (ID: CF_DIBV5; Description: 'DIB image V5') // Do not localize + ); + + +// OLE Clipboard and drag'n drop helper +procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; const List: TStrings); overload; +procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray); overload; +function GetVTClipboardFormatDescription(AFormat: Word): string; +procedure RegisterVTClipboardFormat(AFormat: Word; TreeClass: TVirtualTreeClass; Priority: Cardinal); overload; +function RegisterVTClipboardFormat(const Description: string; TreeClass: TVirtualTreeClass; Priority: Cardinal; + tymed: Integer = TYMED_HGLOBAL; ptd: PDVTargetDevice = nil; + dwAspect: Integer = DVASPECT_CONTENT; lindex: Integer = -1): Word; overload; + +//----------------- TClipboardFormats ---------------------------------------------------------------------------------- + +type + TClipboardFormatListEntry = class + public + Description: string; // The string used to register the format with Winapi.Windows. + TreeClass: TVirtualTreeClass; // The tree class which supports rendering this format. + Priority: Cardinal; // Number which determines the order of formats used in IDataObject. + FormatEtc: TFormatEtc; // The definition of the format in the IDataObject. + end; + + TClipboardFormatList = class + strict private + class function GetList(): TList; static; + class property List: TList read GetList; + protected + class procedure Sort; + public + class procedure Add(const FormatString: string; AClass: TVirtualTreeClass; Priority: Cardinal; AFormatEtc: TFormatEtc); + class procedure Clear; + class procedure EnumerateFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray; const AllowedFormats: TClipboardFormats = nil); overload; + class procedure EnumerateFormats(TreeClass: TVirtualTreeClass; const Formats: TStrings); overload; + class function FindFormat(const FormatString: string): TClipboardFormatListEntry; overload; + class function FindFormat(const FormatString: string; var Fmt: Word): TVirtualTreeClass; overload; + class function FindFormat(Fmt: Word; var Description: string): TVirtualTreeClass; overload; + end; + + +implementation + +uses + System.SysUtils; + +var + _List: TList = nil; //Note - not using class constructors as they are not supported on C++ Builder. See also issue # + +procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; const List: TStrings); + +begin + TClipboardFormatList.EnumerateFormats(TreeClass, List); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure EnumerateVTClipboardFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray); + +begin + TClipboardFormatList.EnumerateFormats(TreeClass, Formats); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function GetVTClipboardFormatDescription(AFormat: Word): string; + +begin + if TClipboardFormatList.FindFormat(AFormat, Result) = nil then + Result := ''; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure RegisterVTClipboardFormat(AFormat: Word; TreeClass: TVirtualTreeClass; Priority: Cardinal); + +// Registers the given clipboard format for the given TreeClass. + +var + I: Integer; + Buffer: array[0..2048] of Char; + FormatEtc: TFormatEtc; + +begin + + // Assumes a HGlobal format. + FormatEtc.cfFormat := AFormat; + FormatEtc.ptd := nil; + FormatEtc.dwAspect := DVASPECT_CONTENT; + FormatEtc.lindex := -1; + FormatEtc.tymed := TYMED_HGLOBAL; + + // Determine description string of the given format. For predefined formats we need the lookup table because they + // don't have a description string. For registered formats the description string is the string which was used + // to register them. + if AFormat < CF_MAX then + begin + for I := 1 to High(ClipboardDescriptions) do + if ClipboardDescriptions[I].ID = AFormat then + begin + TClipboardFormatList.Add(ClipboardDescriptions[I].Description, TreeClass, Priority, FormatEtc); + Break; + end; + end + else + begin + GetClipboardFormatName(AFormat, Buffer, Length(Buffer)); + TClipboardFormatList.Add(Buffer, TreeClass, Priority, FormatEtc); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function RegisterVTClipboardFormat(const Description: string; TreeClass: TVirtualTreeClass; Priority: Cardinal; + tymed: Integer = TYMED_HGLOBAL; ptd: PDVTargetDevice = nil; dwAspect: Integer = DVASPECT_CONTENT; + lindex: Integer = -1): Word; + +// Alternative method to register a certain clipboard format for a given tree class. Registration with the +// clipboard is done here too and the assigned ID returned by the function. +// tymed may contain or'ed TYMED constants which allows to register several storage formats for one clipboard format. + +var + FormatEtc: TFormatEtc; + +begin + Result := RegisterClipboardFormat(PChar(Description)); + FormatEtc.cfFormat := Result; + FormatEtc.ptd := ptd; + FormatEtc.dwAspect := dwAspect; + FormatEtc.lindex := lindex; + FormatEtc.tymed := tymed; + TClipboardFormatList.Add(Description, TreeClass, Priority, FormatEtc); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +class procedure TClipboardFormatList.Sort; + +// Sorts all entry for priority (increasing priority value). + + //--------------- local function -------------------------------------------- + procedure QuickSort(L, R: Integer); + + var + I, J: Integer; + P, T: TClipboardFormatListEntry; + + begin + repeat + I := L; + J := R; + P := _List[(L + R) shr 1]; + repeat + while TClipboardFormatListEntry(_List[I]).Priority < P.Priority do + Inc(I); + while TClipboardFormatListEntry(_List[J]).Priority > P.Priority do + Dec(J); + if I <= J then + begin + T := List[I]; + _List[I] := _List[J]; + _List[J] := T; + Inc(I); + Dec(J); + end; + until I > J; + if L < J then + QuickSort(L, J); + L := I; + until I >= R; + end; + //--------------- end local function ---------------------------------------- + +begin + if List.Count > 1 then + QuickSort(0, List.Count - 1); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +class procedure TClipboardFormatList.Add(const FormatString: string; AClass: TVirtualTreeClass; Priority: Cardinal; AFormatEtc: TFormatEtc); + +// Adds the given data to the internal list. The priority value is used to sort formats for importance. Larger priority +// values mean less priority. + +var + Entry: TClipboardFormatListEntry; + +begin + Entry := TClipboardFormatListEntry.Create; + Entry.Description := FormatString; + Entry.TreeClass := AClass; + Entry.Priority := Priority; + Entry.FormatEtc := AFormatEtc; + List.Add(Entry); + + Sort; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +class procedure TClipboardFormatList.Clear; + +var + I: Integer; + +begin + if Assigned(_List) then begin + for I := 0 to _List.Count - 1 do + TClipboardFormatListEntry(List[I]).Free; + _List.Clear; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +class procedure TClipboardFormatList.EnumerateFormats(TreeClass: TVirtualTreeClass; var Formats: TFormatEtcArray; const AllowedFormats: TClipboardFormats = nil); + +// Returns a list of format records for the given class. If assigned the AllowedFormats is used to limit the +// enumerated formats to those described in the list. + +var + I, Count: Integer; + Entry: TClipboardFormatListEntry; + +begin + SetLength(Formats, List.Count); + Count := 0; + for I := 0 to List.Count - 1 do + begin + Entry := List[I]; + // Does the tree class support this clipboard format? + if TreeClass.InheritsFrom(Entry.TreeClass) then + begin + // Is this format allowed to be included? + if (AllowedFormats = nil) or (AllowedFormats.IndexOf(Entry.Description) > -1) then + begin + // The list could change before we use the FormatEtc so it is best not to pass a pointer to the true FormatEtc + // structure. Instead make a copy and send that. + Formats[Count] := Entry.FormatEtc; + Inc(Count); + end; + end; + end; + SetLength(Formats, Count); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +class procedure TClipboardFormatList.EnumerateFormats(TreeClass: TVirtualTreeClass; const Formats: TStrings); + +// Returns a list of format descriptions for the given class. + +var + I: Integer; + Entry: TClipboardFormatListEntry; + +begin + for I := 0 to List.Count - 1 do + begin + Entry := List[I]; + if TreeClass.InheritsFrom(Entry.TreeClass) then + Formats.Add(Entry.Description); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +class function TClipboardFormatList.FindFormat(const FormatString: string): TClipboardFormatListEntry; + +var + I: Integer; + Entry: TClipboardFormatListEntry; + +begin + Result := nil; + for I := List.Count - 1 downto 0 do + begin + Entry := List[I]; + if CompareText(Entry.Description, FormatString) = 0 then + begin + Result := Entry; + Break; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +class function TClipboardFormatList.FindFormat(const FormatString: string; var Fmt: Word): TVirtualTreeClass; + +var + I: Integer; + Entry: TClipboardFormatListEntry; + +begin + Result := nil; + for I := List.Count - 1 downto 0 do + begin + Entry := List[I]; + if CompareText(Entry.Description, FormatString) = 0 then + begin + Result := Entry.TreeClass; + Fmt := Entry.FormatEtc.cfFormat; + Break; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +class function TClipboardFormatList.FindFormat(Fmt: Word; var Description: string): TVirtualTreeClass; + +var + I: Integer; + Entry: TClipboardFormatListEntry; + +begin + Result := nil; + for I := List.Count - 1 downto 0 do + begin + Entry := List[I]; + if Entry.FormatEtc.cfFormat = Fmt then + begin + Result := Entry.TreeClass; + Description := Entry.Description; + Break; + end; + end; +end; + + +class function TClipboardFormatList.GetList: TList; +begin + if not Assigned(_List) then + _List := TList.Create; + Exit(_List); +end; + +initialization + +finalization + + TClipboardFormatList.Clear; + FreeAndNil(_List); + +end. diff --git a/Source/VirtualTrees.Utils.pas b/Source/VirtualTrees.Utils.pas index 0696e2e84..cd04a3f46 100644 --- a/Source/VirtualTrees.Utils.pas +++ b/Source/VirtualTrees.Utils.pas @@ -1,113 +1,113 @@ -unit VirtualTrees.Utils; - -// The contents of this file are subject to the Mozilla Public License -// Version 1.1 (the "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ -// -// Alternatively, you may redistribute this library, use and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation; -// either version 2.1 of the License, or (at your option) any later version. -// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/. -// -// Software distributed under the License is distributed on an "AS IS" basis, -// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the -// specific language governing rights and limitations under the License. -// -// The original code is VirtualTrees.pas, released September 30, 2000. -// -// The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de), -// written by Mike Lischke (public@soft-gems.net, www.soft-gems.net). -// -// Portions created by digital publishing AG are Copyright -// (C) 1999-2001 digital publishing AG. All Rights Reserved. -//---------------------------------------------------------------------------------------------------------------------- - -interface - -{$WARN UNSAFE_TYPE OFF} -{$WARN UNSAFE_CAST OFF} -{$WARN UNSAFE_CODE OFF} - -uses - Winapi.Windows, - Winapi.ActiveX, - System.Types, - Vcl.Graphics, - Vcl.ImgList, - Vcl.Controls; - - -type - // Describes the mode how to blend pixels. - TBlendMode = ( - bmConstantAlpha, // apply given constant alpha - bmPerPixelAlpha, // use alpha value of the source pixel - bmMasterAlpha, // use alpha value of source pixel and multiply it with the constant alpha value - bmConstantAlphaAndColor // blend the destination color with the given constant color und the constant alpha value - ); - -procedure AlphaBlend(Source, Destination: HDC; R: TRect; Target: TPoint; Mode: TBlendMode; ConstantAlpha, Bias: Integer); -function GetRGBColor(Value: TColor): DWORD; -procedure PrtStretchDrawDIB(Canvas: TCanvas; DestRect: TRect; ABitmap: TBitmap); - -procedure SetBrushOrigin(Canvas: TCanvas; X, Y: Integer); inline; - - -procedure SetCanvasOrigin(Canvas: TCanvas; X, Y: Integer); inline; - -// Clip a given canvas to ClipRect while transforming the given rect to device coordinates. -procedure ClipCanvas(Canvas: TCanvas; ClipRect: TRect; VisibleRegion: HRGN = 0); - -procedure DrawImage(ImageList: TCustomImageList; Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean); - - -// Adjusts the given string S so that it fits into the given width. EllipsisWidth gives the width of -// the three points to be added to the shorted string. If this value is 0 then it will be determined implicitely. -// For higher speed (and multiple entries to be shorted) specify this value explicitely. -function ShortenString(DC: HDC; const S: string; Width: Integer; EllipsisWidth: Integer = 0): string; - -// Wrap the given string S so that it fits into a space of given width. -// RTL determines if right-to-left reading is active. -function WrapString(DC: HDC; const S: string; const Bounds: TRect; RTL: Boolean; DrawFormat: Cardinal): string; - -// Calculates bounds of a drawing rectangle for the given string -procedure GetStringDrawRect(DC: HDC; const S: string; var Bounds: TRect; DrawFormat: Cardinal); - -// Converts the incoming rectangle so that left and top are always less than or equal to right and bottom. -function OrderRect(const R: TRect): TRect; - -// Fills the given rectangles with values which can be used while dragging around an image -// (used in DragMove of the drag manager and DragTo of the header columns). -procedure FillDragRectangles(DragWidth, DragHeight, DeltaX, DeltaY: Integer; var RClip, RScroll, RSamp1, RSamp2, RDraw1, RDraw2: TRect); - -// Attaches a bitmap as drag image to an IDataObject, see issue #405 -// Usage: Set property DragImageKind to diNoImage, in your event handler OnCreateDataObject -// call VirtualTrees.Utils.ApplyDragImage() with your `IDataObject` and your bitmap. -procedure ApplyDragImage(const pDataObject: IDataObject; pBitmap: TBitmap); - -/// Returns True if the mouse cursor is currently visible and False in case it is suppressed. -/// Useful when doing hot-tracking on touchscreens, see issue #766 -function IsMouseCursorVisible(): Boolean; - -procedure ScaleImageList(const ImgList: TImageList; M, D: Integer); - -/// Returns True if the high contrast theme is anabled in the system settings, False otherwise. -function IsHighContrastEnabled(): Boolean; - - -implementation - -uses - Winapi.CommCtrl, - Winapi.ShlObj, - System.SysUtils, - System.StrUtils, - System.Math; - -const - WideLF = Char(#10); - -procedure ApplyDragImage(const pDataObject: IDataObject; pBitmap: TBitmap); +unit VirtualTrees.Utils; + +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ +// +// Alternatively, you may redistribute this library, use and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation; +// either version 2.1 of the License, or (at your option) any later version. +// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/. +// +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +// specific language governing rights and limitations under the License. +// +// The original code is VirtualTrees.pas, released September 30, 2000. +// +// The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de), +// written by Mike Lischke (public@soft-gems.net, www.soft-gems.net). +// +// Portions created by digital publishing AG are Copyright +// (C) 1999-2001 digital publishing AG. All Rights Reserved. +//---------------------------------------------------------------------------------------------------------------------- + +interface + +{$WARN UNSAFE_TYPE OFF} +{$WARN UNSAFE_CAST OFF} +{$WARN UNSAFE_CODE OFF} + +uses + Winapi.Windows, + Winapi.ActiveX, + System.Types, + Vcl.Graphics, + Vcl.ImgList, + Vcl.Controls; + + +type + // Describes the mode how to blend pixels. + TBlendMode = ( + bmConstantAlpha, // apply given constant alpha + bmPerPixelAlpha, // use alpha value of the source pixel + bmMasterAlpha, // use alpha value of source pixel and multiply it with the constant alpha value + bmConstantAlphaAndColor // blend the destination color with the given constant color und the constant alpha value + ); + +procedure AlphaBlend(Source, Destination: HDC; R: TRect; Target: TPoint; Mode: TBlendMode; ConstantAlpha, Bias: Integer); +function GetRGBColor(Value: TColor): DWORD; +procedure PrtStretchDrawDIB(Canvas: TCanvas; DestRect: TRect; ABitmap: TBitmap); + +procedure SetBrushOrigin(Canvas: TCanvas; X, Y: Integer); inline; + + +procedure SetCanvasOrigin(Canvas: TCanvas; X, Y: Integer); inline; + +// Clip a given canvas to ClipRect while transforming the given rect to device coordinates. +procedure ClipCanvas(Canvas: TCanvas; ClipRect: TRect; VisibleRegion: HRGN = 0); + +procedure DrawImage(ImageList: TCustomImageList; Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean); + + +// Adjusts the given string S so that it fits into the given width. EllipsisWidth gives the width of +// the three points to be added to the shorted string. If this value is 0 then it will be determined implicitely. +// For higher speed (and multiple entries to be shorted) specify this value explicitely. +function ShortenString(DC: HDC; const S: string; Width: Integer; EllipsisWidth: Integer = 0): string; + +// Wrap the given string S so that it fits into a space of given width. +// RTL determines if right-to-left reading is active. +function WrapString(DC: HDC; const S: string; const Bounds: TRect; RTL: Boolean; DrawFormat: Cardinal): string; + +// Calculates bounds of a drawing rectangle for the given string +procedure GetStringDrawRect(DC: HDC; const S: string; var Bounds: TRect; DrawFormat: Cardinal); + +// Converts the incoming rectangle so that left and top are always less than or equal to right and bottom. +function OrderRect(const R: TRect): TRect; + +// Fills the given rectangles with values which can be used while dragging around an image +// (used in DragMove of the drag manager and DragTo of the header columns). +procedure FillDragRectangles(DragWidth, DragHeight, DeltaX, DeltaY: Integer; var RClip, RScroll, RSamp1, RSamp2, RDraw1, RDraw2: TRect); + +// Attaches a bitmap as drag image to an IDataObject, see issue #405 +// Usage: Set property DragImageKind to diNoImage, in your event handler OnCreateDataObject +// call VirtualTrees.Utils.ApplyDragImage() with your `IDataObject` and your bitmap. +procedure ApplyDragImage(const pDataObject: IDataObject; pBitmap: TBitmap); + +/// Returns True if the mouse cursor is currently visible and False in case it is suppressed. +/// Useful when doing hot-tracking on touchscreens, see issue #766 +function IsMouseCursorVisible(): Boolean; + +procedure ScaleImageList(const ImgList: TImageList; M, D: Integer); + +/// Returns True if the high contrast theme is anabled in the system settings, False otherwise. +function IsHighContrastEnabled(): Boolean; + + +implementation + +uses + Winapi.CommCtrl, + Winapi.ShlObj, + System.SysUtils, + System.StrUtils, + System.Math; + +const + WideLF = Char(#10); + +procedure ApplyDragImage(const pDataObject: IDataObject; pBitmap: TBitmap); var DragSourceHelper: IDragSourceHelper; DragInfo: SHDRAGIMAGE; @@ -136,1160 +136,1160 @@ procedure ApplyDragImage(const pDataObject: IDataObject; pBitmap: TBitmap); end;//if not InitializeFromWindow end; end; - - -function OrderRect(const R: TRect): TRect; - -begin - if R.Left < R.Right then - begin - Result.Left := R.Left; - Result.Right := R.Right; - end - else - begin - Result.Left := R.Right; - Result.Right := R.Left; - end; - if R.Top < R.Bottom then - begin - Result.Top := R.Top; - Result.Bottom := R.Bottom; - end - else - begin - Result.Top := R.Bottom; - Result.Bottom := R.Top; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - - -procedure SetBrushOrigin(Canvas: TCanvas; X, Y: Integer); - -// Set the brush origin of a given canvas. - -//var -// P: TPoint; - -begin - //P := Point(X, Y); - //LPtoDP(Canvas.Handle, P, 1);// No longer used, see issue #608 - //SetBrushOrgEx(Canvas.Handle, P.X, P.Y, nil); - SetBrushOrgEx(Canvas.Handle, X, Y, nil); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure SetCanvasOrigin(Canvas: TCanvas; X, Y: Integer); - -// Set the coordinate space origin of a given canvas. - -var - P: TPoint; - -begin - // Reset origin as otherwise we would accumulate the origin shifts when calling LPtoDP. - SetWindowOrgEx(Canvas.Handle, 0, 0, nil); - - // The shifting is expected in physical points, so we have to transform them accordingly. - P := Point(X, Y); - LPtoDP(Canvas.Handle, P, 1); - - // Do the shift. - SetWindowOrgEx(Canvas.Handle, P.X, P.Y, nil); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure ClipCanvas(Canvas: TCanvas; ClipRect: TRect; VisibleRegion: HRGN = 0); - -var - ClipRegion: HRGN; - -begin - // Regions expect their coordinates in device coordinates, hence we have to transform the region rectangle. - LPtoDP(Canvas.Handle, ClipRect, 2); - ClipRegion := CreateRectRgnIndirect(ClipRect); - if VisibleRegion <> 0 then - CombineRgn(ClipRegion, ClipRegion, VisibleRegion, RGN_AND); - SelectClipRgn(Canvas.Handle, ClipRegion); - DeleteObject(ClipRegion); -end; - -//---------------------------------------------------------------------------------------------------------------------- - - -procedure GetStringDrawRect(DC: HDC; const S: string; var Bounds: TRect; DrawFormat: Cardinal); - -begin - Bounds.Right := Bounds.Left + 1; - Bounds.Bottom := Bounds.Top + 1; - - Winapi.Windows.DrawTextW(DC, PWideChar(S), Length(S), Bounds, DrawFormat or DT_CALCRECT); -end; - -//---------------------------------------------------------------------------------------------------------------------- - - -function ShortenString(DC: HDC; const S: string; Width: Integer; EllipsisWidth: Integer = 0): string; - -var - Size: TSize; - Len: Integer; - L, H, N, W: Integer; - -begin - Len := Length(S); - if (Len = 0) or (Width <= 0) then - Result := '' - else - begin - // Determine width of triple point using the current DC settings (if not already done). - if EllipsisWidth = 0 then - begin - GetTextExtentPoint32W(DC, '...', 3, Size); - EllipsisWidth := Size.cx; - end; - - begin - // Do a binary search for the optimal string length which fits into the given width. - L := 0; - N := 0; - W := Width; - H := Len; - while L < H do - begin - N := (L + H + 1) shr 1; - GetTextExtentPoint32W(DC, PWideChar(S), N, Size); - W := Size.cx + EllipsisWidth; - if W <= Width then - L := N - else - H := N - 1; - end; - if W <= Width then - L := N; - if L >= Len then - Result := S - else if Width <= EllipsisWidth then - Result := '' - else - Result := Copy(S, 1, L) + '...'; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function WrapString(DC: HDC; const S: string; const Bounds: TRect; RTL: Boolean; DrawFormat: Cardinal): string; - -var - Width, - Len, - WordCounter, - WordsInLine, - I, W: Integer; - Buffer, - Line: string; - Words: array of string; - R: TRect; - -begin - Result := ''; - // Leading and trailing are ignored. - Buffer := Trim(S); - Len := Length(Buffer); - if Len < 1 then - Exit; - - Width := Bounds.Right - Bounds.Left; - R := Rect(0, 0, 0, 0); - - // Count the words in the string. - WordCounter := 1; - for I := 1 to Len do - if Buffer[I] = ' ' then - Inc(WordCounter); - SetLength(Words, WordCounter); - - if RTL then - begin - // At first we split the string into words with the last word being the - // first element in Words. - W := 0; - for I := 1 to Len do - if Buffer[I] = ' ' then - Inc(W) - else - Words[W] := Words[W] + Buffer[I]; - - // Compose Result. - while WordCounter > 0 do - begin - WordsInLine := 0; - Line := ''; - - while WordCounter > 0 do - begin - GetStringDrawRect(DC, Line + IfThen(WordsInLine > 0, ' ', '') + Words[WordCounter - 1], R, DrawFormat); - if R.Right > Width then - begin - // If at least one word fits into this line then continue with the next line. - if WordsInLine > 0 then - Break; - - Buffer := Words[WordCounter - 1]; - if Len > 1 then - begin - for Len := Length(Buffer) - 1 downto 2 do - begin - GetStringDrawRect(DC, RightStr(Buffer, Len), R, DrawFormat); - if R.Right <= Width then - Break; - end; - end - else - Len := Length(Buffer); - - Line := Line + RightStr(Buffer, Max(Len, 1)); - Words[WordCounter - 1] := LeftStr(Buffer, Length(Buffer) - Max(Len, 1)); - if Words[WordCounter - 1] = '' then - Dec(WordCounter); - Break; - end - else - begin - Dec(WordCounter); - Line := Words[WordCounter] + IfThen(WordsInLine > 0, ' ', '') + Line; - Inc(WordsInLine); - end; - end; - - Result := Result + Line + WideLF; - end; - end - else - begin - // At first we split the string into words with the last word being the - // first element in Words. - W := WordCounter - 1; - for I := 1 to Len do - if Buffer[I] = ' ' then - Dec(W) - else - Words[W] := Words[W] + Buffer[I]; - - // Compose Result. - while WordCounter > 0 do - begin - WordsInLine := 0; - Line := ''; - - while WordCounter > 0 do - begin - GetStringDrawRect(DC, Line + IfThen(WordsInLine > 0, ' ', '') + Words[WordCounter - 1], R, DrawFormat); - if R.Right > Width then - begin - // If at least one word fits into this line then continue with the next line. - if WordsInLine > 0 then - Break; - - Buffer := Words[WordCounter - 1]; - if Len > 1 then - begin - for Len := Length(Buffer) - 1 downto 2 do - begin - GetStringDrawRect(DC, LeftStr(Buffer, Len), R, DrawFormat); - if R.Right <= Width then - Break; - end; - end - else - Len := Length(Buffer); - - Line := Line + LeftStr(Buffer, Max(Len, 1)); - Words[WordCounter - 1] := RightStr(Buffer, Length(Buffer) - Max(Len, 1)); - if Words[WordCounter - 1] = '' then - Dec(WordCounter); - Break; - end - else - begin - Dec(WordCounter); - Line := Line + IfThen(WordsInLine > 0, ' ', '') + Words[WordCounter]; - Inc(WordsInLine); - end; - end; - - Result := Result + Line + WideLF; - end; - end; - - Len := Length(Result); - if Result[Len] = WideLF then - SetLength(Result, Len - 1); -end; - -//---------------------------------------------------------------------------------------------------------------------- - - -function CalculateScanline(Bits: Pointer; Width, Height, Row: Integer): Pointer; - -// Helper function to calculate the start address for the given row. - -begin - if Height > 0 then // bottom-up DIB - Row := Height - Row - 1; - // Return DWORD aligned address of the requested scanline. - Result := PAnsiChar(Bits) + Row * ((Width * 32 + 31) and not 31) div 8; -end; - - - -//---------------------------------------------------------------------------------------------------------------------- - -function GetBitmapBitsFromDeviceContext(DC: HDC; var Width, Height: Integer): Pointer; - -// Helper function used to retrieve the bitmap selected into the given device context. If there is a bitmap then -// the function will return a pointer to its bits otherwise nil is returned. -// Additionally the dimensions of the bitmap are returned. - -var - Bitmap: HBITMAP; - DIB: TDIBSection; - -begin - Result := nil; - Width := 0; - Height := 0; - - Bitmap := GetCurrentObject(DC, OBJ_BITMAP); - if Bitmap <> 0 then - begin - if GetObject(Bitmap, SizeOf(DIB), @DIB) = SizeOf(DIB) then - begin - Assert(DIB.dsBm.bmPlanes * DIB.dsBm.bmBitsPixel = 32, 'Alpha blending error: bitmap must use 32 bpp.'); - Result := DIB.dsBm.bmBits; - Width := DIB.dsBmih.biWidth; - Height := DIB.dsBmih.biHeight; - end; - end; - Assert(Result <> nil, 'Alpha blending DC error: no bitmap available.'); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure AlphaBlendLineConstant(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer); - -// Blends a line of Count pixels from Source to Destination using a constant alpha value. -// The layout of a pixel must be BGRA where A is ignored (but is calculated as the other components). -// ConstantAlpha must be in the range 0..255 where 0 means totally transparent (destination pixel only) -// and 255 totally opaque (source pixel only). -// Bias is an additional value which gets added to every component and must be in the range -128..127 -// -{$ifdef CPUX64} -// RCX contains Source -// RDX contains Destination -// R8D contains Count -// R9D contains ConstantAlpha -// Bias is on the stack - -asm - //.NOFRAME - - // Load XMM3 with the constant alpha value (replicate it for every component). - // Expand it to word size. - MOVD XMM3, R9D // ConstantAlpha - PUNPCKLWD XMM3, XMM3 - PUNPCKLDQ XMM3, XMM3 - - // Load XMM5 with the bias value. - MOVD XMM5, [Bias] - PUNPCKLWD XMM5, XMM5 - PUNPCKLDQ XMM5, XMM5 - - // Load XMM4 with 128 to allow for saturated biasing. - MOV R10D, 128 - MOVD XMM4, R10D - PUNPCKLWD XMM4, XMM4 - PUNPCKLDQ XMM4, XMM4 - -@1: // The pixel loop calculates an entire pixel in one run. - // Note: The pixel byte values are expanded into the higher bytes of a word due - // to the way unpacking works. We compensate for this with an extra shift. - MOVD XMM1, DWORD PTR [RCX] // data is unaligned - MOVD XMM2, DWORD PTR [RDX] // data is unaligned - PXOR XMM0, XMM0 // clear source pixel register for unpacking - PUNPCKLBW XMM0, XMM1{[RCX]} // unpack source pixel byte values into words - PSRLW XMM0, 8 // move higher bytes to lower bytes - PXOR XMM1, XMM1 // clear target pixel register for unpacking - PUNPCKLBW XMM1, XMM2{[RDX]} // unpack target pixel byte values into words - MOVQ XMM2, XMM1 // make a copy of the shifted values, we need them again - PSRLW XMM1, 8 // move higher bytes to lower bytes - - // calculation is: target = (alpha * (source - target) + 256 * target) / 256 - PSUBW XMM0, XMM1 // source - target - PMULLW XMM0, XMM3 // alpha * (source - target) - PADDW XMM0, XMM2 // add target (in shifted form) - PSRLW XMM0, 8 // divide by 256 - - // Bias is accounted for by conversion of range 0..255 to -128..127, - // doing a saturated add and convert back to 0..255. - PSUBW XMM0, XMM4 - PADDSW XMM0, XMM5 - PADDW XMM0, XMM4 - PACKUSWB XMM0, XMM0 // convert words to bytes with saturation - MOVD DWORD PTR [RDX], XMM0 // store the result -@3: - ADD RCX, 4 - ADD RDX, 4 - DEC R8D - JNZ @1 -end; -{$else} -// EAX contains Source -// EDX contains Destination -// ECX contains Count -// ConstantAlpha and Bias are on the stack - -asm - PUSH ESI // save used registers - PUSH EDI - - MOV ESI, EAX // ESI becomes the actual source pointer - MOV EDI, EDX // EDI becomes the actual target pointer - - // Load MM6 with the constant alpha value (replicate it for every component). - // Expand it to word size. - MOV EAX, [ConstantAlpha] - DB $0F, $6E, $F0 /// MOVD MM6, EAX - DB $0F, $61, $F6 /// PUNPCKLWD MM6, MM6 - DB $0F, $62, $F6 /// PUNPCKLDQ MM6, MM6 - - // Load MM5 with the bias value. - MOV EAX, [Bias] - DB $0F, $6E, $E8 /// MOVD MM5, EAX - DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5 - DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5 - - // Load MM4 with 128 to allow for saturated biasing. - MOV EAX, 128 - DB $0F, $6E, $E0 /// MOVD MM4, EAX - DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4 - DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4 - -@1: // The pixel loop calculates an entire pixel in one run. - // Note: The pixel byte values are expanded into the higher bytes of a word due - // to the way unpacking works. We compensate for this with an extra shift. - DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking - DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words - DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes - DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking - DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words - DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again - DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes - - // calculation is: target = (alpha * (source - target) + 256 * target) / 256 - DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target - DB $0F, $D5, $C6 /// PMULLW MM0, MM6, alpha * (source - target) - DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form) - DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256 - - // Bias is accounted for by conversion of range 0..255 to -128..127, - // doing a saturated add and convert back to 0..255. - DB $0F, $F9, $C4 /// PSUBW MM0, MM4 - DB $0F, $ED, $C5 /// PADDSW MM0, MM5 - DB $0F, $FD, $C4 /// PADDW MM0, MM4 - DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation - DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result -@3: - ADD ESI, 4 - ADD EDI, 4 - DEC ECX - JNZ @1 - POP EDI - POP ESI -end; -{$endif CPUX64} - -//---------------------------------------------------------------------------------------------------------------------- - -procedure AlphaBlendLinePerPixel(Source, Destination: Pointer; Count, Bias: Integer); - -// Blends a line of Count pixels from Source to Destination using the alpha value of the source pixels. -// The layout of a pixel must be BGRA. -// Bias is an additional value which gets added to every component and must be in the range -128..127 -// -{$ifdef CPUX64} -// RCX contains Source -// RDX contains Destination -// R8D contains Count -// R9D contains Bias - -asm - //.NOFRAME - - // Load XMM5 with the bias value. - MOVD XMM5, R9D // Bias - PUNPCKLWD XMM5, XMM5 - PUNPCKLDQ XMM5, XMM5 - - // Load XMM4 with 128 to allow for saturated biasing. - MOV R10D, 128 - MOVD XMM4, R10D - PUNPCKLWD XMM4, XMM4 - PUNPCKLDQ XMM4, XMM4 - -@1: // The pixel loop calculates an entire pixel in one run. - // Note: The pixel byte values are expanded into the higher bytes of a word due - // to the way unpacking works. We compensate for this with an extra shift. - MOVD XMM1, DWORD PTR [RCX] // data is unaligned - MOVD XMM2, DWORD PTR [RDX] // data is unaligned - PXOR XMM0, XMM0 // clear source pixel register for unpacking - PUNPCKLBW XMM0, XMM1{[RCX]} // unpack source pixel byte values into words - PSRLW XMM0, 8 // move higher bytes to lower bytes - PXOR XMM1, XMM1 // clear target pixel register for unpacking - PUNPCKLBW XMM1, XMM2{[RDX]} // unpack target pixel byte values into words - MOVQ XMM2, XMM1 // make a copy of the shifted values, we need them again - PSRLW XMM1, 8 // move higher bytes to lower bytes - - // Load XMM3 with the source alpha value (replicate it for every component). - // Expand it to word size. - MOVQ XMM3, XMM0 - PUNPCKHWD XMM3, XMM3 - PUNPCKHDQ XMM3, XMM3 - - // calculation is: target = (alpha * (source - target) + 256 * target) / 256 - PSUBW XMM0, XMM1 // source - target - PMULLW XMM0, XMM3 // alpha * (source - target) - PADDW XMM0, XMM2 // add target (in shifted form) - PSRLW XMM0, 8 // divide by 256 - - // Bias is accounted for by conversion of range 0..255 to -128..127, - // doing a saturated add and convert back to 0..255. - PSUBW XMM0, XMM4 - PADDSW XMM0, XMM5 - PADDW XMM0, XMM4 - PACKUSWB XMM0, XMM0 // convert words to bytes with saturation - MOVD DWORD PTR [RDX], XMM0 // store the result -@3: - ADD RCX, 4 - ADD RDX, 4 - DEC R8D - JNZ @1 -end; -{$else} -// EAX contains Source -// EDX contains Destination -// ECX contains Count -// Bias is on the stack - -asm - PUSH ESI // save used registers - PUSH EDI - - MOV ESI, EAX // ESI becomes the actual source pointer - MOV EDI, EDX // EDI becomes the actual target pointer - - // Load MM5 with the bias value. - MOV EAX, [Bias] - DB $0F, $6E, $E8 /// MOVD MM5, EAX - DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5 - DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5 - - // Load MM4 with 128 to allow for saturated biasing. - MOV EAX, 128 - DB $0F, $6E, $E0 /// MOVD MM4, EAX - DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4 - DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4 - -@1: // The pixel loop calculates an entire pixel in one run. - // Note: The pixel byte values are expanded into the higher bytes of a word due - // to the way unpacking works. We compensate for this with an extra shift. - DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking - DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words - DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes - DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking - DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words - DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again - DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes - - // Load MM6 with the source alpha value (replicate it for every component). - // Expand it to word size. - DB $0F, $6F, $F0 /// MOVQ MM6, MM0 - DB $0F, $69, $F6 /// PUNPCKHWD MM6, MM6 - DB $0F, $6A, $F6 /// PUNPCKHDQ MM6, MM6 - - // calculation is: target = (alpha * (source - target) + 256 * target) / 256 - DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target - DB $0F, $D5, $C6 /// PMULLW MM0, MM6, alpha * (source - target) - DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form) - DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256 - - // Bias is accounted for by conversion of range 0..255 to -128..127, - // doing a saturated add and convert back to 0..255. - DB $0F, $F9, $C4 /// PSUBW MM0, MM4 - DB $0F, $ED, $C5 /// PADDSW MM0, MM5 - DB $0F, $FD, $C4 /// PADDW MM0, MM4 - DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation - DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result -@3: - ADD ESI, 4 - ADD EDI, 4 - DEC ECX - JNZ @1 - POP EDI - POP ESI -end; -{$endif CPUX64} - -//---------------------------------------------------------------------------------------------------------------------- - -procedure EMMS; - -// Reset MMX state to use the FPU for other tasks again. - -{$ifdef CPUX64} - inline; -begin -end; -{$else} -asm - DB $0F, $77 /// EMMS -end; -{$endif CPUX64} - -//---------------------------------------------------------------------------------------------------------------------- - -procedure AlphaBlendLineMaster(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer); - -// Blends a line of Count pixels from Source to Destination using the source pixel and a constant alpha value. -// The layout of a pixel must be BGRA. -// ConstantAlpha must be in the range 0..255. -// Bias is an additional value which gets added to every component and must be in the range -128..127 -// -{$ifdef CPUX64} -// RCX contains Source -// RDX contains Destination -// R8D contains Count -// R9D contains ConstantAlpha -// Bias is on the stack - -asm - .SAVENV XMM6 - - // Load XMM3 with the constant alpha value (replicate it for every component). - // Expand it to word size. - MOVD XMM3, R9D // ConstantAlpha - PUNPCKLWD XMM3, XMM3 - PUNPCKLDQ XMM3, XMM3 - - // Load XMM5 with the bias value. - MOV R10D, [Bias] - MOVD XMM5, R10D - PUNPCKLWD XMM5, XMM5 - PUNPCKLDQ XMM5, XMM5 - - // Load XMM4 with 128 to allow for saturated biasing. - MOV R10D, 128 - MOVD XMM4, R10D - PUNPCKLWD XMM4, XMM4 - PUNPCKLDQ XMM4, XMM4 - -@1: // The pixel loop calculates an entire pixel in one run. - // Note: The pixel byte values are expanded into the higher bytes of a word due - // to the way unpacking works. We compensate for this with an extra shift. - MOVD XMM1, DWORD PTR [RCX] // data is unaligned - MOVD XMM2, DWORD PTR [RDX] // data is unaligned - PXOR XMM0, XMM0 // clear source pixel register for unpacking - PUNPCKLBW XMM0, XMM1{[RCX]} // unpack source pixel byte values into words - PSRLW XMM0, 8 // move higher bytes to lower bytes - PXOR XMM1, XMM1 // clear target pixel register for unpacking - PUNPCKLBW XMM1, XMM2{[RCX]} // unpack target pixel byte values into words - MOVQ XMM2, XMM1 // make a copy of the shifted values, we need them again - PSRLW XMM1, 8 // move higher bytes to lower bytes - - // Load XMM6 with the source alpha value (replicate it for every component). - // Expand it to word size. - MOVQ XMM6, XMM0 - PUNPCKHWD XMM6, XMM6 - PUNPCKHDQ XMM6, XMM6 - PMULLW XMM6, XMM3 // source alpha * master alpha - PSRLW XMM6, 8 // divide by 256 - - // calculation is: target = (alpha * master alpha * (source - target) + 256 * target) / 256 - PSUBW XMM0, XMM1 // source - target - PMULLW XMM0, XMM6 // alpha * (source - target) - PADDW XMM0, XMM2 // add target (in shifted form) - PSRLW XMM0, 8 // divide by 256 - - // Bias is accounted for by conversion of range 0..255 to -128..127, - // doing a saturated add and convert back to 0..255. - PSUBW XMM0, XMM4 - PADDSW XMM0, XMM5 - PADDW XMM0, XMM4 - PACKUSWB XMM0, XMM0 // convert words to bytes with saturation - MOVD DWORD PTR [RDX], XMM0 // store the result -@3: - ADD RCX, 4 - ADD RDX, 4 - DEC R8D - JNZ @1 -end; -{$else} -// EAX contains Source -// EDX contains Destination -// ECX contains Count -// ConstantAlpha and Bias are on the stack - -asm - PUSH ESI // save used registers - PUSH EDI - - MOV ESI, EAX // ESI becomes the actual source pointer - MOV EDI, EDX // EDI becomes the actual target pointer - - // Load MM6 with the constant alpha value (replicate it for every component). - // Expand it to word size. - MOV EAX, [ConstantAlpha] - DB $0F, $6E, $F0 /// MOVD MM6, EAX - DB $0F, $61, $F6 /// PUNPCKLWD MM6, MM6 - DB $0F, $62, $F6 /// PUNPCKLDQ MM6, MM6 - - // Load MM5 with the bias value. - MOV EAX, [Bias] - DB $0F, $6E, $E8 /// MOVD MM5, EAX - DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5 - DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5 - - // Load MM4 with 128 to allow for saturated biasing. - MOV EAX, 128 - DB $0F, $6E, $E0 /// MOVD MM4, EAX - DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4 - DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4 - -@1: // The pixel loop calculates an entire pixel in one run. - // Note: The pixel byte values are expanded into the higher bytes of a word due - // to the way unpacking works. We compensate for this with an extra shift. - DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking - DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words - DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes - DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking - DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words - DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again - DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes - - // Load MM7 with the source alpha value (replicate it for every component). - // Expand it to word size. - DB $0F, $6F, $F8 /// MOVQ MM7, MM0 - DB $0F, $69, $FF /// PUNPCKHWD MM7, MM7 - DB $0F, $6A, $FF /// PUNPCKHDQ MM7, MM7 - DB $0F, $D5, $FE /// PMULLW MM7, MM6, source alpha * master alpha - DB $0F, $71, $D7, $08 /// PSRLW MM7, 8, divide by 256 - - // calculation is: target = (alpha * master alpha * (source - target) + 256 * target) / 256 - DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target - DB $0F, $D5, $C7 /// PMULLW MM0, MM7, alpha * (source - target) - DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form) - DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256 - - // Bias is accounted for by conversion of range 0..255 to -128..127, - // doing a saturated add and convert back to 0..255. - DB $0F, $F9, $C4 /// PSUBW MM0, MM4 - DB $0F, $ED, $C5 /// PADDSW MM0, MM5 - DB $0F, $FD, $C4 /// PADDW MM0, MM4 - DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation - DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result -@3: - ADD ESI, 4 - ADD EDI, 4 - DEC ECX - JNZ @1 - POP EDI - POP ESI -end; -{$endif CPUX64} - -//---------------------------------------------------------------------------------------------------------------------- - -procedure AlphaBlendLineMasterAndColor(Destination: Pointer; Count: Integer; ConstantAlpha, Color: Integer); - -// Blends a line of Count pixels in Destination against the given color using a constant alpha value. -// The layout of a pixel must be BGRA and Color must be rrggbb00 (as stored by a COLORREF). -// ConstantAlpha must be in the range 0..255. -// -{$ifdef CPUX64} -// RCX contains Destination -// EDX contains Count -// R8D contains ConstantAlpha -// R9D contains Color - -asm - //.NOFRAME - - // The used formula is: target = (alpha * color + (256 - alpha) * target) / 256. - // alpha * color (factor 1) and 256 - alpha (factor 2) are constant values which can be calculated in advance. - // The remaining calculation is therefore: target = (F1 + F2 * target) / 256 - - // Load XMM3 with the constant alpha value (replicate it for every component). - // Expand it to word size. (Every calculation here works on word sized operands.) - MOVD XMM3, R8D // ConstantAlpha - PUNPCKLWD XMM3, XMM3 - PUNPCKLDQ XMM3, XMM3 - - // Calculate factor 2. - MOV R10D, $100 - MOVD XMM2, R10D - PUNPCKLWD XMM2, XMM2 - PUNPCKLDQ XMM2, XMM2 - PSUBW XMM2, XMM3 // XMM2 contains now: 255 - alpha = F2 - - // Now calculate factor 1. Alpha is still in XMM3, but the r and b components of Color must be swapped. - BSWAP R9D // Color - ROR R9D, 8 - MOVD XMM1, R9D // Load the color and convert to word sized values. - PXOR XMM4, XMM4 - PUNPCKLBW XMM1, XMM4 - PMULLW XMM1, XMM3 // XMM1 contains now: color * alpha = F1 - -@1: // The pixel loop calculates an entire pixel in one run. - MOVD XMM0, DWORD PTR [RCX] - PUNPCKLBW XMM0, XMM4 - - PMULLW XMM0, XMM2 // calculate F1 + F2 * target - PADDW XMM0, XMM1 - PSRLW XMM0, 8 // divide by 256 - - PACKUSWB XMM0, XMM0 // convert words to bytes with saturation - MOVD DWORD PTR [RCX], XMM0 // store the result - - ADD RCX, 4 - DEC EDX - JNZ @1 -end; -{$else} -// EAX contains Destination -// EDX contains Count -// ECX contains ConstantAlpha -// Color is passed on the stack - -asm - // The used formula is: target = (alpha * color + (256 - alpha) * target) / 256. - // alpha * color (factor 1) and 256 - alpha (factor 2) are constant values which can be calculated in advance. - // The remaining calculation is therefore: target = (F1 + F2 * target) / 256 - - // Load MM3 with the constant alpha value (replicate it for every component). - // Expand it to word size. (Every calculation here works on word sized operands.) - DB $0F, $6E, $D9 /// MOVD MM3, ECX - DB $0F, $61, $DB /// PUNPCKLWD MM3, MM3 - DB $0F, $62, $DB /// PUNPCKLDQ MM3, MM3 - - // Calculate factor 2. - MOV ECX, $100 - DB $0F, $6E, $D1 /// MOVD MM2, ECX - DB $0F, $61, $D2 /// PUNPCKLWD MM2, MM2 - DB $0F, $62, $D2 /// PUNPCKLDQ MM2, MM2 - DB $0F, $F9, $D3 /// PSUBW MM2, MM3 // MM2 contains now: 255 - alpha = F2 - - // Now calculate factor 1. Alpha is still in MM3, but the r and b components of Color must be swapped. - MOV ECX, [Color] - BSWAP ECX - ROR ECX, 8 - DB $0F, $6E, $C9 /// MOVD MM1, ECX // Load the color and convert to word sized values. - DB $0F, $EF, $E4 /// PXOR MM4, MM4 - DB $0F, $60, $CC /// PUNPCKLBW MM1, MM4 - DB $0F, $D5, $CB /// PMULLW MM1, MM3 // MM1 contains now: color * alpha = F1 - -@1: // The pixel loop calculates an entire pixel in one run. - DB $0F, $6E, $00 /// MOVD MM0, [EAX] - DB $0F, $60, $C4 /// PUNPCKLBW MM0, MM4 - - DB $0F, $D5, $C2 /// PMULLW MM0, MM2 // calculate F1 + F2 * target - DB $0F, $FD, $C1 /// PADDW MM0, MM1 - DB $0F, $71, $D0, $08 /// PSRLW MM0, 8 // divide by 256 - - DB $0F, $67, $C0 /// PACKUSWB MM0, MM0 // convert words to bytes with saturation - DB $0F, $7E, $00 /// MOVD [EAX], MM0 // store the result - - ADD EAX, 4 - DEC EDX - JNZ @1 -end; -{$endif CPUX64} - -//---------------------------------------------------------------------------------------------------------------------- - -procedure AlphaBlend(Source, Destination: HDC; R: TRect; Target: TPoint; Mode: TBlendMode; ConstantAlpha, Bias: Integer); - -// Optimized alpha blend procedure using MMX instructions to perform as quick as possible. -// For this procedure to work properly it is important that both source and target bitmap use the 32 bit color format. -// R describes the source rectangle to work on. -// Target is the place (upper left corner) in the target bitmap where to blend to. Note that source width + X offset -// must be less or equal to the target width. Similar for the height. -// If Mode is bmConstantAlpha then the blend operation uses the given ConstantAlpha value for all pixels. -// If Mode is bmPerPixelAlpha then each pixel is blended using its individual alpha value (the alpha value of the source). -// If Mode is bmMasterAlpha then each pixel is blended using its individual alpha value multiplied by ConstantAlpha. -// If Mode is bmConstantAlphaAndColor then each destination pixel is blended using ConstantAlpha but also a constant -// color which will be obtained from Bias. In this case no offset value is added, otherwise Bias is used as offset. -// Blending of a color into target only (bmConstantAlphaAndColor) ignores Source (the DC) and Target (the position). -// CAUTION: This procedure does not check whether MMX instructions are actually available! Call it only if MMX is really -// usable. - -var - Y: Integer; - SourceRun, - TargetRun: PByte; - - SourceBits, - DestBits: Pointer; - SourceWidth, - SourceHeight, - DestWidth, - DestHeight: Integer; - -begin - if not IsRectEmpty(R) then - begin - // Note: it is tempting to optimize the special cases for constant alpha 0 and 255 by just ignoring soure - // (alpha = 0) or simply do a blit (alpha = 255). But this does not take the bias into account. - case Mode of - bmConstantAlpha: - begin - // Get a pointer to the bitmap bits for the source and target device contexts. - // Note: this supposes that both contexts do actually have bitmaps assigned! - SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight); - DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); - if Assigned(SourceBits) and Assigned(DestBits) then - begin - for Y := 0 to R.Bottom - R.Top - 1 do - begin - SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top); - Inc(SourceRun, 4 * R.Left); - TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y); - Inc(TargetRun, 4 * Target.X); - AlphaBlendLineConstant(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias); - end; - end; - EMMS; - end; - bmPerPixelAlpha: - begin - SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight); - DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); - if Assigned(SourceBits) and Assigned(DestBits) then - begin - for Y := 0 to R.Bottom - R.Top - 1 do - begin - SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top); - Inc(SourceRun, 4 * R.Left); - TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y); - Inc(TargetRun, 4 * Target.X); - AlphaBlendLinePerPixel(SourceRun, TargetRun, R.Right - R.Left, Bias); - end; - end; - EMMS; - end; - bmMasterAlpha: - begin - SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight); - DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); - if Assigned(SourceBits) and Assigned(DestBits) then - begin - for Y := 0 to R.Bottom - R.Top - 1 do - begin - SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top); - Inc(SourceRun, 4 * Target.X); - TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y); - AlphaBlendLineMaster(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias); - end; - end; - EMMS; - end; - bmConstantAlphaAndColor: - begin - // Source is ignored since there is a constant color value. - DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); - if Assigned(DestBits) then - begin - for Y := 0 to R.Bottom - R.Top - 1 do - begin - TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + R.Top); - Inc(TargetRun, 4 * R.Left); - AlphaBlendLineMasterAndColor(TargetRun, R.Right - R.Left, ConstantAlpha, Bias); - end; - end; - EMMS; - end; - end; - end; -end; - -function GetRGBColor(Value: TColor): DWORD; - -// Little helper to convert a Delphi color to an image list color. - -begin - Result := ColorToRGB(Value); - case Result of - clNone: - Result := CLR_NONE; - clDefault: - Result := CLR_DEFAULT; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure PrtStretchDrawDIB(Canvas: TCanvas; DestRect: TRect; ABitmap: TBitmap); - -// Stretch draw on to the new canvas. - -var - Header, - Bits: Pointer; - HeaderSize, - BitsSize: Cardinal; - -begin - GetDIBSizes(ABitmap.Handle, HeaderSize, BitsSize); - - GetMem(Header, HeaderSize); - GetMem(Bits, BitsSize); - try - GetDIB(ABitmap.Handle, ABitmap.Palette, Header^, Bits^); - StretchDIBits(Canvas.Handle, DestRect.Left, DestRect.Top, DestRect.Right - DestRect.Left, DestRect.Bottom - - DestRect.Top, 0, 0, ABitmap.Width, ABitmap.Height, Bits, TBitmapInfo(Header^), DIB_RGB_COLORS, SRCCOPY); - finally - FreeMem(Header); - FreeMem(Bits); - end; -end; - - -//---------------------------------------------------------------------------------------------------------------------- - - -procedure FillDragRectangles(DragWidth, DragHeight, DeltaX, DeltaY: Integer; var RClip, RScroll, RSamp1, RSamp2, RDraw1, RDraw2: TRect); - -begin - // ScrollDC limits - RClip := Rect(0, 0, DragWidth, DragHeight); - if DeltaX > 0 then - begin - // move to the left - if DeltaY = 0 then - begin - // move only to the left - // background movement - RScroll := Rect(0, 0, DragWidth - DeltaX, DragHeight); - RSamp1 := Rect(0, 0, DeltaX, DragHeight); - RDraw1 := Rect(DragWidth - DeltaX, 0, DeltaX, DragHeight); - end - else - if DeltaY < 0 then - begin - // move to bottom left - RScroll := Rect(0, -DeltaY, DragWidth - DeltaX, DragHeight); - RSamp1 := Rect(0, 0, DeltaX, DragHeight); - RSamp2 := Rect(DeltaX, DragHeight + DeltaY, DragWidth - DeltaX, -DeltaY); - RDraw1 := Rect(0, 0, DragWidth - DeltaX, -DeltaY); - RDraw2 := Rect(DragWidth - DeltaX, 0, DeltaX, DragHeight); - end - else - begin - // move to upper left - RScroll := Rect(0, 0, DragWidth - DeltaX, DragHeight - DeltaY); - RSamp1 := Rect(0, 0, DeltaX, DragHeight); - RSamp2 := Rect(DeltaX, 0, DragWidth - DeltaX, DeltaY); - RDraw1 := Rect(0, DragHeight - DeltaY, DragWidth - DeltaX, DeltaY); - RDraw2 := Rect(DragWidth - DeltaX, 0, DeltaX, DragHeight); - end; - end - else - if DeltaX = 0 then - begin - // vertical movement only - if DeltaY < 0 then - begin - // move downwards - RScroll := Rect(0, -DeltaY, DragWidth, DragHeight); - RSamp2 := Rect(0, DragHeight + DeltaY, DragWidth, -DeltaY); - RDraw2 := Rect(0, 0, DragWidth, -DeltaY); - end - else - begin - // move upwards - RScroll := Rect(0, 0, DragWidth, DragHeight - DeltaY); - RSamp2 := Rect(0, 0, DragWidth, DeltaY); - RDraw2 := Rect(0, DragHeight - DeltaY, DragWidth, DeltaY); - end; - end - else - begin - // move to the right - if DeltaY > 0 then - begin - // move up right - RScroll := Rect(-DeltaX, 0, DragWidth, DragHeight); - RSamp1 := Rect(0, 0, DragWidth + DeltaX, DeltaY); - RSamp2 := Rect(DragWidth + DeltaX, 0, -DeltaX, DragHeight); - RDraw1 := Rect(0, 0, -DeltaX, DragHeight); - RDraw2 := Rect(-DeltaX, DragHeight - DeltaY, DragWidth + DeltaX, DeltaY); - end - else - if DeltaY = 0 then - begin - // to the right only - RScroll := Rect(-DeltaX, 0, DragWidth, DragHeight); - RSamp1 := Rect(DragWidth + DeltaX, 0, -DeltaX, DragHeight); - RDraw1 := Rect(0, 0, -DeltaX, DragHeight); - end - else - begin - // move down right - RScroll := Rect(-DeltaX, -DeltaY, DragWidth, DragHeight); - RSamp1 := Rect(0, DragHeight + DeltaY, DragWidth + DeltaX, -DeltaY); - RSamp2 := Rect(DragWidth + DeltaX, 0, -DeltaX, DragHeight); - RDraw1 := Rect(0, 0, -DeltaX, DragHeight); - RDraw2 := Rect(-DeltaX, 0, DragWidth + DeltaX, -DeltaY); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -type - TCustomImageListCast = class(TCustomImageList); - -procedure DrawImage(ImageList: TCustomImageList; Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean); - - procedure DrawDisabledImage(ImageList: TCustomImageList; Canvas: TCanvas; X, Y, Index: Integer); - var - Params: TImageListDrawParams; - begin - FillChar(Params, SizeOf(Params), 0); - Params.cbSize := SizeOf(Params); - Params.himl := ImageList.Handle; - Params.i := Index; - Params.hdcDst := Canvas.Handle; - Params.x := X; - Params.y := Y; - Params.fState := ILS_SATURATE; - ImageList_DrawIndirect(@Params); - end; - -begin - if Enabled then - TCustomImageListCast(ImageList).DoDraw(Index, Canvas, X, Y, Style, Enabled) - else - DrawDisabledImage(ImageList, Canvas, X, Y, Index); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function IsMouseCursorVisible(): Boolean; + + +function OrderRect(const R: TRect): TRect; + +begin + if R.Left < R.Right then + begin + Result.Left := R.Left; + Result.Right := R.Right; + end + else + begin + Result.Left := R.Right; + Result.Right := R.Left; + end; + if R.Top < R.Bottom then + begin + Result.Top := R.Top; + Result.Bottom := R.Bottom; + end + else + begin + Result.Top := R.Bottom; + Result.Bottom := R.Top; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + + +procedure SetBrushOrigin(Canvas: TCanvas; X, Y: Integer); + +// Set the brush origin of a given canvas. + +//var +// P: TPoint; + +begin + //P := Point(X, Y); + //LPtoDP(Canvas.Handle, P, 1);// No longer used, see issue #608 + //SetBrushOrgEx(Canvas.Handle, P.X, P.Y, nil); + SetBrushOrgEx(Canvas.Handle, X, Y, nil); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure SetCanvasOrigin(Canvas: TCanvas; X, Y: Integer); + +// Set the coordinate space origin of a given canvas. + +var + P: TPoint; + +begin + // Reset origin as otherwise we would accumulate the origin shifts when calling LPtoDP. + SetWindowOrgEx(Canvas.Handle, 0, 0, nil); + + // The shifting is expected in physical points, so we have to transform them accordingly. + P := Point(X, Y); + LPtoDP(Canvas.Handle, P, 1); + + // Do the shift. + SetWindowOrgEx(Canvas.Handle, P.X, P.Y, nil); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure ClipCanvas(Canvas: TCanvas; ClipRect: TRect; VisibleRegion: HRGN = 0); + +var + ClipRegion: HRGN; + +begin + // Regions expect their coordinates in device coordinates, hence we have to transform the region rectangle. + LPtoDP(Canvas.Handle, ClipRect, 2); + ClipRegion := CreateRectRgnIndirect(ClipRect); + if VisibleRegion <> 0 then + CombineRgn(ClipRegion, ClipRegion, VisibleRegion, RGN_AND); + SelectClipRgn(Canvas.Handle, ClipRegion); + DeleteObject(ClipRegion); +end; + +//---------------------------------------------------------------------------------------------------------------------- + + +procedure GetStringDrawRect(DC: HDC; const S: string; var Bounds: TRect; DrawFormat: Cardinal); + +begin + Bounds.Right := Bounds.Left + 1; + Bounds.Bottom := Bounds.Top + 1; + + Winapi.Windows.DrawTextW(DC, PWideChar(S), Length(S), Bounds, DrawFormat or DT_CALCRECT); +end; + +//---------------------------------------------------------------------------------------------------------------------- + + +function ShortenString(DC: HDC; const S: string; Width: Integer; EllipsisWidth: Integer = 0): string; + +var + Size: TSize; + Len: Integer; + L, H, N, W: Integer; + +begin + Len := Length(S); + if (Len = 0) or (Width <= 0) then + Result := '' + else + begin + // Determine width of triple point using the current DC settings (if not already done). + if EllipsisWidth = 0 then + begin + GetTextExtentPoint32W(DC, '...', 3, Size); + EllipsisWidth := Size.cx; + end; + + begin + // Do a binary search for the optimal string length which fits into the given width. + L := 0; + N := 0; + W := Width; + H := Len; + while L < H do + begin + N := (L + H + 1) shr 1; + GetTextExtentPoint32W(DC, PWideChar(S), N, Size); + W := Size.cx + EllipsisWidth; + if W <= Width then + L := N + else + H := N - 1; + end; + if W <= Width then + L := N; + if L >= Len then + Result := S + else if Width <= EllipsisWidth then + Result := '' + else + Result := Copy(S, 1, L) + '...'; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function WrapString(DC: HDC; const S: string; const Bounds: TRect; RTL: Boolean; DrawFormat: Cardinal): string; + +var + Width, + Len, + WordCounter, + WordsInLine, + I, W: Integer; + Buffer, + Line: string; + Words: array of string; + R: TRect; + +begin + Result := ''; + // Leading and trailing are ignored. + Buffer := Trim(S); + Len := Length(Buffer); + if Len < 1 then + Exit; + + Width := Bounds.Right - Bounds.Left; + R := Rect(0, 0, 0, 0); + + // Count the words in the string. + WordCounter := 1; + for I := 1 to Len do + if Buffer[I] = ' ' then + Inc(WordCounter); + SetLength(Words, WordCounter); + + if RTL then + begin + // At first we split the string into words with the last word being the + // first element in Words. + W := 0; + for I := 1 to Len do + if Buffer[I] = ' ' then + Inc(W) + else + Words[W] := Words[W] + Buffer[I]; + + // Compose Result. + while WordCounter > 0 do + begin + WordsInLine := 0; + Line := ''; + + while WordCounter > 0 do + begin + GetStringDrawRect(DC, Line + IfThen(WordsInLine > 0, ' ', '') + Words[WordCounter - 1], R, DrawFormat); + if R.Right > Width then + begin + // If at least one word fits into this line then continue with the next line. + if WordsInLine > 0 then + Break; + + Buffer := Words[WordCounter - 1]; + if Len > 1 then + begin + for Len := Length(Buffer) - 1 downto 2 do + begin + GetStringDrawRect(DC, RightStr(Buffer, Len), R, DrawFormat); + if R.Right <= Width then + Break; + end; + end + else + Len := Length(Buffer); + + Line := Line + RightStr(Buffer, Max(Len, 1)); + Words[WordCounter - 1] := LeftStr(Buffer, Length(Buffer) - Max(Len, 1)); + if Words[WordCounter - 1] = '' then + Dec(WordCounter); + Break; + end + else + begin + Dec(WordCounter); + Line := Words[WordCounter] + IfThen(WordsInLine > 0, ' ', '') + Line; + Inc(WordsInLine); + end; + end; + + Result := Result + Line + WideLF; + end; + end + else + begin + // At first we split the string into words with the last word being the + // first element in Words. + W := WordCounter - 1; + for I := 1 to Len do + if Buffer[I] = ' ' then + Dec(W) + else + Words[W] := Words[W] + Buffer[I]; + + // Compose Result. + while WordCounter > 0 do + begin + WordsInLine := 0; + Line := ''; + + while WordCounter > 0 do + begin + GetStringDrawRect(DC, Line + IfThen(WordsInLine > 0, ' ', '') + Words[WordCounter - 1], R, DrawFormat); + if R.Right > Width then + begin + // If at least one word fits into this line then continue with the next line. + if WordsInLine > 0 then + Break; + + Buffer := Words[WordCounter - 1]; + if Len > 1 then + begin + for Len := Length(Buffer) - 1 downto 2 do + begin + GetStringDrawRect(DC, LeftStr(Buffer, Len), R, DrawFormat); + if R.Right <= Width then + Break; + end; + end + else + Len := Length(Buffer); + + Line := Line + LeftStr(Buffer, Max(Len, 1)); + Words[WordCounter - 1] := RightStr(Buffer, Length(Buffer) - Max(Len, 1)); + if Words[WordCounter - 1] = '' then + Dec(WordCounter); + Break; + end + else + begin + Dec(WordCounter); + Line := Line + IfThen(WordsInLine > 0, ' ', '') + Words[WordCounter]; + Inc(WordsInLine); + end; + end; + + Result := Result + Line + WideLF; + end; + end; + + Len := Length(Result); + if Result[Len] = WideLF then + SetLength(Result, Len - 1); +end; + +//---------------------------------------------------------------------------------------------------------------------- + + +function CalculateScanline(Bits: Pointer; Width, Height, Row: Integer): Pointer; + +// Helper function to calculate the start address for the given row. + +begin + if Height > 0 then // bottom-up DIB + Row := Height - Row - 1; + // Return DWORD aligned address of the requested scanline. + Result := PAnsiChar(Bits) + Row * ((Width * 32 + 31) and not 31) div 8; +end; + + + +//---------------------------------------------------------------------------------------------------------------------- + +function GetBitmapBitsFromDeviceContext(DC: HDC; var Width, Height: Integer): Pointer; + +// Helper function used to retrieve the bitmap selected into the given device context. If there is a bitmap then +// the function will return a pointer to its bits otherwise nil is returned. +// Additionally the dimensions of the bitmap are returned. + +var + Bitmap: HBITMAP; + DIB: TDIBSection; + +begin + Result := nil; + Width := 0; + Height := 0; + + Bitmap := GetCurrentObject(DC, OBJ_BITMAP); + if Bitmap <> 0 then + begin + if GetObject(Bitmap, SizeOf(DIB), @DIB) = SizeOf(DIB) then + begin + Assert(DIB.dsBm.bmPlanes * DIB.dsBm.bmBitsPixel = 32, 'Alpha blending error: bitmap must use 32 bpp.'); + Result := DIB.dsBm.bmBits; + Width := DIB.dsBmih.biWidth; + Height := DIB.dsBmih.biHeight; + end; + end; + Assert(Result <> nil, 'Alpha blending DC error: no bitmap available.'); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure AlphaBlendLineConstant(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer); + +// Blends a line of Count pixels from Source to Destination using a constant alpha value. +// The layout of a pixel must be BGRA where A is ignored (but is calculated as the other components). +// ConstantAlpha must be in the range 0..255 where 0 means totally transparent (destination pixel only) +// and 255 totally opaque (source pixel only). +// Bias is an additional value which gets added to every component and must be in the range -128..127 +// +{$ifdef CPUX64} +// RCX contains Source +// RDX contains Destination +// R8D contains Count +// R9D contains ConstantAlpha +// Bias is on the stack + +asm + //.NOFRAME + + // Load XMM3 with the constant alpha value (replicate it for every component). + // Expand it to word size. + MOVD XMM3, R9D // ConstantAlpha + PUNPCKLWD XMM3, XMM3 + PUNPCKLDQ XMM3, XMM3 + + // Load XMM5 with the bias value. + MOVD XMM5, [Bias] + PUNPCKLWD XMM5, XMM5 + PUNPCKLDQ XMM5, XMM5 + + // Load XMM4 with 128 to allow for saturated biasing. + MOV R10D, 128 + MOVD XMM4, R10D + PUNPCKLWD XMM4, XMM4 + PUNPCKLDQ XMM4, XMM4 + +@1: // The pixel loop calculates an entire pixel in one run. + // Note: The pixel byte values are expanded into the higher bytes of a word due + // to the way unpacking works. We compensate for this with an extra shift. + MOVD XMM1, DWORD PTR [RCX] // data is unaligned + MOVD XMM2, DWORD PTR [RDX] // data is unaligned + PXOR XMM0, XMM0 // clear source pixel register for unpacking + PUNPCKLBW XMM0, XMM1{[RCX]} // unpack source pixel byte values into words + PSRLW XMM0, 8 // move higher bytes to lower bytes + PXOR XMM1, XMM1 // clear target pixel register for unpacking + PUNPCKLBW XMM1, XMM2{[RDX]} // unpack target pixel byte values into words + MOVQ XMM2, XMM1 // make a copy of the shifted values, we need them again + PSRLW XMM1, 8 // move higher bytes to lower bytes + + // calculation is: target = (alpha * (source - target) + 256 * target) / 256 + PSUBW XMM0, XMM1 // source - target + PMULLW XMM0, XMM3 // alpha * (source - target) + PADDW XMM0, XMM2 // add target (in shifted form) + PSRLW XMM0, 8 // divide by 256 + + // Bias is accounted for by conversion of range 0..255 to -128..127, + // doing a saturated add and convert back to 0..255. + PSUBW XMM0, XMM4 + PADDSW XMM0, XMM5 + PADDW XMM0, XMM4 + PACKUSWB XMM0, XMM0 // convert words to bytes with saturation + MOVD DWORD PTR [RDX], XMM0 // store the result +@3: + ADD RCX, 4 + ADD RDX, 4 + DEC R8D + JNZ @1 +end; +{$else} +// EAX contains Source +// EDX contains Destination +// ECX contains Count +// ConstantAlpha and Bias are on the stack + +asm + PUSH ESI // save used registers + PUSH EDI + + MOV ESI, EAX // ESI becomes the actual source pointer + MOV EDI, EDX // EDI becomes the actual target pointer + + // Load MM6 with the constant alpha value (replicate it for every component). + // Expand it to word size. + MOV EAX, [ConstantAlpha] + DB $0F, $6E, $F0 /// MOVD MM6, EAX + DB $0F, $61, $F6 /// PUNPCKLWD MM6, MM6 + DB $0F, $62, $F6 /// PUNPCKLDQ MM6, MM6 + + // Load MM5 with the bias value. + MOV EAX, [Bias] + DB $0F, $6E, $E8 /// MOVD MM5, EAX + DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5 + DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5 + + // Load MM4 with 128 to allow for saturated biasing. + MOV EAX, 128 + DB $0F, $6E, $E0 /// MOVD MM4, EAX + DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4 + DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4 + +@1: // The pixel loop calculates an entire pixel in one run. + // Note: The pixel byte values are expanded into the higher bytes of a word due + // to the way unpacking works. We compensate for this with an extra shift. + DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking + DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words + DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes + DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking + DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words + DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again + DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes + + // calculation is: target = (alpha * (source - target) + 256 * target) / 256 + DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target + DB $0F, $D5, $C6 /// PMULLW MM0, MM6, alpha * (source - target) + DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form) + DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256 + + // Bias is accounted for by conversion of range 0..255 to -128..127, + // doing a saturated add and convert back to 0..255. + DB $0F, $F9, $C4 /// PSUBW MM0, MM4 + DB $0F, $ED, $C5 /// PADDSW MM0, MM5 + DB $0F, $FD, $C4 /// PADDW MM0, MM4 + DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation + DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result +@3: + ADD ESI, 4 + ADD EDI, 4 + DEC ECX + JNZ @1 + POP EDI + POP ESI +end; +{$endif CPUX64} + +//---------------------------------------------------------------------------------------------------------------------- + +procedure AlphaBlendLinePerPixel(Source, Destination: Pointer; Count, Bias: Integer); + +// Blends a line of Count pixels from Source to Destination using the alpha value of the source pixels. +// The layout of a pixel must be BGRA. +// Bias is an additional value which gets added to every component and must be in the range -128..127 +// +{$ifdef CPUX64} +// RCX contains Source +// RDX contains Destination +// R8D contains Count +// R9D contains Bias + +asm + //.NOFRAME + + // Load XMM5 with the bias value. + MOVD XMM5, R9D // Bias + PUNPCKLWD XMM5, XMM5 + PUNPCKLDQ XMM5, XMM5 + + // Load XMM4 with 128 to allow for saturated biasing. + MOV R10D, 128 + MOVD XMM4, R10D + PUNPCKLWD XMM4, XMM4 + PUNPCKLDQ XMM4, XMM4 + +@1: // The pixel loop calculates an entire pixel in one run. + // Note: The pixel byte values are expanded into the higher bytes of a word due + // to the way unpacking works. We compensate for this with an extra shift. + MOVD XMM1, DWORD PTR [RCX] // data is unaligned + MOVD XMM2, DWORD PTR [RDX] // data is unaligned + PXOR XMM0, XMM0 // clear source pixel register for unpacking + PUNPCKLBW XMM0, XMM1{[RCX]} // unpack source pixel byte values into words + PSRLW XMM0, 8 // move higher bytes to lower bytes + PXOR XMM1, XMM1 // clear target pixel register for unpacking + PUNPCKLBW XMM1, XMM2{[RDX]} // unpack target pixel byte values into words + MOVQ XMM2, XMM1 // make a copy of the shifted values, we need them again + PSRLW XMM1, 8 // move higher bytes to lower bytes + + // Load XMM3 with the source alpha value (replicate it for every component). + // Expand it to word size. + MOVQ XMM3, XMM0 + PUNPCKHWD XMM3, XMM3 + PUNPCKHDQ XMM3, XMM3 + + // calculation is: target = (alpha * (source - target) + 256 * target) / 256 + PSUBW XMM0, XMM1 // source - target + PMULLW XMM0, XMM3 // alpha * (source - target) + PADDW XMM0, XMM2 // add target (in shifted form) + PSRLW XMM0, 8 // divide by 256 + + // Bias is accounted for by conversion of range 0..255 to -128..127, + // doing a saturated add and convert back to 0..255. + PSUBW XMM0, XMM4 + PADDSW XMM0, XMM5 + PADDW XMM0, XMM4 + PACKUSWB XMM0, XMM0 // convert words to bytes with saturation + MOVD DWORD PTR [RDX], XMM0 // store the result +@3: + ADD RCX, 4 + ADD RDX, 4 + DEC R8D + JNZ @1 +end; +{$else} +// EAX contains Source +// EDX contains Destination +// ECX contains Count +// Bias is on the stack + +asm + PUSH ESI // save used registers + PUSH EDI + + MOV ESI, EAX // ESI becomes the actual source pointer + MOV EDI, EDX // EDI becomes the actual target pointer + + // Load MM5 with the bias value. + MOV EAX, [Bias] + DB $0F, $6E, $E8 /// MOVD MM5, EAX + DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5 + DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5 + + // Load MM4 with 128 to allow for saturated biasing. + MOV EAX, 128 + DB $0F, $6E, $E0 /// MOVD MM4, EAX + DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4 + DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4 + +@1: // The pixel loop calculates an entire pixel in one run. + // Note: The pixel byte values are expanded into the higher bytes of a word due + // to the way unpacking works. We compensate for this with an extra shift. + DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking + DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words + DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes + DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking + DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words + DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again + DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes + + // Load MM6 with the source alpha value (replicate it for every component). + // Expand it to word size. + DB $0F, $6F, $F0 /// MOVQ MM6, MM0 + DB $0F, $69, $F6 /// PUNPCKHWD MM6, MM6 + DB $0F, $6A, $F6 /// PUNPCKHDQ MM6, MM6 + + // calculation is: target = (alpha * (source - target) + 256 * target) / 256 + DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target + DB $0F, $D5, $C6 /// PMULLW MM0, MM6, alpha * (source - target) + DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form) + DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256 + + // Bias is accounted for by conversion of range 0..255 to -128..127, + // doing a saturated add and convert back to 0..255. + DB $0F, $F9, $C4 /// PSUBW MM0, MM4 + DB $0F, $ED, $C5 /// PADDSW MM0, MM5 + DB $0F, $FD, $C4 /// PADDW MM0, MM4 + DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation + DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result +@3: + ADD ESI, 4 + ADD EDI, 4 + DEC ECX + JNZ @1 + POP EDI + POP ESI +end; +{$endif CPUX64} + +//---------------------------------------------------------------------------------------------------------------------- + +procedure EMMS; + +// Reset MMX state to use the FPU for other tasks again. + +{$ifdef CPUX64} + inline; +begin +end; +{$else} +asm + DB $0F, $77 /// EMMS +end; +{$endif CPUX64} + +//---------------------------------------------------------------------------------------------------------------------- + +procedure AlphaBlendLineMaster(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer); + +// Blends a line of Count pixels from Source to Destination using the source pixel and a constant alpha value. +// The layout of a pixel must be BGRA. +// ConstantAlpha must be in the range 0..255. +// Bias is an additional value which gets added to every component and must be in the range -128..127 +// +{$ifdef CPUX64} +// RCX contains Source +// RDX contains Destination +// R8D contains Count +// R9D contains ConstantAlpha +// Bias is on the stack + +asm + .SAVENV XMM6 + + // Load XMM3 with the constant alpha value (replicate it for every component). + // Expand it to word size. + MOVD XMM3, R9D // ConstantAlpha + PUNPCKLWD XMM3, XMM3 + PUNPCKLDQ XMM3, XMM3 + + // Load XMM5 with the bias value. + MOV R10D, [Bias] + MOVD XMM5, R10D + PUNPCKLWD XMM5, XMM5 + PUNPCKLDQ XMM5, XMM5 + + // Load XMM4 with 128 to allow for saturated biasing. + MOV R10D, 128 + MOVD XMM4, R10D + PUNPCKLWD XMM4, XMM4 + PUNPCKLDQ XMM4, XMM4 + +@1: // The pixel loop calculates an entire pixel in one run. + // Note: The pixel byte values are expanded into the higher bytes of a word due + // to the way unpacking works. We compensate for this with an extra shift. + MOVD XMM1, DWORD PTR [RCX] // data is unaligned + MOVD XMM2, DWORD PTR [RDX] // data is unaligned + PXOR XMM0, XMM0 // clear source pixel register for unpacking + PUNPCKLBW XMM0, XMM1{[RCX]} // unpack source pixel byte values into words + PSRLW XMM0, 8 // move higher bytes to lower bytes + PXOR XMM1, XMM1 // clear target pixel register for unpacking + PUNPCKLBW XMM1, XMM2{[RCX]} // unpack target pixel byte values into words + MOVQ XMM2, XMM1 // make a copy of the shifted values, we need them again + PSRLW XMM1, 8 // move higher bytes to lower bytes + + // Load XMM6 with the source alpha value (replicate it for every component). + // Expand it to word size. + MOVQ XMM6, XMM0 + PUNPCKHWD XMM6, XMM6 + PUNPCKHDQ XMM6, XMM6 + PMULLW XMM6, XMM3 // source alpha * master alpha + PSRLW XMM6, 8 // divide by 256 + + // calculation is: target = (alpha * master alpha * (source - target) + 256 * target) / 256 + PSUBW XMM0, XMM1 // source - target + PMULLW XMM0, XMM6 // alpha * (source - target) + PADDW XMM0, XMM2 // add target (in shifted form) + PSRLW XMM0, 8 // divide by 256 + + // Bias is accounted for by conversion of range 0..255 to -128..127, + // doing a saturated add and convert back to 0..255. + PSUBW XMM0, XMM4 + PADDSW XMM0, XMM5 + PADDW XMM0, XMM4 + PACKUSWB XMM0, XMM0 // convert words to bytes with saturation + MOVD DWORD PTR [RDX], XMM0 // store the result +@3: + ADD RCX, 4 + ADD RDX, 4 + DEC R8D + JNZ @1 +end; +{$else} +// EAX contains Source +// EDX contains Destination +// ECX contains Count +// ConstantAlpha and Bias are on the stack + +asm + PUSH ESI // save used registers + PUSH EDI + + MOV ESI, EAX // ESI becomes the actual source pointer + MOV EDI, EDX // EDI becomes the actual target pointer + + // Load MM6 with the constant alpha value (replicate it for every component). + // Expand it to word size. + MOV EAX, [ConstantAlpha] + DB $0F, $6E, $F0 /// MOVD MM6, EAX + DB $0F, $61, $F6 /// PUNPCKLWD MM6, MM6 + DB $0F, $62, $F6 /// PUNPCKLDQ MM6, MM6 + + // Load MM5 with the bias value. + MOV EAX, [Bias] + DB $0F, $6E, $E8 /// MOVD MM5, EAX + DB $0F, $61, $ED /// PUNPCKLWD MM5, MM5 + DB $0F, $62, $ED /// PUNPCKLDQ MM5, MM5 + + // Load MM4 with 128 to allow for saturated biasing. + MOV EAX, 128 + DB $0F, $6E, $E0 /// MOVD MM4, EAX + DB $0F, $61, $E4 /// PUNPCKLWD MM4, MM4 + DB $0F, $62, $E4 /// PUNPCKLDQ MM4, MM4 + +@1: // The pixel loop calculates an entire pixel in one run. + // Note: The pixel byte values are expanded into the higher bytes of a word due + // to the way unpacking works. We compensate for this with an extra shift. + DB $0F, $EF, $C0 /// PXOR MM0, MM0, clear source pixel register for unpacking + DB $0F, $60, $06 /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words + DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, move higher bytes to lower bytes + DB $0F, $EF, $C9 /// PXOR MM1, MM1, clear target pixel register for unpacking + DB $0F, $60, $0F /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words + DB $0F, $6F, $D1 /// MOVQ MM2, MM1, make a copy of the shifted values, we need them again + DB $0F, $71, $D1, $08 /// PSRLW MM1, 8, move higher bytes to lower bytes + + // Load MM7 with the source alpha value (replicate it for every component). + // Expand it to word size. + DB $0F, $6F, $F8 /// MOVQ MM7, MM0 + DB $0F, $69, $FF /// PUNPCKHWD MM7, MM7 + DB $0F, $6A, $FF /// PUNPCKHDQ MM7, MM7 + DB $0F, $D5, $FE /// PMULLW MM7, MM6, source alpha * master alpha + DB $0F, $71, $D7, $08 /// PSRLW MM7, 8, divide by 256 + + // calculation is: target = (alpha * master alpha * (source - target) + 256 * target) / 256 + DB $0F, $F9, $C1 /// PSUBW MM0, MM1, source - target + DB $0F, $D5, $C7 /// PMULLW MM0, MM7, alpha * (source - target) + DB $0F, $FD, $C2 /// PADDW MM0, MM2, add target (in shifted form) + DB $0F, $71, $D0, $08 /// PSRLW MM0, 8, divide by 256 + + // Bias is accounted for by conversion of range 0..255 to -128..127, + // doing a saturated add and convert back to 0..255. + DB $0F, $F9, $C4 /// PSUBW MM0, MM4 + DB $0F, $ED, $C5 /// PADDSW MM0, MM5 + DB $0F, $FD, $C4 /// PADDW MM0, MM4 + DB $0F, $67, $C0 /// PACKUSWB MM0, MM0, convert words to bytes with saturation + DB $0F, $7E, $07 /// MOVD [EDI], MM0, store the result +@3: + ADD ESI, 4 + ADD EDI, 4 + DEC ECX + JNZ @1 + POP EDI + POP ESI +end; +{$endif CPUX64} + +//---------------------------------------------------------------------------------------------------------------------- + +procedure AlphaBlendLineMasterAndColor(Destination: Pointer; Count: Integer; ConstantAlpha, Color: Integer); + +// Blends a line of Count pixels in Destination against the given color using a constant alpha value. +// The layout of a pixel must be BGRA and Color must be rrggbb00 (as stored by a COLORREF). +// ConstantAlpha must be in the range 0..255. +// +{$ifdef CPUX64} +// RCX contains Destination +// EDX contains Count +// R8D contains ConstantAlpha +// R9D contains Color + +asm + //.NOFRAME + + // The used formula is: target = (alpha * color + (256 - alpha) * target) / 256. + // alpha * color (factor 1) and 256 - alpha (factor 2) are constant values which can be calculated in advance. + // The remaining calculation is therefore: target = (F1 + F2 * target) / 256 + + // Load XMM3 with the constant alpha value (replicate it for every component). + // Expand it to word size. (Every calculation here works on word sized operands.) + MOVD XMM3, R8D // ConstantAlpha + PUNPCKLWD XMM3, XMM3 + PUNPCKLDQ XMM3, XMM3 + + // Calculate factor 2. + MOV R10D, $100 + MOVD XMM2, R10D + PUNPCKLWD XMM2, XMM2 + PUNPCKLDQ XMM2, XMM2 + PSUBW XMM2, XMM3 // XMM2 contains now: 255 - alpha = F2 + + // Now calculate factor 1. Alpha is still in XMM3, but the r and b components of Color must be swapped. + BSWAP R9D // Color + ROR R9D, 8 + MOVD XMM1, R9D // Load the color and convert to word sized values. + PXOR XMM4, XMM4 + PUNPCKLBW XMM1, XMM4 + PMULLW XMM1, XMM3 // XMM1 contains now: color * alpha = F1 + +@1: // The pixel loop calculates an entire pixel in one run. + MOVD XMM0, DWORD PTR [RCX] + PUNPCKLBW XMM0, XMM4 + + PMULLW XMM0, XMM2 // calculate F1 + F2 * target + PADDW XMM0, XMM1 + PSRLW XMM0, 8 // divide by 256 + + PACKUSWB XMM0, XMM0 // convert words to bytes with saturation + MOVD DWORD PTR [RCX], XMM0 // store the result + + ADD RCX, 4 + DEC EDX + JNZ @1 +end; +{$else} +// EAX contains Destination +// EDX contains Count +// ECX contains ConstantAlpha +// Color is passed on the stack + +asm + // The used formula is: target = (alpha * color + (256 - alpha) * target) / 256. + // alpha * color (factor 1) and 256 - alpha (factor 2) are constant values which can be calculated in advance. + // The remaining calculation is therefore: target = (F1 + F2 * target) / 256 + + // Load MM3 with the constant alpha value (replicate it for every component). + // Expand it to word size. (Every calculation here works on word sized operands.) + DB $0F, $6E, $D9 /// MOVD MM3, ECX + DB $0F, $61, $DB /// PUNPCKLWD MM3, MM3 + DB $0F, $62, $DB /// PUNPCKLDQ MM3, MM3 + + // Calculate factor 2. + MOV ECX, $100 + DB $0F, $6E, $D1 /// MOVD MM2, ECX + DB $0F, $61, $D2 /// PUNPCKLWD MM2, MM2 + DB $0F, $62, $D2 /// PUNPCKLDQ MM2, MM2 + DB $0F, $F9, $D3 /// PSUBW MM2, MM3 // MM2 contains now: 255 - alpha = F2 + + // Now calculate factor 1. Alpha is still in MM3, but the r and b components of Color must be swapped. + MOV ECX, [Color] + BSWAP ECX + ROR ECX, 8 + DB $0F, $6E, $C9 /// MOVD MM1, ECX // Load the color and convert to word sized values. + DB $0F, $EF, $E4 /// PXOR MM4, MM4 + DB $0F, $60, $CC /// PUNPCKLBW MM1, MM4 + DB $0F, $D5, $CB /// PMULLW MM1, MM3 // MM1 contains now: color * alpha = F1 + +@1: // The pixel loop calculates an entire pixel in one run. + DB $0F, $6E, $00 /// MOVD MM0, [EAX] + DB $0F, $60, $C4 /// PUNPCKLBW MM0, MM4 + + DB $0F, $D5, $C2 /// PMULLW MM0, MM2 // calculate F1 + F2 * target + DB $0F, $FD, $C1 /// PADDW MM0, MM1 + DB $0F, $71, $D0, $08 /// PSRLW MM0, 8 // divide by 256 + + DB $0F, $67, $C0 /// PACKUSWB MM0, MM0 // convert words to bytes with saturation + DB $0F, $7E, $00 /// MOVD [EAX], MM0 // store the result + + ADD EAX, 4 + DEC EDX + JNZ @1 +end; +{$endif CPUX64} + +//---------------------------------------------------------------------------------------------------------------------- + +procedure AlphaBlend(Source, Destination: HDC; R: TRect; Target: TPoint; Mode: TBlendMode; ConstantAlpha, Bias: Integer); + +// Optimized alpha blend procedure using MMX instructions to perform as quick as possible. +// For this procedure to work properly it is important that both source and target bitmap use the 32 bit color format. +// R describes the source rectangle to work on. +// Target is the place (upper left corner) in the target bitmap where to blend to. Note that source width + X offset +// must be less or equal to the target width. Similar for the height. +// If Mode is bmConstantAlpha then the blend operation uses the given ConstantAlpha value for all pixels. +// If Mode is bmPerPixelAlpha then each pixel is blended using its individual alpha value (the alpha value of the source). +// If Mode is bmMasterAlpha then each pixel is blended using its individual alpha value multiplied by ConstantAlpha. +// If Mode is bmConstantAlphaAndColor then each destination pixel is blended using ConstantAlpha but also a constant +// color which will be obtained from Bias. In this case no offset value is added, otherwise Bias is used as offset. +// Blending of a color into target only (bmConstantAlphaAndColor) ignores Source (the DC) and Target (the position). +// CAUTION: This procedure does not check whether MMX instructions are actually available! Call it only if MMX is really +// usable. + +var + Y: Integer; + SourceRun, + TargetRun: PByte; + + SourceBits, + DestBits: Pointer; + SourceWidth, + SourceHeight, + DestWidth, + DestHeight: Integer; + +begin + if not IsRectEmpty(R) then + begin + // Note: it is tempting to optimize the special cases for constant alpha 0 and 255 by just ignoring soure + // (alpha = 0) or simply do a blit (alpha = 255). But this does not take the bias into account. + case Mode of + bmConstantAlpha: + begin + // Get a pointer to the bitmap bits for the source and target device contexts. + // Note: this supposes that both contexts do actually have bitmaps assigned! + SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight); + DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); + if Assigned(SourceBits) and Assigned(DestBits) then + begin + for Y := 0 to R.Bottom - R.Top - 1 do + begin + SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top); + Inc(SourceRun, 4 * R.Left); + TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y); + Inc(TargetRun, 4 * Target.X); + AlphaBlendLineConstant(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias); + end; + end; + EMMS; + end; + bmPerPixelAlpha: + begin + SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight); + DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); + if Assigned(SourceBits) and Assigned(DestBits) then + begin + for Y := 0 to R.Bottom - R.Top - 1 do + begin + SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top); + Inc(SourceRun, 4 * R.Left); + TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y); + Inc(TargetRun, 4 * Target.X); + AlphaBlendLinePerPixel(SourceRun, TargetRun, R.Right - R.Left, Bias); + end; + end; + EMMS; + end; + bmMasterAlpha: + begin + SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight); + DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); + if Assigned(SourceBits) and Assigned(DestBits) then + begin + for Y := 0 to R.Bottom - R.Top - 1 do + begin + SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top); + Inc(SourceRun, 4 * Target.X); + TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y); + AlphaBlendLineMaster(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias); + end; + end; + EMMS; + end; + bmConstantAlphaAndColor: + begin + // Source is ignored since there is a constant color value. + DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight); + if Assigned(DestBits) then + begin + for Y := 0 to R.Bottom - R.Top - 1 do + begin + TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + R.Top); + Inc(TargetRun, 4 * R.Left); + AlphaBlendLineMasterAndColor(TargetRun, R.Right - R.Left, ConstantAlpha, Bias); + end; + end; + EMMS; + end; + end; + end; +end; + +function GetRGBColor(Value: TColor): DWORD; + +// Little helper to convert a Delphi color to an image list color. + +begin + Result := ColorToRGB(Value); + case Result of + clNone: + Result := CLR_NONE; + clDefault: + Result := CLR_DEFAULT; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure PrtStretchDrawDIB(Canvas: TCanvas; DestRect: TRect; ABitmap: TBitmap); + +// Stretch draw on to the new canvas. + +var + Header, + Bits: Pointer; + HeaderSize, + BitsSize: Cardinal; + +begin + GetDIBSizes(ABitmap.Handle, HeaderSize, BitsSize); + + GetMem(Header, HeaderSize); + GetMem(Bits, BitsSize); + try + GetDIB(ABitmap.Handle, ABitmap.Palette, Header^, Bits^); + StretchDIBits(Canvas.Handle, DestRect.Left, DestRect.Top, DestRect.Right - DestRect.Left, DestRect.Bottom - + DestRect.Top, 0, 0, ABitmap.Width, ABitmap.Height, Bits, TBitmapInfo(Header^), DIB_RGB_COLORS, SRCCOPY); + finally + FreeMem(Header); + FreeMem(Bits); + end; +end; + + +//---------------------------------------------------------------------------------------------------------------------- + + +procedure FillDragRectangles(DragWidth, DragHeight, DeltaX, DeltaY: Integer; var RClip, RScroll, RSamp1, RSamp2, RDraw1, RDraw2: TRect); + +begin + // ScrollDC limits + RClip := Rect(0, 0, DragWidth, DragHeight); + if DeltaX > 0 then + begin + // move to the left + if DeltaY = 0 then + begin + // move only to the left + // background movement + RScroll := Rect(0, 0, DragWidth - DeltaX, DragHeight); + RSamp1 := Rect(0, 0, DeltaX, DragHeight); + RDraw1 := Rect(DragWidth - DeltaX, 0, DeltaX, DragHeight); + end + else + if DeltaY < 0 then + begin + // move to bottom left + RScroll := Rect(0, -DeltaY, DragWidth - DeltaX, DragHeight); + RSamp1 := Rect(0, 0, DeltaX, DragHeight); + RSamp2 := Rect(DeltaX, DragHeight + DeltaY, DragWidth - DeltaX, -DeltaY); + RDraw1 := Rect(0, 0, DragWidth - DeltaX, -DeltaY); + RDraw2 := Rect(DragWidth - DeltaX, 0, DeltaX, DragHeight); + end + else + begin + // move to upper left + RScroll := Rect(0, 0, DragWidth - DeltaX, DragHeight - DeltaY); + RSamp1 := Rect(0, 0, DeltaX, DragHeight); + RSamp2 := Rect(DeltaX, 0, DragWidth - DeltaX, DeltaY); + RDraw1 := Rect(0, DragHeight - DeltaY, DragWidth - DeltaX, DeltaY); + RDraw2 := Rect(DragWidth - DeltaX, 0, DeltaX, DragHeight); + end; + end + else + if DeltaX = 0 then + begin + // vertical movement only + if DeltaY < 0 then + begin + // move downwards + RScroll := Rect(0, -DeltaY, DragWidth, DragHeight); + RSamp2 := Rect(0, DragHeight + DeltaY, DragWidth, -DeltaY); + RDraw2 := Rect(0, 0, DragWidth, -DeltaY); + end + else + begin + // move upwards + RScroll := Rect(0, 0, DragWidth, DragHeight - DeltaY); + RSamp2 := Rect(0, 0, DragWidth, DeltaY); + RDraw2 := Rect(0, DragHeight - DeltaY, DragWidth, DeltaY); + end; + end + else + begin + // move to the right + if DeltaY > 0 then + begin + // move up right + RScroll := Rect(-DeltaX, 0, DragWidth, DragHeight); + RSamp1 := Rect(0, 0, DragWidth + DeltaX, DeltaY); + RSamp2 := Rect(DragWidth + DeltaX, 0, -DeltaX, DragHeight); + RDraw1 := Rect(0, 0, -DeltaX, DragHeight); + RDraw2 := Rect(-DeltaX, DragHeight - DeltaY, DragWidth + DeltaX, DeltaY); + end + else + if DeltaY = 0 then + begin + // to the right only + RScroll := Rect(-DeltaX, 0, DragWidth, DragHeight); + RSamp1 := Rect(DragWidth + DeltaX, 0, -DeltaX, DragHeight); + RDraw1 := Rect(0, 0, -DeltaX, DragHeight); + end + else + begin + // move down right + RScroll := Rect(-DeltaX, -DeltaY, DragWidth, DragHeight); + RSamp1 := Rect(0, DragHeight + DeltaY, DragWidth + DeltaX, -DeltaY); + RSamp2 := Rect(DragWidth + DeltaX, 0, -DeltaX, DragHeight); + RDraw1 := Rect(0, 0, -DeltaX, DragHeight); + RDraw2 := Rect(-DeltaX, 0, DragWidth + DeltaX, -DeltaY); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +type + TCustomImageListCast = class(TCustomImageList); + +procedure DrawImage(ImageList: TCustomImageList; Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean); + + procedure DrawDisabledImage(ImageList: TCustomImageList; Canvas: TCanvas; X, Y, Index: Integer); + var + Params: TImageListDrawParams; + begin + FillChar(Params, SizeOf(Params), 0); + Params.cbSize := SizeOf(Params); + Params.himl := ImageList.Handle; + Params.i := Index; + Params.hdcDst := Canvas.Handle; + Params.x := X; + Params.y := Y; + Params.fState := ILS_SATURATE; + ImageList_DrawIndirect(@Params); + end; + +begin + if Enabled then + TCustomImageListCast(ImageList).DoDraw(Index, Canvas, X, Y, Style, Enabled) + else + DrawDisabledImage(ImageList, Canvas, X, Y, Index); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function IsMouseCursorVisible(): Boolean; var CI: TCursorInfo; begin @@ -1299,11 +1299,11 @@ function IsMouseCursorVisible(): Boolean; // CURSOR_SHOWING (1) Visible // CURSOR_SUPPRESSED (2) Touch/Pen Input (Windows 8+) // https://msdn.microsoft.com/en-us/library/windows/desktop/ms648381(v=vs.85).aspx -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure ScaleImageList(const ImgList: TImageList; M, D: Integer); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure ScaleImageList(const ImgList: TImageList; M, D: Integer); var ii : integer; mb, ib, sib, smb : TBitmap; @@ -1365,15 +1365,15 @@ procedure ScaleImageList(const ImgList: TImageList; M, D: Integer); finally TmpImgList.Free; end; -end; - -function IsHighContrastEnabled(): Boolean; -var - l: HIGHCONTRAST; -begin +end; + +function IsHighContrastEnabled(): Boolean; +var + l: HIGHCONTRAST; +begin l.cbSize := SizeOf(l); Result := SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, @l, 0) and ((l.dwFlags and HCF_HIGHCONTRASTON) <> 0); -end; - - -end. +end; + + +end. diff --git a/Source/VirtualTrees.dtx b/Source/VirtualTrees.dtx index acc249d9c..2185b1417 100644 --- a/Source/VirtualTrees.dtx +++ b/Source/VirtualTrees.dtx @@ -1,6469 +1,6470 @@ - - - - - -@@ckButtonDisabled - - -@@ckCheckMixedDisabled - - -simple button - -@@ckEmpty - - -an empty image used as place holder radio buttons - -@@ckRadioCheckedDisabled - - -check boxes - - - - - - - - - - - - - - - - - -@@TBaseVirtualTree -TBaseVirtualTree is the main and base class for all other Virtual Treeview descendants. This class implements most of the -base features and abilities and can be used to derive new classes, which want to hide most of the details of the tree, -which other descendants like TVirtualStringTree publish. Do not use the base treeview as object. It is not meant to be -instantiated directly, instead via an descendant. - -@@TBaseVirtualTree.AdjustCoordinatesByIndent@TVTPaintInfo@Integer -During painting of the main column some coordinates must be adjusted due to the tree lines. -The offset resulting from the tree lines and indentation level is given in Indent. - -@@TBaseVirtualTree.AdjustImageBorder@TCustomImageList@TBidiMode@Integer@TRect@TVTImageInfo -Depending on the width of the image list as well as the given bidi mode R must be adjusted. - -@@TBaseVirtualTree.AdjustTotalCount@PVirtualNode@Integer@Boolean -Sets a node's total count to the given value and recursively adjusts the parent's total count -(actually, the adjustment is done iteratively to avoid function call overheads). - -@@TBaseVirtualTree.AdjustTotalHeight@PVirtualNode@Integer@Boolean -Sets a node's total height to the given value and recursively adjusts the parent's total height. - -@@TBaseVirtualTree.CalculateCacheEntryCount -Calculates the size of the position cache. - -@@TBaseVirtualTree.CalculateVerticalAlignments@Boolean@Boolean@PVirtualNode@Integer@Integer -Calculates the vertical alignment of the given node and its associated expand/collapse button during -a node paint cycle depending on the required node alignment style. - -@@TBaseVirtualTree.ChangeCheckState@PVirtualNode@TCheckState -Sets the check state of the node according to the given value and the node's check type. -If the check state must be propagated to the parent nodes and one of them refuses to change then -nothing happens and False is returned, otherwise True. - -@@TBaseVirtualTree.ClearNodeBackground@TVTPaintInfo@Boolean@Boolean@TRect -Erases a nodes background depending on what the application decides to do. -UseBackground determines whether or not to use the background picture, while Floating indicates -that R is given in coordinates of the small node bitmap or the superordinated target bitmap used in PaintTree. - -@@TBaseVirtualTree.CMDenySubclassing@TMessage -If a Windows XP Theme Manager component is used in the application it will try to subclass all controls which do not -explicitly deny this. Virtual Treeview knows how to handle XP themes so it does not need subclassing. - -@@TBaseVirtualTree.CMHintShow@TCMHintShow -Determines hint message (tooltip) and out-of-hint rect. -Note: A special handling is needed here because we cannot pass wide strings back to the caller. - I had to introduce the hint data record anyway so we can use this to pass the hint string. - We still need to set a dummy hint string in the message to make the VCL showing the hint window. - -@@TBaseVirtualTree.CMHintShowPause@TCMHintShowPause -Tells the application that the tree (and only the tree) does not want a delayed tool tip. -Normal hints / header hints use the default delay (except the first time). - -@@TBaseVirtualTree.CollectSelectedNodesLTR@Integer@Integer@Integer@TAlignment@TRect@TRect -Helper routine used when a draw selection takes place. This version handles left-to-right directionality. -In the process of adding or removing nodes the current selection is modified which requires to pack it after -the function returns. Another side effect of this method is that a temporary list of nodes will be created -(see also InternalCacheNode) which must be inserted into the current selection by the caller. - -@@TBaseVirtualTree.CollectSelectedNodesRTL@Integer@Integer@Integer@TAlignment@TRect@TRect -Helper routine used when a draw selection takes place. This version handles right-to-left directionality. -See also comments in CollectSelectedNodesLTR. - - -@@TBaseVirtualTree.DrawLineImage@TVTPaintInfo@Integer@Integer@Integer@Integer@TVTLineType@Boolean -Draws (depending on Style) one of the 5 line types of the tree. -If Reverse is True then a right-to-left column is being drawn, hence horizontal lines must be mirrored. -X and Y describe the left upper corner of the line image rectangle, while H denotes its height (and width). - -@@TBaseVirtualTree.FAlignment - - -@@TBaseVirtualTree.FAnimationDuration - - -@@TBaseVirtualTree.FAnimationType - - -@@TBaseVirtualTree.FAutoExpandDelay -amount of milliseconds to wait until a node is expanded if it is the -drop target - -@@TBaseVirtualTree.FAutoScrollDelay -amount of milliseconds to wait until autoscrolling becomes active - -@@TBaseVirtualTree.FAutoScrollInterval -determines speed of auto scrolling - -@@TBaseVirtualTree.FBackground -A background image loadable at design and runtime. - -@@TBaseVirtualTree.FButtonFillMode -for rectangular tree buttons only: how to fill them - -@@TBaseVirtualTree.FButtonStyle -style of the tree buttons - -@@TBaseVirtualTree.FChangeDelay -used to delay OnChange event - - -@@TBaseVirtualTree.FCheckImageKind -light or dark, cross marks or tick marks - -@@TBaseVirtualTree.FCheckImages -Reference to global image list to be used for the check images. - -@@TBaseVirtualTree.FCheckNode -node which "captures" an check event - -@@TBaseVirtualTree.FClipboardFormats -a list of clipboard format descriptions enabled for this tree - -@@TBaseVirtualTree.FColors -class comprising all customizable colors in the tree - -@@TBaseVirtualTree.FDefaultPasteMode -Used to determine where to add pasted nodes to. - -@@TBaseVirtualTree.FDottedBrush -used to paint dotted lines without special pens - -@@TBaseVirtualTree.FDragImage -drag image management - -@@TBaseVirtualTree.FDragImageKind -determines whether or not and what to show in the drag image - -@@TBaseVirtualTree.FDragManager -drag'n drop, cut'n paste - -@@TBaseVirtualTree.FDragOperations -determines which operations are allowed during drag'n drop - -@@TBaseVirtualTree.FDragScrollStart -Contains the start time when a tree does auto scrolling as drop target. - -@@TBaseVirtualTree.FDragSelection -temporary copy of FSelection used during drag'n drop - -@@TBaseVirtualTree.FDragThreshold -used to determine when to actually start a drag'n drop operation - -@@TBaseVirtualTree.FDragType -used to switch between OLE and VCL drag'n drop - -@@TBaseVirtualTree.FDrawSelectionMode -determines the paint mode for draw selection - -@@TBaseVirtualTree.FDrawSelShiftState -keeps the initial shift state when the user starts selection with -the mouse - -@@TBaseVirtualTree.FDropTargetNode -node currently selected as drop target - -@@TBaseVirtualTree.FEditDelay -determines time to elapse before a node goes into edit mode - -@@TBaseVirtualTree.FEditLink -used to comunicate with an application defined editor - -@@TBaseVirtualTree.FFontChanged -flag for keeping informed about font changes in the off screen buffer - -@@TBaseVirtualTree.FHeaderRect -Space which the header currently uses in the control (window coords). - -@@TBaseVirtualTree.FHintData -used while preparing the hint window - -@@TBaseVirtualTree.FHintMode -determines the kind of the hint window - -@@TBaseVirtualTree.FHotCursor -can be set to additionally indicate the current hot node - -@@TBaseVirtualTree.FIncrementalSearch -Used to determine whether and how incremental search is to be used. - -@@TBaseVirtualTree.FindInPositionCache@Cardinal@Cardinal -Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal -to the given vertical position. -The returned node does not necessarily occupy the given position but is the nearest one to start -iterating from to approach the real node for a given position. CurrentPos receives the actual position of the found -node which is needed for further iteration. - -@@TBaseVirtualTree.FindInPositionCache@PVirtualNode@Cardinal -Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal -to the position of the given node. - -@@TBaseVirtualTree.FLastClickPos -Used for retained drag start and wheel mouse scrolling. - -@@TBaseVirtualTree.FLastDropMode -set while dragging and used to track changes - -@@TBaseVirtualTree.FLastHintRect -Area which the must must leave to reshow a hint. - -@@TBaseVirtualTree.FLastSearchNode -Reference to node which was last found as search fit. - -@@TBaseVirtualTree.FLastSelectionLevel -keeps the last node level for constrained multiselection - -@@TBaseVirtualTree.FLastStructureChangeReason -used for delayed structur change event - -@@TBaseVirtualTree.FLastVCLDragTarget -A node cache for VCL drag'n drop (keywords: DragLeave on DragDrop). - -@@TBaseVirtualTree.FLineMode -tree lines or bands etc. - -@@TBaseVirtualTree.FLineStyle -style of the tree lines - -@@TBaseVirtualTree.FMargin -horizontal border distance - -@@TBaseVirtualTree.FNodeAlignment -determines how to interpret the align member of a node - -@@TBaseVirtualTree.FNodeDataSize -number of bytes to allocate with each node (in addition to its base -structure and the internal data), if -1 then do callback - - -@@TBaseVirtualTree.FOldFontChange -helper method pointer for tracking font changes in the off screen buffer - -@@TBaseVirtualTree.FOnAdvancedHeaderDraw -Used when owner draw is enabled for the header and a column -is set to owner draw mode. But only if OnHeaderDrawQueryElements -returns at least one element to be drawn by the application. -In this case OnHeaderDraw is not used. - -@@TBaseVirtualTree.FOnAfterCellPaint -triggered after a column of an item has been painted - -@@TBaseVirtualTree.FOnAfterItemErase -triggered after an item's background has been erased - -@@TBaseVirtualTree.FOnAfterItemPaint -triggered after an item has been painted - -@@TBaseVirtualTree.FOnBeforeCellPaint -triggered when a column of an item is about to be painted - -@@TBaseVirtualTree.FOnBeforeItemErase -triggered when an item's background is about to be erased - -@@TBaseVirtualTree.FOnBeforeItemPaint -triggered when an item is about to be painted - -@@TBaseVirtualTree.FOnChange -selection change - -@@TBaseVirtualTree.FOnChecking -called just before a node's check state is changed - -@@TBaseVirtualTree.FOnCompareNodes -used during sort - -@@TBaseVirtualTree.FOnCreateDataObject -called to allow for app./descentant defined data objects - -@@TBaseVirtualTree.FOnCreateDragManager -called to allow for app./descendant defined drag managers - -@@TBaseVirtualTree.FOnCreateEditor -called when a node goes into edit mode, this allows applications -to supply their own editor - -@@TBaseVirtualTree.FOnDragAllowed -used to get permission for manual drag in mouse down - -@@TBaseVirtualTree.FOnDragDrop -called on release of mouse button (if drop was allowed) - -@@TBaseVirtualTree.FOnDragOver -called for every mouse move - -@@TBaseVirtualTree.FOnEditCancelled -called when editing has been cancelled - -@@TBaseVirtualTree.FOnEdited -called when editing has successfully been finished - -@@TBaseVirtualTree.FOnEditing -called just before a node goes into edit mode - -@@TBaseVirtualTree.FOnFocusChanged -called when the focus goes to a new node and/or column - -@@TBaseVirtualTree.FOnFocusChanging -called when the focus is about to go to a new node and/or column -(can be cancelled) - -@@TBaseVirtualTree.FOnFreeNode -called when a node is about to be destroyed, user data can and should -be freed in this event - -@@TBaseVirtualTree.FOnGetCursor -called to allow the app. to set individual cursors - -@@TBaseVirtualTree.FOnGetHeaderCursor -triggered to allow the app. to use customized cursors for the header - -@@TBaseVirtualTree.FOnGetHelpContext -called when a node specific help theme should be called - -@@TBaseVirtualTree.FOnGetImage -used to retrieve the image index of a given node - -@@TBaseVirtualTree.FOnGetLineStyle -triggered when a custom line style is used and the pattern brush -needs to be build - -@@TBaseVirtualTree.FOnGetNodeDataSize -called if NodeDataSize is -1 - -@@TBaseVirtualTree.FOnGetPopupMenu -called when the popup for a node needs to be shown - -@@TBaseVirtualTree.FOnGetUserClipboardFormats -gives application/descentants the opportunity to -add own clipboard formats on the fly - -@@TBaseVirtualTree.FOnHeaderDragged -header (column) drag'n drop - -@@TBaseVirtualTree.FOnHeaderDraggedOut -header (column) drag'n drop, which did not result in a valid drop. - -@@TBaseVirtualTree.FOnHeaderDragging -header (column) drag'n drop - -@@TBaseVirtualTree.FOnHeaderDraw -Used when owner draw is enabled for the header and a column is set -to owner draw mode. - -@@TBaseVirtualTree.FOnHeaderDrawQueryElements -Used for advanced header painting to query the -application for the elements, which are drawn by it and which should -be drawn by the tree. - -@@TBaseVirtualTree.FOnHotChange -called when the current "hot" node (that is, the node under the mouse) -changes and hot tracking is enabled - -@@TBaseVirtualTree.FOnIncrementalSearch -triggered on every key press (not key down) - -@@TBaseVirtualTree.FOnInitChildren -called when a node's children are needed (expanding etc.) - -@@TBaseVirtualTree.FOnInitNode -called when a node needs to be initialized (child count etc.) - -@@TBaseVirtualTree.FOnKeyAction -used to selectively prevent key actions (full expand on Ctrl+'+' etc.) - -@@TBaseVirtualTree.FOnMeasureItem -Triggered when a node is about to be drawn and its height was not yet -determined by the application. - -@@TBaseVirtualTree.FOnNodeCopied -call after a node has been copied - -@@TBaseVirtualTree.FOnNodeCopying -called when an node is copied to another parent node (probably in -another tree, but within the same application, can be cancelled) - -@@TBaseVirtualTree.FOnNodeMoved -called after a node and its children have been moved to another -parent node (probably another tree, but within the same application) - -@@TBaseVirtualTree.FOnNodeMoving -called just before a node is moved from one parent node to another -(this can be cancelled) - -@@TBaseVirtualTree.FOnPaintBackground -triggered if a part of the tree's background must be erased which is -not covered by any node - -@@TBaseVirtualTree.FOnRenderOLEData -application/descendant defined clipboard formats - -@@TBaseVirtualTree.FOnResetNode -called when a node is set to be uninitialized - -@@TBaseVirtualTree.FOnScroll -called when one or both paint offsets changed - -@@TBaseVirtualTree.FOnStateChange -Called whenever a state in the tree changes. - -@@TBaseVirtualTree.FOnStructureChange -structural change like adding nodes etc. - -@@TBaseVirtualTree.FOnUpdating -called from BeginUpdate, EndUpdate, BeginSynch and EndSynch - -@@TBaseVirtualTree.FPanningCursor -Current wheel panning cursor. - -@@TBaseVirtualTree.FPanningImage -A little 32x32 bitmap to indicate the panning reference point. - -@@TBaseVirtualTree.FPanningWindow -Helper window for wheel panning - -@@TBaseVirtualTree.FPendingCheckState -the new state the check node will get if all wents fine - -@@TBaseVirtualTree.FPositionCache -array which stores node references ordered by vertical positions -(see also DoValidateCache for more information) - -@@TBaseVirtualTree.FRangeAnchor -anchor node for selection with the keyboard, determines start of a -selection range - -@@TBaseVirtualTree.FScrollBarOptions -common properties of horizontal and vertical scrollbar - -@@TBaseVirtualTree.FScrollDirections -directions to scroll client area into depending on mouse position - -@@TBaseVirtualTree.FSearchBuffer -Collects a sequence of keypresses used to do incremental searching. - -@@TBaseVirtualTree.FSearchDirection -Direction to incrementally search the tree. - -@@TBaseVirtualTree.FSearchStart -Where to start iteration on each key press. - -@@TBaseVirtualTree.FSearchTimeout -Number of milliseconds after which to stop incremental searching. - -@@TBaseVirtualTree.FSelection -list of currently selected nodes - -@@TBaseVirtualTree.FSelectionBlendFactor -Determines the factor by which the selection rectangle is to be -faded if enabled. - -@@TBaseVirtualTree.FSelectionCount -number of currently selected nodes (size of FSelection might differ) - -@@TBaseVirtualTree.FSelectionCurveRadius -radius for rounded selection rectangles - -@@TBaseVirtualTree.FSingletonNodeArray -Contains only one element for quick addition of single nodes -to the selection. - -@@TBaseVirtualTree.FStartIndex -index to start validating cache from - -@@TBaseVirtualTree.FStates -various active/pending states the tree needs to consider - -@@TBaseVirtualTree.FSynchUpdateCount -synchronizer, causes all events which are usually done via timers -to happen immediately, regardless of the normal update state - -@@TBaseVirtualTree.FTempNodeCache -used at various places to hold temporarily a bunch of node refs. - -@@TBaseVirtualTree.FTempNodeCount -number of nodes in FTempNodeCache - -@@TBaseVirtualTree.FTextMargin -space between the node's text and its horizontal bounds - -@@TBaseVirtualTree.FTotalInternalDataSize -Cache of the sum of the necessary internal data size for all tree -classes derived from this base class. - -@@TBaseVirtualTree.FUpdateCount -update stopper, updates of the tree control are only done if = 0 - -@@TBaseVirtualTree.FVCLDragEffect -A cache for VCL drag'n drop to keep the current drop effect. - -@@TBaseVirtualTree.FVisibleCount -number of currently visible nodes - -@@TBaseVirtualTree.FWantTabs -If True then the tree also consumes the tab key. - -@@TBaseVirtualTree.GetDragManager -Returns the internal drag manager interface. If this does not yet exist then it is created here. - -@@TBaseVirtualTree.GetFullyVisible@PVirtualNode -Determines whether the given node has the visibility flag set as well as all its parents are expanded. - -@@TBaseVirtualTree.GetVisible@PVirtualNode -Determines if the given node marked as being visible. - -@@TBaseVirtualTree.GetVisiblePath@PVirtualNode -Determines if all parents of the given node are expanded and have the visibility flag set. - - -@@TBaseVirtualTree.HandleDrawSelection@Integer@Integer -Handles multi-selection with a focus rectangle. -Result is True if something changed in selection. - -@@TBaseVirtualTree.HasVisibleNextSibling@PVirtualNode -Helper method to determine if the given node has a visible sibling. This is needed to -draw correct tree lines. - -@@TBaseVirtualTree.InitializeFirstColumnValues@TVTPaintInfo -Determines initial index, position and cell size of the first visible column. - - -@@TBaseVirtualTree.InitRootNode@Cardinal -Reinitializes the root node. - -@@TBaseVirtualTree.InterruptValidation -Waits until the worker thread has stopped validating the caches of this tree. - -@@TBaseVirtualTree.IsFirstVisibleChild@PVirtualNode@PVirtualNode -Helper method to check if Node is the same as the first visible child of Parent. - -@@TBaseVirtualTree.IsLastVisibleChild@PVirtualNode@PVirtualNode -Helper method to check if Node is the same as the last visible child of Parent. - - -@@TBaseVirtualTree.PrepareBitmaps@Boolean@Boolean -initializes the contents of the internal bitmaps - -@@TBaseVirtualTree.PrepareCell@TVTPaintInfo@Integer@Integer -This method is called immediately before a cell's content is drawn und is responsible to paint selection colors etc. - -@@TBaseVirtualTree.ReadOldOptions@TReader -Migration helper routine to silently convert forms containing the old tree options member into the new -sub-options structure. - -@@TBaseVirtualTree.SetChildCount@PVirtualNode@Cardinal -Changes a node's child structure to accomodate the new child count. This is used to add or delete -child nodes to/from the end of the node's child list. To insert or delete a specific node a separate -routine is used. - -@@TBaseVirtualTree.SetFullyVisible@PVirtualNode@Boolean -This method ensures that a node is visible and all its parent nodes are expanded and also visible -if Value is True. Otherwise the visibility flag of the node is reset but the expand state -of the parent nodes stays untouched. - -@@TBaseVirtualTree.SetVisible@PVirtualNode@Boolean -Sets the visibility style of the given node according to Value. - -@@TBaseVirtualTree.SetVisiblePath@PVirtualNode@Boolean -If Value is True then all parent nodes of Node are expanded. - -@@TBaseVirtualTree.TileBackground@TBitmap@TCanvas@TPoint@TRect -Draws the given source graphic so that it tiles into the given rectangle which is relative to the target bitmap. -The graphic is aligned so that it always starts at the upper left corner of the target canvas. -Offset gives the position of the target window in an possible superordinated surface. - -@@TBaseVirtualTree.WMContextMenu@TWMContextMenu -This method is called when a popup menu is about to be displayed. -We have to cancel some pending states here to avoid interferences. - -@@TBaseVirtualTree.WMHScroll@TWMHScroll -local functions - -@@TBaseVirtualTree.WMKeyDown@TWMKeyDown -Keyboard event handling for node focus, selection, node specific popup menus and help invokation. -For a detailed description of every action done here read the help. - - -@@TBlendMode.bmConstantAlpha -apply given constant alpha - -@@TBlendMode.bmConstantAlphaAndColor -blend the destination color with the given constant color und the constant alpha value - -@@TBlendMode.bmMasterAlpha -use alpha value of source pixel and multiply it with the constant alpha value - -@@TBlendMode.bmPerPixelAlpha -use alpha value of the source pixel - -@@TChangeReason.crAccumulated -used for delayed changes - -@@TChangeReason.crChildAdded -one or more child nodes have been added - -@@TChangeReason.crChildDeleted -one or more child nodes have been deleted - -@@TChangeReason.crIgnore -used as placeholder - -@@TChangeReason.crNodeAdded -a node has been added - -@@TChangeReason.crNodeCopied -a node has been duplicated - -@@TChangeReason.crNodeMoved -a node has been moved to a new place - - -@@TCheckImageKind -Summary -Determines which images should be used for checkboxes and radio buttons. - -Description -Provided with the tree are nine different image sets for the check images used when toCheckSupport is enabled in -TreeOptions. - - -
- - -Eight of the nine lists are predefined while one is a custom check image list, which can be filled by the application. -Use ckCustom as CheckImageKind value and assign an image list to the CustomCheckImages property to enable custom images. - - - -The order of the images in the image lists is always as listed below. Make sure you have the same amount of images in -your custom image list, if you want own check images. - - - - * empty image (ckEmpty) - -Radio buttons: - - * uncheck normal (ckRadioUncheckedNormal) - * unchecked hot (ckRadioUncheckedHot) - * unchecked pressed (ckRadioUncheckedPressed) - * unchecked disabled (ckRadioUncheckedDisabled) - * checked normal (ckRadioCheckedNormal) - * checked hot (ckRadioCheckedHot) - * checked pressed (ckRadioCheckedPressed) - * checked disabled (ckRadioCheckedDisabled) - -Check boxes: - - * unchecked normal (ckCheckUncheckedNormal) - * unchecked hot (ckCheckUncheckedHot) - * unchecked pressed (ckCheckUncheckedPressed) - * unchecked disabled (ckCheckUncheckedDisabled) - * checked normal (ckCheckCheckedNormal) - * checked hot (ckCheckCheckedHot) - * checked pressed (ckCheckCheckedPressed) - * checked disabled (ckCheckCheckedDisabled) - * mixed normal (ckCheckMixedNormal) - * mixed hot (ckCheckMixedHot) - * mixed pressed (ckCheckMixedPressed) - * mixed disabled (ckCheckMixedDisabled) - -Node buttons: - - * button normal (ckButtonNormal) - * button hot (ckButtonHot) - * button pressed (ckButtonPressed) - * button disabled (ckButtonDisabled) - -@@TCheckImageKind.ckCustom -application defined check images - -@@TCheckImageKind.ckDarkCheck -black cross - -@@TCheckImageKind.ckDarkTick -black tick mark - -@@TCheckImageKind.ckFlat -flat images (no 3D border) - -@@TCheckImageKind.ckLightCheck -gray cross - -@@TCheckImageKind.ckLightTick -gray tick mark - - -@@TCheckImageKind.ckSystemFlat -Flat system defined check images. - -@@TCheckImageKind.ckXP -Windows XP style - -@@TCheckState.csCheckedNormal -checked and not pressed - -@@TCheckState.csCheckedPressed -checked and pressed - -@@TCheckState.csMixedNormal -3-state check box and not pressed - -@@TCheckState.csMixedPressed -3-state check box and pressed - -@@TCheckState.csUncheckedNormal -unchecked and not pressed - -@@TCheckState.csUncheckedPressed -unchecked and pressed - - -@@TClipboardFormatList.Add@string@TVirtualTreeClass@Cardinal@TFormatEtc -Adds the given data to the internal list. The priority value is used to sort formats for importance. Larger priority -values mean less priority. - -@@TClipboardFormatList.EnumerateFormats@TVirtualTreeClass@TFormatEtcArray@TClipboardFormats -Returns a list of format records for the given class. If assigned the AllowedFormats is used to limit the -enumerated formats to those described in the list. - -@@TClipboardFormatList.EnumerateFormats@TVirtualTreeClass@TStrings -Returns a list of format descriptions for the given class. - -@@TClipboardFormatList.Sort -local function - -@@TClipboardFormatListEntry.Description -The string used to register the format with Windows. - -@@TClipboardFormatListEntry.FormatEtc -The definition of the format in the IDataObject. - -@@TClipboardFormatListEntry.Priority -Number which determines the order of formats used in IDataObject. - -@@TClipboardFormatListEntry.TreeClass -The tree class which supports rendering this format. - -@@TClipboardFormats -Summary -List of strings describing clipboard formats. - - - -Description -This class is an extended string list which allows to enter description strings for clipboard formats which are checked -agains registered formats and only accepted if the particular format could be found. This way there is an unambiqious and -portable description of allowed clipboard formats possible. - -@@TClipboardFormats.Add@string -Summary -Adds a new format to the internal list. - - - -Description -Adds or inserts a new format to the internal list but restricts additions to the clipbard formats to only those which are -registered with the owner tree or one of its ancestors. - -@@TClipboardFormats.Insert@Integer@string - - - -@@TCustomVirtualStringTree.FDefaultText -text to show if there's no OnGetText event handler (e.g. at design time) - -@@TCustomVirtualStringTree.FEllipsisWidth -width of '...' for the current font - -@@TCustomVirtualStringTree.FInternalDataOffset -offset to the internal data of the string tree - -@@TCustomVirtualStringTree.FOnNewText -used to notify the application about an edited node caption - -@@TCustomVirtualStringTree.FOnPaintText -triggered before either normal or fixed text is painted to allow -even finer customization (kind of sub cell painting) - -@@TCustomVirtualStringTree.FOnShortenString -used to allow the application a customized string shortage - -@@TCustomVirtualStringTree.FTextHeight -true size of the font - - -@@TEnumFormatEtc - - - - - -@@THeaderState.hsAutoSizing -auto size chain is in progess, do not trigger again on WM_SIZE - -@@THeaderState.hsDragging -header dragging is in progress (only if enabled) - -@@THeaderState.hsDragPending -left button is down, user might want to start dragging a column - -@@THeaderState.hsLoading -The header currently loads from stream, so updates are not necessary. - - - -@@THintAnimationType.hatFade -fade in the hint/tooltip, like in Windows 2000 - -@@THintAnimationType.hatNone -no animation at all, just display hint/tooltip - -@@THintAnimationType.hatSlide -slide in the hint/tooltip, like in Windows 98 - -@@THintAnimationType.hatSystemDefault -use what the system is using (slide for Win9x, slide/fade for Win2K+, depends on settings) - - -@@THitPosition.hiAbove -above the client area (if relative) or the absolute tree area - -@@THitPosition.hiBelow -below the client area (if relative) or the absolute tree area - -@@THitPosition.hiNowhere -no node is involved (possible only if the tree is not as tall as the client area) - -@@THitPosition.hiOnItem -on the bitmaps/buttons or label associated with an item - -@@THitPosition.hiOnItemButton -on the button associated with an item - -@@THitPosition.hiOnItemCheckbox -on the checkbox if enabled - -@@THitPosition.hiOnItemIndent -in the indentation area in front of a node - -@@THitPosition.hiOnItemLabel -on the normal text area associated with an item - -@@THitPosition.hiOnItemLeft -in the area to the left of a node's text area (e.g. when right aligned or centered) - -@@THitPosition.hiOnItemRight -in the area to the right of a node's text area (e.g. if left aligned or centered) - -@@THitPosition.hiOnNormalIcon -on the "normal" image - -@@THitPosition.hiOnStateIcon -on the state image - -@@THitPosition.hiToLeft -to the left of the client area (if relative) or the absolute tree area - -@@THitPosition.hiToRight -to the right of the client area (if relative) or the absolute tree area - - -@@TItemEraseAction.eaColor -Use the provided color to erase the background instead the one of the tree. - -@@TItemEraseAction.eaDefault -The tree should erase the item's background (bitmap or solid). - -@@TItemEraseAction.eaNone -Do nothing. Let the application paint the background. - - - -@@TScrollBarOptions - - -@@TScrollBarOptions.FScrollBars -used to hide or show vertical and/or horizontal scrollbar - -@@TScrollBarOptions.FScrollBarStyle -kind of scrollbars to use - - -@@TStringEditLink -Summary -TStringEditLink is the standard node editor of a TVirtualStringTree. - -Description -TStringEditLink implements the interface IVTEditLink. This is a simple node editor which wraps a TEdit and is not Unicode -aware. A virtual string tree will use this node editor if the event OnCreateEditor is not handled and a node must be -edited. After the node's text has been edited the event OnNewText will be fired and the application should replace the -\old text with the new and edited text. - - - -The node editor instance will automatically be destroyed via reference counting when it is not needed anymore. Never -destroy it explicitly - except when you know what you are doing. - - - -Remarks -If you want to modify some aspects of how the node editor works, i.e. suppress some characters or initialize it with a -different text but the node's text, you can inherit your own class from TStringEditLink and return an instance of it in -the OnCreateEditor event. - -@@TStringEditLink.BeginEdit -Summary -This function will be called by the virtual string tree when the editing starts. - -Description -Please see interface IVTEditLink for a detailed explanation of this interface function. - -@@TStringEditLink.CancelEdit -Summary -This function will be called by the virtual string tree when the current editing is about to be cancelled. - -Description -Please see interface IVTEditLink for a detailed explanation of this interface function. - -@@TStringEditLink.EndEdit -Summary -This function will be called by the virtual string tree when the current editing is being finished. - -Description -Please see interface IVTEditLink for a detailed explanation of this interface function. - -@@TStringEditLink.FColumn -The column of the node. - -@@TStringEditLink.FEdit -A normal custom edit control. - -@@TStringEditLink.FNode -The node to be edited. - -@@TStringEditLink.FStopping -Set to True when the edit link requests stopping the edit action. - -@@TStringEditLink.FTextBounds -Smallest rectangle around the text. - -@@TStringEditLink.FTree -A back reference to the tree calling. - -@@TStringEditLink.GetBounds -Summary -The virtual string tree uses this function to get the current bounding rect of the node editor. - -Description -Please see interface IVTEditLink for a detailed explanation of this interface function. - -@@TStringEditLink.PrepareEdit@TBaseVirtualTree@PVirtualNode@TColumnIndex -Summary -This function is called by a virtual string tree to initialize the node editor. - -Description -Please see interface IVTEditLink for a detailed explanation of this interface function. - -@@TStringEditLink.ProcessMessage@TMessage -Summary -This function is used to forward messages being directed to the virtual string tree. - -Description -Please see interface IVTEditLink for a detailed explanation of this interface function. - -@@TStringEditLink.SetBounds@TRect -Summary -The virtual string tree calls this function to initialize the bounding rect of the node editor. - -Description -Please see interface IVTEditLink for a detailed explanation of this interface function. - -@@TStringTreeOptions -Summary -Options class used in the string tree and its descentants. - -Description -This options class publishes all properties inherited from its ancestor and does not add any further functionality. - -@@TToggleAnimationData.Brush -the brush to be used to erase uncovered parts - -@@TToggleAnimationData.DC -the DC of the window to erase unconvered parts - - -@@TToggleAnimationData.Window -copy of the tree's window handle - -@@TVirtualDrawTree -Summary -Descendant of TBaseVirtualTree, which passes node paint events through to the application (similar to a draw grid) - -Description -This tree implementation enhances the base tree to allow the application to draw its own stuff into the tree window. - - -@@TVirtualNode.Align -line/button alignment - -@@TVirtualNode.CheckState -indicates the current check state (e.g. checked, pressed etc.) - -@@TVirtualNode.CheckType -indicates which check type shall be used for this node - -@@TVirtualNode.Data -this is a placeholder, each node gets extra data determined by NodeDataSize - -@@TVirtualNode.Dummy -dummy value to fill DWORD boundary - -@@TVirtualNode.NodeHeight -height in pixels - -@@TVirtualNode.States -states describing various properties of the node (expanded, initialized etc.) - -@@TVirtualNodeState.vsAllChildrenHidden -Set if vsHasChildren is set and no child node has the vsVisible flag set. - -@@TVirtualNodeState.vsChecking -Node's check state is changing, avoid propagation. - -@@TVirtualNodeState.vsClearing -A node's children are being deleted. Don't register structure change event. - -@@TVirtualNodeState.vsCutOrCopy -Node is selected as cut or copy and paste source. - -@@TVirtualNodeState.vsDeleting -Set when the node is about to be freed. - -@@TVirtualNodeState.vsDisabled -Set if node is disabled. - -@@TVirtualNodeState.vsExpanded -Set if the node is expanded. - -@@TVirtualNodeState.vsHasChildren -Indicates the presence of child nodes without actually setting them. - -@@TVirtualNodeState.vsHeightMeasured -Node height has been determined and does not need a recalculation. - -@@TVirtualNodeState.vsInitialized -Set after the node has been initialized. - -@@TVirtualNodeState.vsInitialUserData -Set if (via AddChild or InsertNode) initial user data has been set which requires OnFreeNode. - -@@TVirtualNodeState.vsMultiline -Node text is wrapped at the cell boundaries instead of being shorted. - -@@TVirtualNodeState.vsSelected -Set if the node is in the current selection. - -@@TVirtualNodeState.vsVisible -Indicate whether the node is visible or not (independant of the expand states of its parents). - - -@@TVirtualTreeColumn.ComputeHeaderLayout@HDC@TRect@Boolean@Boolean@TPoint@TPoint@TRect -Description -The layout of a column header is determined by a lot of factors. This method takes them all into account and determines -all necessary positions and bounds: - - * for the header text - * the header glyph - * the sort glyph - -Summary -Calculates the layout of a column header. - -@@TVirtualTreeColumn.GetRect -Summary -\Returns the rectangle this column occupies in the header (relative to (0, 0) of the non-client area). - -Description -\Returns the rectangle this column occupies in the header (relative to (0, 0) of the non-client area). - - -@@TVirtualTreeColumns.AdjustAutoSize@TColumnIndex@Boolean -Description -Called only if the header is in auto-size mode which means a column needs to be so large that it fills all the horizontal -space not occupied by the other columns. CurrentIndex (if not InvalidColumn) describes which column has just been -resized. - -Summary -Called when columns must be sized so that the fit the client area. - -@@TVirtualTreeColumns.AdjustDownColumn@TPoint -Description -If this column is allowed to be clicked then it is also kept for later use. - -Summary -Determines the column from the given position and returns it. - -@@TVirtualTreeColumns.AdjustHoverColumn@TPoint -Summary -Determines the new hover column index and returns true if the index actually changed else False. - -Description -Determines the new hover column index and returns true if the index actually changed else False. - -@@TVirtualTreeColumns.AdjustPosition@TVirtualTreeColumn@Cardinal -Summary -Reorders the column position array so that the given column gets the given position. - -Description -Reorders the column position array so that the given column gets the given position. - -@@TVirtualTreeColumns.AnimatedResize@TColumnIndex@Integer -Summary -Resizes the given column animated by scrolling the window DC. - -Description -Resizes the given column animated by scrolling the window DC. - -@@TVirtualTreeColumns.ColumnFromPosition@TColumnPosition -Summary -\Returns the index of the column at the given position. - -Description -\Returns the index of the column at the given position. - -@@TVirtualTreeColumns.ColumnFromPosition@TPoint@Boolean -Summary -Determines the current column based on the position passed in P. - -Description -Determines the current column based on the position passed in P. - -@@TVirtualTreeColumns.DrawXPButton@HDC@TRect@Boolean@Boolean@Boolean -Summary -Helper procedure to draw an Windows XP like header button. - -Description -Helper procedure to draw an Windows XP like header button. - -@@TVirtualTreeColumns.Equals@TVirtualTreeColumns -Summary -Compares itself with the given set of columns. - -Description -Equals returns true if all published properties are the same (including column order), otherwise false is returned. - -@@TVirtualTreeColumns.FClearing -True if columns are being deleted entirely. - -@@TVirtualTreeColumns.FClickIndex -last clicked column - -@@TVirtualTreeColumns.FDragIndex -index of column currently being dragged - -@@TVirtualTreeColumns.FDropBefore -True if drop position is in the left half of a column, False for the right -side to drop the dragged column to - -@@TVirtualTreeColumns.FDropTarget -current target column (index) while dragging - -@@TVirtualTreeColumns.FHeaderBitmap -backbuffer for drawing - -@@TVirtualTreeColumns.FixPositions -Summary -Fixes column positions after loading from DFM. - -Description -Fixes column positions after loading from DFM. - -@@TVirtualTreeColumns.FNeedPositionsFix -True if FixPositions must still be called after DFM loading. - -@@TVirtualTreeColumns.GetColumnAndBounds@TPoint@Integer@Integer@Boolean -Summary -\Returns the column where the mouse is currently in as well as the left and right bound of this column. - -Description -Left and Right are undetermined if no column is involved. - -@@TVirtualTreeColumns.GetColumnBounds@TColumnIndex@Integer@Integer -Summary -\Returns the left and right bound of the given column. - -Description -If Column is NoColumn then the entire client width is returned. - -@@TVirtualTreeColumns.GetFirstVisibleColumn -Summary -\Returns the index of the first visible column or "InvalidColumn" if either no columns are defined or all columns are -hidden. - -Description -\Returns the index of the first visible column or "InvalidColumn" if either no columns are defined or all columns are -hidden. - -@@TVirtualTreeColumns.GetLastVisibleColumn -Summary -\Returns the index of the last visible column or "InvalidColumn" if either no columns are defined or all columns are -hidden. - - - -Description -\Returns the index of the last visible column or "InvalidColumn" if either no columns are defined or all columns are -hidden. - -@@TVirtualTreeColumns.GetNextColumn@TColumnIndex -Summary -\Returns the next column in display order. Column is the index of an item in the collection (a column). - -Description -\Returns the next column in display order. Column is the index of an item in the collection (a column). - -@@TVirtualTreeColumns.GetNextVisibleColumn@TColumnIndex -Summary -\Returns the next visible column in display order, Column is an index into the columns list. - - - -Description -\Returns the next visible column in display order, Column is an index into the columns list. - -@@TVirtualTreeColumns.GetPreviousColumn@TColumnIndex -Summary -\Returns the previous column in display order, Column is an index into the columns list. - -Description -\Returns the previous column in display order, Column is an index into the columns list. - -@@TVirtualTreeColumns.GetPreviousVisibleColumn@TColumnIndex -Summary -\Returns the previous column in display order, Column is an index into the columns list. - -Description -\Returns the previous column in display order, Column is an index into the columns list. - -@@TVirtualTreeColumns.GetVisibleColumns -Summary -\Returns a list of all currently visible columns in actual order. - -Description -\Returns a list of all currently visible columns in actual order. - -@@TVirtualTreeColumns.HandleClick@TPoint@TMouseButton@Boolean@Boolean -Summary -Generates a click event if the mouse button has been released over the same column it was pressed first. - -Description -Alternatively, Force might be set to true to indicate that the down index does not matter (right, middle and -double click). - -@@TVirtualTreeColumns.IndexChanged@Integer@Integer -Summary -Called by a column when its index in the collection changes. - -Description -If NewIndex is -1 then the column is about to be removed otherwise it is moved to a new index. The method will -then update the position array to reflect the change. - -@@TVirtualTreeColumns.InitializePositionArray -Summary -Ensures that the column position array contains as much entries as columns are defined. - -Description -The array is resized and initialized with default values if needed. - -@@TVirtualTreeColumns.IsValidColumn@TColumnIndex -Summary -Determines whether the given column is valid or not, that is, whether it is one of the current columns. - -Description -Determines whether the given column is valid or not, that is, whether it is one of the current columns. - - -@@TVirtualTreeColumns.UpdatePositions@Boolean -Summary -Recalculates the left border of every column and updates their position property according to the PostionToIndex array, -which primarily determines where each column is placed visually. - -@@TVirtualTreeHintWindow -Summary -Internally used hint window class to support Unicode hints. - -Description -TVirtualTreeHintWindow replaces Delphi's own hint window, but only for the tree controls. For the rest of the application -the hint stays at it is. This means not the global HintWindowClass variable is changed but only the locally used class by -properly responding to CM_HINTSHOW. - -@@TVirtualTreeHintWindow.InternalPaint@Integer@Integer -local functions - -@@TVirtualTreeHintWindow.IsHintMsg@TMsg -The VCL is a bit too generous when telling that an existing hint can be cancelled. Need to specify further here. - -@@TVirtualTreeHintWindow.WMEraseBkgnd@TWMEraseBkgnd -The control is fully painted by own code so don't erase its background as this causes flickering. - -@@TVirtualTreeHintWindow.WMNCPaint@TMessage -The control is fully painted by own code so don't paint any borders. - -@@TVirtualTreeHintWindow.WMShowWindow@TWMShowWindow -Clear hint data when the window becomes hidden. - -@@TVirtualTreeOptions -Summary -Collects all binary options of the tree control into one place for easier access. - -Description -TVirtualTreeOptions does not add any new functionality to TCustomVirtualTreeOptions but is the publicly available class. - - -@@TVSTTextSourceType.tstAll -All nodes are rendered. Initialization is done on the fly. - -@@TVSTTextSourceType.tstCutCopySet -Only nodes currently marked as being in the cut/copy clipboard set are rendered. - -@@TVSTTextSourceType.tstInitialized -Only initialized nodes are rendered. - -@@TVSTTextSourceType.tstSelected -Only selected nodes are rendered. - -@@TVSTTextSourceType.tstVisible -Only visible nodes are rendered. - - -@@TVSTTextType.ttNormal -normal label of the node, this is also the text which can be edited - -@@TVSTTextType.ttStatic -static (non-editable) text after the normal text - -@@TVTAnimationOption.toAnimatedToggle -Expanding and collapsing a node is animated (quick window scroll). - - -@@TVTAutoOption.toAutoChangeScale -Change default node height automatically if the system's font scale is set to big fonts. - -@@TVTAutoOption.toAutoDeleteMovedNodes -Delete nodes which where moved in a drag operation (if not directed otherwise). - -@@TVTAutoOption.toAutoDropExpand -Expand node if it is the drop target for more than certain time. - -@@TVTAutoOption.toAutoExpand -Nodes are expanded (collapsed) when getting (losing) the focus. - -@@TVTAutoOption.toAutoFreeOnCollapse -Frees any child node after a node has been collapsed (HasChildren flag stays there). - -@@TVTAutoOption.toAutoHideButtons -Node buttons are hidden when there are child nodes, but all are invisible. - -@@TVTAutoOption.toAutoScroll -Scroll if mouse is near the border while dragging or selecting. - -@@TVTAutoOption.toAutoScrollOnExpand -Scroll as many child nodes in view as possible after expanding a node. - -@@TVTAutoOption.toAutoSort -Sort tree when Header.SortColumn or Header.SortDirection change or sort node if -child nodes are added. - -@@TVTAutoOption.toAutoSpanColumns -Large entries continue into next column(s) if there's no text in them (no clipping). - -@@TVTAutoOption.toAutoTristateTracking -Checkstates are automatically propagated for tri state check boxes. - -@@TVTAutoOption.toDisableAutoscrollOnFocus -Disable scrolling a column entirely into view if it gets focused. - - - -@@TVTButtonFillMode.fmShaded -color gradient, Windows XP style (legacy code, use toThemeAware on Windows XP instead) - -@@TVTButtonFillMode.fmTransparent -transparent color, use the item's background color - -@@TVTButtonFillMode.fmTreeColor -solid color, uses the tree's background color - -@@TVTButtonFillMode.fmWindowColor -solid color, uses clWindow - - -@@TVTButtonStyle.bsRectangle -traditional Windows look (plus/minus buttons) - -@@TVTButtonStyle.bsTriangle -traditional Macintosh look - -@@TVTColors -Summary -Collects all color related options for the tree control. - -Description -TVTColors makes it much more conventient to adjust Virtual Treeview's colors. Since everything is in one place you can -also easily compare all colors. - - -@@TVTDataObject.FAdviseHolder -Reference to an OLE supplied implementation for advising. - -@@TVTDataObject.FForClipboard -Determines which data to render with GetData. - -@@TVTDataObject.FInternalStgMediumArray -The available formats in the DataObject - -@@TVTDataObject.FOwner -The tree which provides clipboard or drag data. - - - - - - - - -@@TVTDragImage.FColorKey -color to make fully transparent regardless of any other setting - -@@TVTDragImage.FFade -determines whether to fade the drag image from center to borders or not - -@@TVTDragImage.FOwner -the actual drag image to blend to screen - -@@TVTDragImage.FRestriction -determines in which directions the drag image can be moved - -@@TVTDragImage.FStates -Determines the states of the drag image class. - -@@TVTDragImage.FTransparency -alpha value of the drag image (0 - fully transparent, 255 - fully opaque) - - -@@TVTDragImage.GetVisible -True if the drag image is currently hidden (used only when dragging) - -Returns True if the internal drag image is used (i.e. the system does not natively support drag images) and -the internal image is currently visible on screen. - - - - -@@TVTDragImage.RecaptureBackground@TBaseVirtualTree@TRect@HRGN@Boolean@Boolean -Note -The passed rectangle is given in client coordinates of the current drop target tree (given in Tree). The caller does not -check if the given rectangle is actually within the drag image. Hence this method must do all the checks. This method -does nothing if the system manages the drag image. -Summary -Notification by the drop target tree to update the background image because something in the tree has changed. - -@@TVTDragImage.ShowDragImage -Summary -Shows the drag image after it has been hidden by HideDragImage. - -Description -Also this method does nothing if the system manages the drag image. - - - -@@TVTDragImageKind.diComplete -show a complete drag image with all columns, only visible columns are shown - -@@TVTDragImageKind.diMainColumnOnly -show only the main column (the tree column) - -@@TVTDragImageKind.diNoImage -don't show a drag image at all - - -@@TVTDragManager.FDataObject -A reference to the data object passed in by DragEnter (only used when the owner -tree is the current drop target). - -@@TVTDragManager.FDropTargetHelper -Win2k > Drag image support - -@@TVTDragManager.FFullDragging -True, if full dragging is currently enabled in the system. - -@@TVTDragManager.FIsDropTarget -True if the owner is currently the drop target. - - - -@@TVTDrawSelectionMode.smBlendedRectangle -alpha blending, uses special colors (see TVTColors) - -@@TVTDrawSelectionMode.smDottedRectangle -same as DrawFocusRect - - -@@TVTHeader.DetermineSplitterIndex@TPoint -Note -The hit test is checking from right to left to make enlarging of zero-sized columns possible. -Description -\Result is rrue if column border was hit (with -3..+5 pixels tolerance). For continuous resizing the current track index -and the column's left border are set. - -Summary -Tries to find the index of that column whose right border corresponds to P. - - -@@TVTHeader.FDragImage -drag image management during header drag - -@@TVTHeader.FDragStart -initial mouse drag position - -@@TVTHeader.FImageChangeLink -connections to the image list to get notified about changes - -@@TVTHeader.FLeftTrackPos -left border of this column to quickly calculate its width on resize - -@@TVTHeader.FMainColumn -the column which holds the tree - -@@TVTHeader.FStates -used to keep track of internal states the header can enter - -@@TVTHeader.FStyle -button style - -@@TVTHeader.FTrackStart -client coordinates of the tracking start point - - - - - - - - - - - -@@TVTHeaderOption.hoAutoResize -adjust a column so that the header never exceeds client width of owner control - -@@TVTHeaderOption.hoColumnResize -resizing columns is allowed - -@@TVTHeaderOption.hoDblClickResize -allows a column to resize itself to its largest entry - -@@TVTHeaderOption.hoDrag -dragging columns is allowed - -@@TVTHeaderOption.hoHotTrack -header captions are highlighted when mouse is over a particular column - -@@TVTHeaderOption.hoOwnerDraw -header items with the owner draw style can be drawn by the application via event - -@@TVTHeaderOption.hoRestrictDrag -header can only be dragged horizontally - -@@TVTHeaderOption.hoShowHint -show application defined header hint - -@@TVTHeaderOption.hoShowImages -show images - -@@TVTHeaderOption.hoShowSortGlyphs -allow visible sort glyphs - -@@TVTHeaderOption.hoVisible -header is visible - -@@TVTHeaderStyle.hsFlatButtons -flatter look than hsThickButton, like an always raised flat TToolButton - -@@TVTHeaderStyle.hsPlates -flat TToolButton look and feel (raise on hover etc.) - -@@TVTHeaderStyle.hsThickButtons -TButton look and feel - -@@TVTHeaderStyle.hsXPStyle -Windows XP style - - -@@TVTHintData.DefaultHint -used only if there is no node specific hint string available -or a header hint is about to appear - -@@TVTHintData.HintRect -used for draw trees only, string trees get the size from the hint string - -@@TVTHintData.HintText -set when size of the hint window is calculated - -@@TVTHintMode.hmDefault -show the hint of the control - -@@TVTHintMode.hmHint -show node specific hint string returned by the application - -@@TVTHintMode.hmHintAndDefault -same as hmHint but show the control's hint if no node is concerned - -@@TVTHintMode.hmTooltip -show the text of the node if it isn't already fully shown - -@@TVTImageInfo.Ghosted -flag to indicate that the image must be drawn slightly lighter - -@@TVTImageInfo.Index -index in the associated image list - - - -@@TVTIncrementalSearch.isAll -search every node in tree, initialize if necessary - -@@TVTIncrementalSearch.isInitializedOnly -search only initialized nodes, skip others - -@@TVTIncrementalSearch.isNone -disable incremental search - -@@TVTIncrementalSearch.isVisibleOnly -search only visible nodes, initialize if necessary - - -@@TVTInternalPaintOption.poBackground -draw background image if there is any and it is enabled - -@@TVTInternalPaintOption.poColumnColor -erase node's background with the column's color - -@@TVTInternalPaintOption.poDrawDropMark -draw drop mark if a node is currently the drop target - -@@TVTInternalPaintOption.poDrawFocusRect -draw focus rectangle around the focused node - -@@TVTInternalPaintOption.poDrawSelection -draw selected nodes with the normal selection color - -@@TVTInternalPaintOption.poGridLines -draw grid lines if enabled - -@@TVTInternalPaintOption.poMainOnly -draw only the main column - -@@TVTInternalPaintOption.poSelectedOnly -draw only selected nodes - - - -@@TVTLineMode.lmBands -looks similar to a Nassi-Schneidermann diagram - -@@TVTLineMode.lmNormal -usual tree lines (as in TTreeview) - - -@@TVTLineStyle.lsCustomStyle -application provides a line pattern - -@@TVTLineStyle.lsDotted -usual dotted lines (default) - -@@TVTLineStyle.lsSolid -simple solid lines - - -@@TVTLineType.ltBottomRight -a line from bottom to the center and from there to the right - -@@TVTLineType.ltLeft -a line from top to bottom at the left - -@@TVTLineType.ltLeftBottom -a combination of ltLeft and a line at the bottom from left to right - -@@TVTLineType.ltNone -no line at all - -@@TVTLineType.ltRight -a line from center to the right - -@@TVTLineType.ltTopDown -a line from top to bottom - -@@TVTLineType.ltTopDownRight -a line from top to bottom and from center to the right - -@@TVTLineType.ltTopRight -a line from bottom to center and from there to the right -special styles for alternative drawings of tree lines - - -@@TVTMiscOption.toAcceptOLEDrop -Register tree as OLE accepting drop target - -@@TVTMiscOption.toCheckSupport -Show checkboxes/radio buttons. - -@@TVTMiscOption.toEditable -Node captions can be edited. - -@@TVTMiscOption.toFullRepaintOnResize -Fully invalidate the tree when its window is resized (CS_HREDRAW/CS_VREDRAW). - -@@TVTMiscOption.toGridExtensions -Use some special enhancements to simulate and support grid behavior. - -@@TVTMiscOption.toInitOnSave -Initialize nodes when saving a tree to a stream. - -@@TVTMiscOption.toReadOnly -The tree does not allow to be modified in any way. No action is executed and -node editing is not possible. - -@@TVTMiscOption.toReportMode -Tree behaves like TListView in report mode. - -@@TVTMiscOption.toToggleOnDblClick -Toggle node expansion state when it is double clicked. - -@@TVTMiscOption.toWheelPanning -Support for mouse panning (wheel mice only). This option and toMiddleClickSelect are -mutal exclusive, where panning has precedence. - - -@@TVTNodeAlignment.naFromBottom -the align member specifies amount of units (usually pixels) from top border of the node - -@@TVTNodeAlignment.naFromTop -align is to be measured from bottom - -@@TVTNodeAlignment.naProportional -align is to be measure in percent of the entire node height and relative to top - - -@@TVTNodeAttachMode.amAddChildFirst -add node as first child of destination - -@@TVTNodeAttachMode.amAddChildLast -add node as last child of destination - -@@TVTNodeAttachMode.amInsertAfter -insert node just after destionation (as sibling of destination) - -@@TVTNodeAttachMode.amInsertBefore -insert node just before destination (as sibling of destination) - -@@TVTNodeAttachMode.amNoWhere -just for simplified tests, means to ignore the Add/Insert command - - - - - - - - - -@@TVTPaintInfo.Alignment -how to align within the node rectangle - -@@TVTPaintInfo.BidiMode -directionality to be used for painting - -@@TVTPaintInfo.BrushOrigin -the alignment for the brush used to draw dotted lines - -@@TVTPaintInfo.Canvas -the canvas to paint on - -@@TVTPaintInfo.Column -the node's column index to paint - -@@TVTPaintInfo.ImageInfo -info about each possible node image - -@@TVTPaintInfo.Node -the node to paint - -@@TVTPaintInfo.NodeWidth -the actual node width - -@@TVTPaintInfo.PaintOptions -a copy of the paint options passed to PaintTree - -@@TVTPaintInfo.Position -the column position of the node - -@@TVTPaintOption.toAlwaysHideSelection -Do not draw node selection, regardless of focused state. - -@@TVTPaintOption.toFullVertGridLines -Display vertical lines over the full client area, not only the space occupied by nodes. -This option only has an effect if toShowVertGridLines is enabled too. - -@@TVTPaintOption.toGhostedIfUnfocused -Ghosted images are still shown as ghosted if unfocused (otherwise the become non-ghosted -images). - -@@TVTPaintOption.toHideFocusRect -Avoid drawing the dotted rectangle around the currently focused node. - -@@TVTPaintOption.toHideSelection -Selected nodes are drawn as unselected nodes if the tree is unfocused. - -@@TVTPaintOption.toHotTrack -Track which node is under the mouse cursor. - -@@TVTPaintOption.toPopupMode -Paint tree as would it always have the focus (useful for tree combo boxes etc.) - -@@TVTPaintOption.toShowBackground -Use the background image if there's one. - -@@TVTPaintOption.toShowButtons -Display collapse/expand buttons left to a node. - -@@TVTPaintOption.toShowDropmark -Show the dropmark during drag'n drop operations. - -@@TVTPaintOption.toShowHorzGridLines -Display horizontal lines to simulate a grid. - -@@TVTPaintOption.toShowRoot -Show lines also at top level (does not show the hidden/internal root node). - -@@TVTPaintOption.toShowTreeLines -Display tree lines to show hierarchy of nodes. - -@@TVTPaintOption.toShowVertGridLines -Display vertical lines (depending on columns) to simulate a grid. - -@@TVTPaintOption.toThemeAware -Draw UI elements (header, tree buttons etc.) according to the current theme if -enabled (Windows XP+ only, application must be themed). - -@@TVTPaintOption.toUseBlendedImages -Enable alpha blending for ghosted nodes or those which are being cut/copied. - -@@TVTPaintOption.toUseBlendedSelection -Enable alpha blending for node selections. - - - - - - -@@TVTSearchStart.ssAlwaysStartOver -always use the first/last node (depending on direction) to search from - -@@TVTSearchStart.ssFocusedNode -use the currently focused node - -@@TVTSearchStart.ssLastHit -use the last found node - -@@TVTSelectionOption.toCenterScrollIntoView -Center nodes vertically in the client area when scrolling into view. - -@@TVTSelectionOption.toDisableDrawSelection -Prevent user from selecting with the selection rectangle in multiselect mode. - -@@TVTSelectionOption.toExtendedFocus -Entries other than in the main column can be selected, edited etc. - -@@TVTSelectionOption.toFullRowSelect -Hit test as well as selection highlight are not constrained to the text of a node. - -@@TVTSelectionOption.toLevelSelectConstraint -Constrain selection to the same level as the selection anchor. - -@@TVTSelectionOption.toMiddleClickSelect -Allow selection, dragging etc. with the middle mouse button. This and toWheelPanning -are mutual exclusive. - -@@TVTSelectionOption.toMultiSelect -Allow more than one node to be selected. - -@@TVTSelectionOption.toRightClickSelect -Allow selection, dragging etc. with the right mouse button. - -@@TVTSelectionOption.toSiblingSelectConstraint -constrain selection to nodes with same parent - -@@TVTSelectionOption.toSimpleDrawSelection -Simplifies draw selection, so a node's caption does not need to intersect with the -selection rectangle. - - - -@@TVTStringOption.toAutoAcceptEditChange -Automatically accept changes during edit if the user finishes editing other then -VK_RETURN or ESC. If not set then changes are cancelled. - -@@TVTStringOption.toSaveCaptions -If set then the caption is automatically saved with the tree node, regardless of what is -saved in the user data. - -@@TVTStringOption.toShowStaticText -Show static text in a caption which can be differently formatted than the caption -but cannot be edited. - - -@@TVTUpdateState.usBegin -The tree just entered the update state (BeginUpdate call for the first time). - -@@TVTUpdateState.usBeginSynch -The tree just entered the synch update state (BeginSynch call for the first time). - -@@TVTUpdateState.usEnd -The tree just left the update state (EndUpdate called for the last level). - -@@TVTUpdateState.usEndSynch -The tree just left the synch update state (EndSynch called for the last level). - -@@TVTUpdateState.usSynch -Begin/EndSynch has been called but the tree did not change the update state. - -@@TVTUpdateState.usUpdate -Begin/EndUpdate has been called but the tree did not change the update state. - - - - - - -@@VirtualTrees.pas -Version 4.0.0 - - The contents of this file are subject to the Mozilla Public License - Version 1.1 (the "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ - - Alternatively, you may redistribute this library, use and/or modify it under the terms of the - GNU Lesser General Public License as published by the Free Software Foundation; - either version 2.1 of the License, or (at your option) any later version. - You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/. - - Software distributed under the License is distributed on an "AS IS" basis, - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the - specific language governing rights and limitations under the License. - - The original code is VirtualTrees.pas, released September 30, 2000. - - The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de), - written by Dipl. Ing. Mike Lischke (public@lischke-online.de, www.lischke-online.de). - - Portions created by digital publishing AG are Copyright - (C) 1999-2001 digital publishing AG. All Rights Reserved. ----------------------------------------------------------------------------------------------------------------------- - - September 2003 - - Improvement: Edit property of TStringEditLink promoted to public. - - Improvement: ShortenString better takes right-to-left contexts into account. - - Improvement: toAlwaysHideSelection introduced. Allows to hide node selections entirely. - - Improvement: toUseBlendedSelection introduced. Allows to have translucent node selections. - - Bug fix: Mantis bug entries #140, 144, 125, 122, 129. - - Improvement: Mantis feature request #113, toSimpleDrawSelection introduced. - - Improvement: ComputeNodeHeight introduced. Helper method to delegate node height calculation to the tree. - - Improvement: Alt key might be pressed when clicking in the tree. This allows to start drawing the selection - rectangle also on node captions and images (which would otherwise start dragging). - August 2003 - - Bug fix: ValidateCache was not always called in ToggleNode when InvalidateCache was used. - - Bug fix: FLastHintRect was sometimes not reset preventing so a new hint to appear. - - Bug fix: Redundant ChangeCheckState in HandleMouseDown removed. - - Bug fix: OnHeaderDblClick was triggered even if the colum was set to be unclickable. - - Bug fix: Wheel panning and scrolling was not possible if toAutoScroll was not set. This option has another meaning - and should not impact wheel handling. - - Bug fix: VT control could not be set as ActiveControl at design time. - - Bug fix: In method ContentToText it could be that the text contained the separator char as regular character, so - it was necessary to wrap the text with quotation marks then. - - Bug fix: Bidi mode and aligment was not correctly considered in UpdateEditBounds when grid extensions were enabled. - July 2003 - - Improvement: Check for nil hint data in TVirtualTreeHintWindow.CalcHintRect just to be on the safe side. - - Improvedment: TVirtualTreeColumn.ComputeHeaderLayout is now virtual to allow descentants to change the layout. - - Improvement: toFullVertGridLines, vertical grid lines can be drawn over the full client are height. - - Improvement: flickering on column resizing is gone. - - Improvement: System conformal border width calculation for certain tasks. - - Improvement: Animation parameter for TVTHeader.AutoFitColumns to avoid the size animation (default: True). - - Improvement: ParentFont property for the header. Default is False to stay compatible with older tree versions. - - Bug fix: cursor rectangle for spanned columns in normal hint mode was too small. - - Feature: the implementation is now more than 30.000 lines in size. - - Bug fix: Access violation fixed, which was sometimes caused by setting VT to edit mode if the old edit link - was not freed yet (because it was still handling a message). - - Improvement: Hint animation now does no longer stop quick switches to new hints. - - Improvement: ParentBackground property published. - - Bug fix: vsAllChildrenHidden and vsExpanded are now removed from a node's state if there are no child nodes anymore - - Improvement: column width limit to 10000 is now only applied on non-NT systems (Win9x/Me). - - Improvement: single letter mode in incremental search is not used if the current node also fits - the repeated character. - June 2003 - - Bug fix: correct theme change handling when switching to classic mode. - - Improvement: new event OnMeasureItem, new handling for application driven node heights. - TCustomVirtualStringTree.ComputeNodeHeight implementation to easy node height computation for multi line nodes. - - Improvement: Header is nil'ed when the tree is destroyed and checked before used in TBaseVirtualTree.Notification - in order to avoid potential problems accessing an invalid address. - - Bug fix: The cut and copy pending states in the tree and participating nodes were not removed. - - Bug fix: csPaintCopy was not considered when painting (used for TWinControl.PaintTo, e.g. in Form.Print). - - Bug fix: DT_NOPREFIX added for header text output. - May 2003 - - Bug fix: Thread safe check for current tree reference in the worker thread, as it can be reset before it was used. - - Bug fix: Color change for non-standard background colors after all columns were hidden. - - Improvement: new node background erase action (eaNone). - - For full document history see help file. - - Credits for their valuable assistance and code donations go to: - Freddy Ertl, Marian Aldenhövel, Thomas Bogenrieder, Jim Kuenemann, Werner Lehmann, Jens Treichler, - Paul Gallagher (IBO tree), Ondrej Kelle, Ronaldo Melo Ferraz, Heri Bender, Roland Bedürftig (BCB) - Anthony Mills, Alexander Egorushkin (BCB), Mathias Torell (BCB), Frank van den Bergh, Vadim Sedulin, Peter Evans, - Milan Vandrovec (BCB), Steve Moss (system check images), Joe White, David Clark (local node memory manager), - Anders Thomsen, Igor Afanasyev, Eugene Programmer - Beta testers: - Freddy Ertl, Hans-Jürgen Schnorrenberg, Werner Lehmann, Jim Kueneman, Vadim Sedulin, Moritz Franckenstein, - Wim van der Vegt, Franc v/d Westelaken - Indirect contribution (via publicly accessible work of those persons): - Alex Denissov, Hiroyuki Hori (MMXAsm expert) - Documentation: - Markus Spoettl and toolsfactory GbR (http://www.doc-o-matic.com/, sponsoring Virtual Treeview - with a free copy of the Doc-O-Matic help authoring system), Sven H. (Step by step tutorial) - CLX: - Dmitri Dmitrienko (initial developer) - - - - - - - - - - - - - -@@Check button image indices - - -@@ckButtonHot - - -@@ckButtonNormal - - -@@ckButtonPressed - - -@@ckCheckCheckedDisabled - - -@@ckCheckCheckedHot - - -@@ckCheckCheckedNormal - - -@@ckCheckCheckedPressed - - -@@ckCheckMixedHot - - -@@ckCheckMixedNormal - - -@@ckCheckMixedPressed - - -@@ckCheckUncheckedDisabled - - -@@ckCheckUncheckedHot - - -@@ckCheckUncheckedNormal - - -@@ckCheckUncheckedPressed - - -@@ckRadioCheckedHot - - -@@ckRadioCheckedNormal - - -@@ckRadioCheckedPressed - - -@@ckRadioUncheckedDisabled - - -@@ckRadioUncheckedHot - - -@@ckRadioUncheckedNormal - - -@@ckRadioUncheckedPressed - - -@@EVirtualTreeError -Description -EVirtualTreeError is a normal exception derivation especially for Virtual Treeview. This class does not add much value to -its parent class but is rather there to better tell when an exception particularly from Virtual Treeview was raised. - -@@TBufferedString - - -@@TBaseVirtualTree.Alignment -Summary -Determines the horizontal alignment of text if no columns are defined. - -Description -This property is only used if there are no columns defined and applies only to the node captions. Right alignment means -here the right client area border and left aligned means the node buttons/lines etc. (both less the text margin). - -@@TBaseVirtualTree.AnimationDuration -Summary -Determines the maximum duration the tree can use to play an animation. - -Description -The value is specified in milliseconds and per default there are 200 ms as time frame, which is the recommended duration -for such operations. On older systems (particularly Windows 95 and Windows 98) the animation process might not get enough -CPU time to avoid expensive animations to finish properly. Still the animation loop tries to stay as close as possible to -the given time. - -@@TBaseVirtualTree.AutoExpandDelay -Summary -Time delay after which a node gets expanded if it is the current drop target. - -Description -This value is specified in milliseconds and determines when to expand a node if it is the current drop target. This value -is only used if voAutoDropExpand in Options is set. - -@@TBaseVirtualTree.AutoScrollDelay -Summary -Time which determines when auto scrolling should start. - -Description -Once the mouse pointer has been moved near to a border a timer is started using the interval specified by -AutoScrollDelay. When the timer has fired auto scrolling starts provided it is enabled (see also TreeOptions). The value -is specified in milliseconds. - -@@TBaseVirtualTree.AutoScrollInterval -Summary -Time interval between scroll events when doing auto scroll. - -Description -This property determines the speed how the tree is scrolled vertically or horizontally when auto scrolling is in -progress. The value is given in milliseconds. - -@@TBaseVirtualTree.Background -Summary -Holds a background image for the tree. - -Description -Virtual Treeview supports a fixed background image which does not scroll but can be adjusted by BackgroundOffsetX and -BackgroundOffsetY. - -@@TBaseVirtualTree.BackgroundOffsetX -Summary -Horizontal offset of the background image. - -Description -Determines the horizontal offset of the left border of the background image. This value is relative to the target canvas -where the tree is painted to (usually the tree window). - -@@TBaseVirtualTree.BackgroundOffsetY -Summary -Vertical offset of the background image. - -Description -Determines the vertical offset of the top border of the background image. This value is relative to the target canvas -where the tree is painted to (usually the tree window). - -@@TBaseVirtualTree.BorderStyle -Summary -Same as TForm.BorderStyle. - -Description -See TForm.BorderStyle. - -@@TBaseVirtualTree.ButtonFillMode -Summary -Determines how to fill the background of the node buttons. - -Description -This property is used to specify how the interior of the little plus and minus node buttons should be drawn, if -ButtonStyle is bsTriangle. - -@@TBaseVirtualTree.ButtonStyle -Summary -Determines the look of node buttons. - -Description -Determines the look of node buttons. - -@@TBaseVirtualTree.ChangeDelay -Summary -Time which determines when the OnChange event should be triggered after the actual change event. - -Description -In order to accumulate many quick changes in the tree you can use this delay value to specify after which wait time the -OnChange event should occur. A value of 0 means to trigger OnChange immediately after the change (usually a selection or -focus change) happend. Any value \> 0 will start a timer which then triggers OnChange. - - - -\Note that there is the synchronous mode (started by BeginSynch) which effectively circumvents the change delay for the -duration of the synchronous mode (stopped by EndSynch) regardless of the ChangeDelay setting. - -@@TBaseVirtualTree.CheckImageKind -Summary -Determines which images should be used for checkboxes and radio buttons. - -Description -CheckImageKind can be used to switch the image set, which should be used for the tree. Read the description about -TCheckImageKind for a list of all images, which can be used. CheckImageKind can also be set to ckCustom, which allows to -supply a customized set of images to the tree. In order to have that working you must assign an image list -(TCustomImageList) to the CustomCheckImages property. - -@@TCheckState -Summary -\Returns the current state of a node's check box, radio button or node button. - -Description -The check states include both, transient and fluent (temporary) states. The only temporary state defined so far is the -pressed state. - -@@TBaseVirtualTree.CheckState -Summary -Read or set the check state of a node. - -Description -The CheckState property can be used to read the current check state of a node or to set a new one. Virtual Treeview -ensures that invalid check states (e.g. csMixedPressed for radio buttons) do not cause an error. - -@@TBaseVirtualTree.CheckType -Summary -Read or set the check type of a node. - -Description -The CheckType property can be used to read the current check type of a node or to set a new one. Setting a new check type -will reset a the node's check state to csUncheckedNormal. - -@@TBaseVirtualTree.ChildCount -Summary -Read or set the number of child nodes of a node. - -Description -ChildCount can be used to read the current number of child nodes or to change it. Assigning a lower value than there was -before will automatically delete as many child nodes (starting from the last child) as there are more than what was set. -Increasing the value will add new child nodes. Note: code behind this property is very effective, so it using ChildCount -is highly recommended over manipulating the child count using AddChild, InsertNode and DeleteNode. - -@@TBaseVirtualTree.ChildrenInitialized -Summary -Read whether a node's child count has been initialized already. - -Description -This read only property is used to determine whether a node's child count has been set. Alternatively, the child count -value is not considered if vsHasChildren is not in the node states. - -@@TBaseVirtualTree.ClipboardFormats -Summary -Special class to keep a list of clipboard format descriptions. - -Description -This TStringList descendant is used to keep a number of clipboard format descriptions, which are usually used to register -clipboard formats with the system. Using a string list for this task allows to store enabled clipboard formats in the -DFM. - -@@TBaseVirtualTree.Colors -Summary -A collection of colors used in the tree. - -Description -This property holds an instance of the TVTColors class, which is used to customize many of the colors used in a tree. -Placing them all in a specialized class helps organizing the colors in the object inspector and improves general -management. - -@@TBaseVirtualTree.CustomCheckImages -Summary -Assign your own image list to get the check images you like most. - -Description -The CustomCheckImages property is used when custom check images are enabled (see also ckCustom in TCheckImageKind). - -See Also -TCheckImageKind - -@@TBaseVirtualTree.DefaultNodeHeight -Summary -Read or set the height new nodes get as initial value. - -Description -This property allows to read the current initial height for new nodes and to set a new value. Note that changing the -property value does not change the height of existing nodes. Only new nodes are affected. - -@@TBaseVirtualTree.DefaultPasteMode -Summary -Read or set the value, which determines where to add pasted nodes to. - -Description -The default paste mode is an attach mode, which is used when pasting data from the clipboard into the tree. Usually, you -will want new nodes to be added as child nodes to the currently focused node (and this is also the default value), but -you can also specify to add nodes only as siblings. - - - -See Also -TVTNodeAttachMode - -@@TBaseVirtualTree.HintAnimation -Summary -Read or set the current hint animation type. - -Description -With this property you can specify what animation you would like to play when displaying a hint. For some applications it -might not be good to animate hints, hence you can entirely switch them off. Usually however you will leave the system -standard. This way the user can decide whether and which hint animation he or she likes. - -@@TBaseVirtualTree.DragHeight -Summary -Read or set the vertical limit of the internal drag image. - -Description -The DragHeight property (as well as the DragWidth property) are only for compatibility reason in the tree. If a platform -does not support the IDropTargetHelper interface (Windows 9x/Me, Windows NT 4.0) then Virtual Treeview uses its own -implementation of a DragImage. Since displaying a translucent drag image is performance hungry you should limit the image -size shown for the drag operation. - -@@TBaseVirtualTree.DragWidth -Summary -Read or set the horizontal limit of the internal drag image. - -Description -The DragWidth property (as well as the DragHeight property) are only for compatibility reason in the tree. If a platform -does not support the IDropTargetHelper interface (Windows 9x/Me, Windows NT 4.0) then Virtual Treeview uses its own -implementation of a DragImage. Since displaying a translucent drag image is performance hungry you should limit the image -size shown for the drag operation. - -@@TBaseVirtualTree.DragImage -Summary -Holds the instance of the internal drag image. - -Description -For older systems where the IDropTargetHelper interface is not supported Virtual Treeview simulates the translucent drag -image during drag'n drop. The property DragImage makes the internal drag image instance accessible for special handling. -The class itself is always created but is usually not visible when the IDropTargetHelper interface is supported. - -@@TBaseVirtualTree.DragImageKind -Summary -Read or set what should be shown in the drag image. - -Description -DragImageKind allows to switch parts of the drag image off and on. - -@@TBaseVirtualTree.DragManager -Summary -Holds the reference to the internal drag manager. - -Description -The drag manager is the central point for the drag'n drop support in Virtual Treeview. Usually you do not need to access -it but sometimes it might be necessary so the reference is accessible through this property. - - - -See Also -TVTDragManager - -@@TBaseVirtualTree.DragOperations -Summary -Read or set which drag operations may be allowed in the tree. - -Description -Using this property you can determine, which actions may be performed when a drag operation is finished. The default -value includes move, copy and link, where link is rather an esoteric value and only there because it is supported by OLE. -The values used directly determine which image is shown for the drag cursor. The specified drag operations do not tell -which actions will actually be performed but only, which actions are allowed. They still can be modified during drag'n -drop by using a modifier key like the control, shift or alt key or can entirely be ignored by the drop handler. - -@@TBaseVirtualTree.DragSelection -Summary -Keeps a temporary list of nodes during drag'n drop. - -Description -This list is a local copy of the current selection array and is only used during a drag operation. - -@@TBaseVirtualTree.DragType -Summary -Read or set which subsystem should be used for dragging. - -Description -Traditionally, Delphi only supports its own drag mechanism, which is not compatible with the rest of the system. This VCL -dragging also does not support to transport random data nor does it support drag operations between applications. Thus -Virtual Treeview also supports the generally used OLE dragging, which in turn is incompatible with VCL dragging. -Depending on your needs you can enable either VCL or OLE dragging as both together cannot be started. However, Virtual -Treeview is able to act as drop target for both kind of data, independant of what is set in DragType. - -@@TBaseVirtualTree.DrawSelectionMode -Summary -Read or set how multiselection with the mouse is to be visualized. - -Description -Virtuall Treeview allows to display two different selection rectangles when doing multiselection with the mouse. One is -the traditiional dotted focus rectangle and the other one is a translucent color rectangle. The latter is the preferred -\one but the former is set as default (for compatibility reasons). - -@@TBaseVirtualTree.DropTargetNode -Summary -Contains the current drop target node if the tree is currently the target of a drag'n drop operation. - -Description -The drop target node has no meaning except during drag'n drop and only if the tree it belongs to is itself the current -drop target. But even then DropTargetNode might be nil, particularly when the mouse hovers over an area in the tree, -which is not covered by a node. - -@@TBaseVirtualTree.EditDelay -Summary -Read or set the maximum time between two single clicks on the same node, which should start node editing. - -Description -A node edit operation can be started using the keyboard (F2 key), in code using EditNode or by clicking twice on the same -node (but not doing a double click). EditDelay is the maxmimum time distance between both clicks in which the edit -\operation is started. - -See Also - - -@@TBaseVirtualTree.EditLink -Summary -Keeps a reference to the internal edit link during a node edit operation. - -Description -During an edit operation a link is established between the tree and the editor for the current node. By default a simple -TEdit control is used as editor but due to the great customization possibilities there can be any node editor you may -want. In order to communicate with this potentially unknown node editor the edit link is used. The EditLink property -holds this link during the edit operation, so you can manipulate the interface. - -@@TBaseVirtualTree.Expanded -Summary -Read or set the expanded state of a particular node. - -Description -Using this property you can expand or collapse the given node. This method uses the central ToggleNode method. - -@@TBaseVirtualTree.FocusedColumn -Summary -Read or set the currently focused collumn. - -Description -When toExtendedFocus in TVTSelectionOptions is enabled then the user can select node cells in others than the main column -(the column with the tree structure). In order to keep track, which column is currently selected FocusedColumn is used -(similar to FocusedNode). - - - -See Also -FocusedNode, TVTSelectionOptions - -@@TBaseVirtualTree.FocusedNode -Summary -Read or set the currently focused node. - -Description -One node (and only one) in the tree view can have the current input focus, marked as dotted rectangle around the node's -caption. Having the input focus means this node can be edited by pressing F2 or clicking on it and user keyboard input is -interpreted with respect to the focused node (e.g. tree navigation, expansion/collapsing etc.). If extended focus is -enabled then also the FocusedColumn property is taken into account. Read there for more info about column focus. - - - -See Also -FocusedColumn, TVTSelectionOptions - -@@TBaseVirtualTree.Font -Summary -Same as TWinControl.Font. - -Description -See TWinControl.Font. - -@@TBaseVirtualTree.FullyVisible -Summary -Read or set whether a node is fully visible or not. - -Description -Beside the fact that a node can be out of the client area there are two possibilities for it to be hidden. One is the -vsVisible state in TVirtualNodeState, which hides the node regardles of the current state of another node, if not -specified. The other one is that one or more parent nodes might be collapsed, hiding so their entire child nodes -structure. The visibility flag itself can be checked using the IsVisible property, while the expansion state of parents -nodes can be examined via the VisiblePath property. If both are true then the node is said to be fully visible. - - - -See Also -IsVisible, VisiblePath, vsVisible, TVirtualNodeStates - -@@TBaseVirtualTree.HasChildren -Summary -Read or set whether a node has got children. - -Description -A node can be set to have children by assigning true to this property. Internally this will add the vsHasChildren state -to the node but not add any child nodes. This state in turn will cause the node to be drawn with a plus sign in front of -its caption, denoting so it can be expanded and will show child nodes. As long as the child nodes are not touch in any -way (e.g. by expanding the parent node or by navigatin or searching/sorting the tree) there will be no actual child -nodes. They simply do not exist yet. However they will be created as soon as an access is done. - - - -Setting the HasChildren property to false will delete any existing child node. - - - -See Also -vsHasChildren, TVirtualNodeStates - -@@TBaseVirtualTree.Header -Summary -Provides access to the header instance. - -Description -This property is used to allow access to the header instance, which manages all aspects of the tree's header image as -well as the column settings. - - - -See Also -TVTHeader - -@@TBaseVirtualTree.HeaderRect -Summary -\Returns the non-client-area rectangle used for the header. - -Description -Use this property to determine the extents used by the header of Virtual Treeview. - -@@TBaseVirtualTree.HintMode -Summary -Read or set what type of hint you want for the tree view. - -Description -Virtual Treeview supports several hints modes. This includes the normal hint used for any other TControl class as well as -a node specific hint, which is individual for each node or even each cell. - -@@TBaseVirtualTree.HotCursor -Summary -Read or set which cursor should be used for hot nodes. - -Description -When you enable toHotTrack in TreeOptions.PaintOptions then the node, which is currently under the mouse pointer becomes -the hot node. This is a special state, which can be used for certain effects. Hot nodes have by default an underlined -caption and may cause the cursor to change to what ever you like. The HotCursor property is used to specify, which cursor -is to be used. - - - -See Also -HotNode, TVTPaintOptions - -@@TBaseVirtualTree.HotNode -Summary -Read, which node is currently the hot node. - -Description -When you enable toHotTrack in TreeOptions.PaintOptions then the node, which is currently under the mouse pointer becomes -the hot node. The property HotNode can be used to access this node for special handling. - - - -See Also -HotCursor, toHotTrack, TVTPaintOptions - -@@TBaseVirtualTree.Images -Summary -Read or set the tree's normal image list. - -Description -Just like with TListView and TTreeview also Virtual Treeview can take an image list for its normal images. Additionally, -there are image lists for state images and check images. - - - -See Also -StateImages, CheckImages - -@@TBaseVirtualTree.IncrementalSearch -Summary -Read or set the current incremental search mode. - -Description -Virtual Treeview can do an incremental search by calling back the application when comparing node captions. The -IncrementalSearch property determines whether incremental search is enabled and which nodes should be searched through. - - - -See Also -IncrementalSearchDirection, IncrementalSearchStart, IncrementalSearchTimeout - -@@TBaseVirtualTree.IncrementalSearchDirection -Summary -Read or set the direction to be used for incremental search. - -Description -When incremental search is enabled then Virtual Treeview can search forward and backward from the start point given by -IncrementalSearchStart. - - - -See Also -IncrementalSearch, IncrementalSearchStart, IncrementalSearchTime123out - -@@TBaseVirtualTree.IncrementalSearchStart -Summary -Read or set where to start incremental search. - -Description -When incremental search is enabled in the tree view then you can specify here, where to start the next incremental search -\operation from. - - - -See Also -IncrementalSearch, IncrementalSearchDirection, IncrementalSearchTimeout - -@@TBaseVirtualTree.IncrementalSearchTimeout -Summary -Read or set the maximum time, which is allowed between two consecutive key strokes so that incremental search stays -active. - -Description -When incremental search is enabled in Virtual Treeview then you can specify here after what time incremental search -should stop when no keyboard input is encountered any longer. This property so determines also the speed at which users -have to type letters to keep the incremental search rolling. - - - -See Also -IncrementalSearch, IncrementalSearchDirection, IncrementalSearchStart - -@@TBaseVirtualTree.Indent -Summary -Read or set the indentation amount for node levels. - -Description -Each new level in the tree (child nodes of a parent node) are visually shifted to distinguish betwenn them and their -parent node (that's the tree layout after all). The Indent property determines the shift distance in pixels. - -@@TBaseVirtualTree.IsDisabled -Summary -Read or set the enabled state of the given node. - -Description -A node can have many different states. One of them is its enabled state, which can be set via this property. Enabling a -node means it can be focused and selected, so it can take part in clipboard and drag'n drop operations, and can be -edited. - -@@TBaseVirtualTree.IsVisible -Summary -Read or set the visibility state of the given node. - -Description -A node can be made invisible using this property. That means, even if its parent nodes all are expanded the node is not -shown and the visual image is as would the node not exist. However it still can be searched or take part in certain other -\operations. - -@@TBaseVirtualTree.LastDropMode -Summary -Read how the last drop operation finished. - -Description -In the case you don't handle drag'n drop operations directly in OnDragDrop it might be necessary to know how the last -drag operation finshed. Read more in the drag mode enumeration about what is possible. - -@@TBaseVirtualTree.LineMode -Summary -Read or set the mode of the tree lines. - -Description -Apart from the usual lines Virtual Treeview also supports a special draw mode named bands. This allows for neat visual -effects. - -@@TBaseVirtualTree.LineStyle -Summary -Read or set the mode of the tree lines. - -Description -Virtual Treeview allows to customize the lines used to display the node hierarchy. The default style is a dotted pattern, -but you can also make solid lines or specify your own line pattern. - -@@TBaseVirtualTree.Margin -Summary -Read or set the tree's node margin. - -Description -The node margin is the distance between the cell bounds and its content like the lines, images, check box and so on. -However this border is only applied to the left and right side of the node cell. - - - -\Note: there is also a TextMargin property in TVirtualStringTree, which is an additional border for the cell text only. - - - -See Also -TVirtualStringTree.TextMargin - -@@TBaseVirtualTree.MultiLine -Summary -Read or toggle the multiline feature for a given node. - -Description -Since multiline support for nodes requires extra processing this behavior is switchable. When switched on the node is -wrapped into the available space until the node height is exhausted. By including carriage return/line feed pairs you can -explicitely specify where to start new lines. The node's height is not automatically adjusted to the given text. Instead -there is an event (OnMeasureItem), which can be used to compute a node's height before it is displayed the first time. In -addition an application can use the ComputeNodeHeight method to compute the height of the node depending on its caption -text. - -@@TBaseVirtualTree.NodeAlignment -Summary -Read or set the node alignment value. - -Description -Nodes have got an align member, which is used to determine the vertical position of the node's images and tree lines. The -NodeAlignment property specifies how to interpret the value in the align member. - - - -See Also -TVirtualNode - -@@TBaseVirtualTree.NodeDataSize -Summary -Read or set the extra data size for each node. - -Description -A node can have an area for user data, which can be used to store application defined, node specific data in. Use -GetNodeData to get the address of this area. In addition to assigning a value here you can also use the OnGetNodeDataSize -event, which is called when NodeDataSize is -1. - - - -See Also - - -@@TBaseVirtualTree.NodeHeight -Summary -Read or set a node's height. - -Description -Each node can have its individual height, which is stored in the node's record. You could directly assign a value to this -member but I strongly discourage this as it does not update certain other structures in the tree. Instead use the -NodeHeight property here to modify a node's height. - -@@TBaseVirtualTree.NodeParent -Summary -Read or set a node's parent node. - -Description -When reading this property then either the node's real parent node is returned or nil if the parent node is the internal, -hidden root node. When writing to this property you will effectively move a node to a new location. - - - -See Also -MoveTo, CopyTo - -@@TBaseVirtualTree.OffsetX - - -@@TBaseVirtualTree.OffsetXY -Summary -Read or set the tree's current horizontal and vertical scroll offsets. - -Description -Virtual Treeview allows to retrieve or set the internal scroll offset directly, without sending WM_HSCROLL/WM_VSCROLL -message around. This allows also to link two or more trees together. This scroll offset is given in pixels and is always -less or equal 0. - -@@TBaseVirtualTree.OffsetY - - -@@TBaseVirtualTree.OnAdvancedHeaderDraw -Summary -Header paint support event. - -Description -The OnAdvancedHeaderDraw event is used when owner draw is enabled for the header and a column is set to owner draw mode. -It can be used to custom draw only certain parts of the header instead the whole thing. A good example for this event is -customizing the background of the header for only one column. With the standard custom draw method (OnHeaderDraw) you are -in an all-or-nothing situation and have to paint everything in the header including the text, images and sort direction -indicator. OnAdvancedHeaderDraw however uses OnHeaderDrawQueryElements to ask for the elements the application wants to -draw and acts accordingly. - - - -See Also -OnHeaderDrawQueryElements, OnHeaderDraw - -@@TBaseVirtualTree.OnAfterCellPaint -Summary -Paint support event. - -Description -This event is called whenever a cell has been painted. A cell is defined as being one part of a node bound to a certain -column. This event is called several times per node (the amount is determined by visible columns and size of the part to -draw). - - - -See Also - - -@@TBaseVirtualTree.OnAfterItemErase -Summary -Paint support event. - -Description -Called after the background of a node has been erased (erasing can also be filling with a background image). This event -is called once per node in a paint cycle. - -See Also - - -@@TBaseVirtualTree.OnAfterItemPaint -Summary -Paint support event. - -Description -Called after a node has been drawn. This event is called once per node. - -See Also - - -@@TBaseVirtualTree.OnAfterPaint -Summary -Paint support event. - -Description -Called after all nodes which needed an update have been drawn. This event is called once per paint cycle. - -See Also - - -@@TBaseVirtualTree.OnBeforeCellPaint -Summary -Paint support event. - -Description -This event is called immediately before a cell is painted. - -See Also - - -@@TBaseVirtualTree.OnBeforeItemErase -Summary -Paint support event. - -Description -Called when the background of a node is about to be erased. - -See Also - - -@@TBaseVirtualTree.OnBeforeItemPaint -Summary -Paint support event. - -Description -Called after the background of a node has been drawn and just before the node itself is painted. In this event the -application gets the opportunity to decide whether a node should be drawn normally or should be skipped. The application -can draw the node itself if necessary or leave the node area blank. - -See Also - - -@@TBaseVirtualTree.OnBeforePaint -Summary -Paint support event. - -Description -Called as very first event in a paint cycle. In this event has the application the opportunity to do some special -preparation of the canvas onto which the tree is painted, e.g. setting a special viewport and origin or a different -mapping mode. - -See Also - - -@@TBaseVirtualTree.OnChange -Summary -Navigation support event. - -Description -Called when a node's selection state has changed. - -@@TBaseVirtualTree.OnChecked -Summary -Check support event. - -Description -Triggered when a node's check state has changed. - -@@TBaseVirtualTree.OnChecking -Summary -Check support event. - -Description -Triggered when a node's check state is about to change and allows to prevent the change. - -@@TBaseVirtualTree.OnCollapsed -Summary -Miscellaneous event. - -Description -Triggered after a node has been collapsed, that is, its child nodes are no longer displayed. - -@@TBaseVirtualTree.OnCollapsing -Summary -Miscellaneous event. - -Description -Triggered when a node is about to be collapsed and allows to prevent collapsing the node by setting Allowed to -false. - -@@TBaseVirtualTree.OnColumnClick -Summary -Header and column support event. - -Description -Triggered when the user released a mouse button over the same column in the client area on which the button was pressed -previously. - -See Also -OnHeaderClick - -@@TBaseVirtualTree.OnColumnDblClick -Summary -Header and column support event. - -Description -Same as OnColumnClick but for double clicks. - - - -See Also -OnColumnClick, OnHeaderDblClick - -@@TBaseVirtualTree.OnColumnResize -Summary -Header and column support routine. - -Description -Triggered when a column is being resized. During resize OnColumnResize is frequently hence you should make any code in -the associated event handle a short and fast as possible. - -@@TBaseVirtualTree.OnCompareNodes -Summary -Sort and search support event. - -Description -This event is the core event for all comparations between nodes. It is important that you write a handler for this -event if you want to sort nodes! - - - -Result must be set to less than 0 if Node1 is considered as being before Node2, equal to 0 if both a -considered being the same and greater than 0 if the first node is considered as being after node 2. Keep in mind that you -don't need to take sort direction into account. This is automatically handled by the tree. Simply return a comparation -\result as would there be an ascending sort order. - - - -Below is some sample code taken from the Advanced Demo: - - - -procedure TMainForm.VDT1CompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: Integer; - var Result: Integer); - -// used to sort the image draw tree - -var - Data1, - Data2: PImageData; - -begin - Data1 := Sender.GetNodeData(Node1); - Data2 := Sender.GetNodeData(Node2); - // folder are always before files - if Data1.IsFolder \<\> Data2.IsFolder then - begin - // one of both is a folder the other a file - if Data1.IsFolder then - \Result := -1 - else - \Result := 1; - end - else // both are of same type (folder or file) - \Result := CompareText(Data1.FullPath, Data2.FullPath); -end; - - -See Also -SortTree, Sort - -@@TBaseVirtualTree.OnCreateDataObject -Summary -Drag'n drop support event. - -Description -This event is called when the tree's drag manager needs a data object interface to start a drag'n drop operation. -Descentants (which override DoGetDataObject) or the application can return an own IDataObject implementation to support -special formats. - -@@TBaseVirtualTree.OnCreateDragManager -Summary -Drag'n drop support event. - -Description -This event is usually not used but allows power users to create their own drag manager to have different actions and/or -formats than the internal drag manager. - -@@TBaseVirtualTree.OnCreateEditor -Summary -Editing support event. - -Description -Allows to supply a customized node editor without changing the tree. TBaseVirtualTree triggers this event and raises an -exception if there no editor is returned. If you don't want this then disable edit support for nodes in -TreeOptions.MiscOptions. Descentants like TCustomVirtualStringTree supply a generic and simple string editor. - -See Also - - -@@TBaseVirtualTree.OnDragAllowed -Summary -Drag'n drop support event. - -Description -This event is called in the mouse button down handler to determine whether the application allows to start a drag -\operation. Since this check is done in sync with the other code it is much prefered over doing a manual -BeginDrag. - -Note -The OnDragAllowed event is called only if the current DragMode is dmManual. - -@@TBaseVirtualTree.OnDragDrop -Summary -Drag'n drop support event. - -Description -Triggered when either a VCL or a OLE drop action occured. Accepting drag and drop actions is not trivial. In order to -maintain a minimum compatibility with the VCL drag'n drop system Virtual Tree accepts not only OLE drop actions but also -those issued by the Delphi VCL (which is totally different to the OLE way, unfortunately), provided toAcceptOLEDrop is -set in TreeOptions.MiscOptions. The code snippet below is taken from a sample project provided with Virtual Tree. It -shows a general way to deal with dropped data. The following check list can be used as orientation and additional comment -to the code: - - - - 1. Determine what kind of drop data is passed. If DataObject is nil or Formats is empty then the drag - source is a VCL control. The event is not triggered for OLE drag'n drop if there is no OLE format is available (which - should never occur). - 2. If the event is triggered by a VCL control then use Source to access either the control or the drag - \object, depending on the circumstances of the action. - 3. For OLE drag'n drop iterate through the Formats list to find a format you can handle. - 4. If you find CF_VIRTUALTREE then the source of the drag operation is a Virtual Treeview. Since this is the native - tree format you can pass it to the Sender's ProcessDrop method which will take care to retrieve the data and act - depending on Effect and Mode. No further action by the application is usually required in this case. - 5. If you do not find CF_VIRTUALTREE then the operation has been initiated by another application, e.g. the - Explorer (then you will find CF_HDROP or CF_SHELLIDLIST in formats) or Notepad (then you will get CF_TEXT and perhaps - CF_UNICODETEXT) etc., depending on the data which is actually dropped. - 6. Use the provided DataObject to get the drop data via IDataObject.GetData and act depending on the format - you get. - 7. Finally set Effect to either DROPEFFECT_COPY, DROPEFFECT_MOVE or DROPEFFECT_NONE to indicate which - \operation needs to be finished in Sender when the event returns. If you return DROPEFFECT_MOVE then all marked - nodes in the source tree will be deleted, otherwise they stay where they are. - - - -procedure TMainForm.VTDragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject; - const Formats: array of Word; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode); - -var - I: Integer; - AttachMode: TVTNodeAttachMode; - -begin - if Length(Formats) \> 0 then - begin - // OLE drag'n drop - // If the native tree format is listed then use this and accept the drop, otherwise recject (ignore) it. - // It is recommend by Microsoft to order available clipboard formats in decreasing detail richness so - // the first best format which we can accept is usually the best format we can get at all. - for I := 0 to High(Formats) do - if Formats[I] = CF_VIRTUALTREE then - begin - case Mode of - dmAbove: - AttachMode := amInsertBefore; - dmOnNode: - AttachMode := amAddChildLast; - dmBelow: - AttachMode := amInsertAfter; - else - if Assigned(Source) and (Source is TBaseVirtualTree) and (Sender \<\> Source) then - AttachMode := amInsertBefore - else - AttachMode := amNowhere; - end; - // in the case the drop target does an optimized move Effect is set to DROPEFFECT_NONE - // to indicate this also to the drag source (so the source doesn't need to take any further action) - Sender.ProcessDrop(DataObject, Sender.DropTargetNode, Effect, AttachMode); - Break; - end; - end - else - begin - // VCL drag'n drop, Effects contains by default both move and copy effect suggestion, - // as usual the application has to find out what operation is finally to do - Beep; - end; -end; - - - -@@TBaseVirtualTree.OnDragOver -Summary -Drag'n drop support event. - -Description -Triggered when Sender is the potential target of a drag'n drop operation. You can use this event to allow or deny a drop -\operation by setting Allowed to True or False, respectively. For conditions of OLE or VCL drag source see OnDragDrop. - -See Also -OnDragDrop - -@@TBaseVirtualTree.OnEditCancelled -Summary -Editing support event. - -Description -Triggered when an edit action has been cancelled. - -See Also - - -@@TBaseVirtualTree.OnEdited -Summary -Editing support event. - -Description -Triggered when an edit action has successfully been finished. - -See Also - - -@@TBaseVirtualTree.OnEditing -Summary -Editing support event. - -Description -Triggered when a node is about to be edited. Use Allowed to allow or deny this action. - -See Also - - -@@TBaseVirtualTree.OnExpanded -Summary -Misscellaneous event. - -Description -Triggered after a node has been expanded. - -@@TBaseVirtualTree.OnExpanding -Summary -Miscellaneous event. - -Description -Triggered just before a node is expanded. Use Allowed to allow or deny this action. - -@@TBaseVirtualTree.OnFocusChanged -Summary -Navigation support event. - -Description -Triggered after the focused node changed. When examining Node keep in mind that it can be nil, meaning there is no -focused node. - -@@TBaseVirtualTree.OnFocusChanging -Summary -Navigation support event. - -Description -Triggered when the node focus is about to change. You can use Allowed to allow or deny a focus change. Keep in -mind that either the old or the new node can be nil. - -@@TBaseVirtualTree.OnFreeNode -Summary -Data management node. - -Description -Triggered when a node is about to be freed. This is the ideal place to free/disconnect your own data you associated with Node. -Keep in mind, that data which is stored directly in the node does not need to be free by the application. This is part of -the node record and will be freed when the node is freed. You should however finalize the data in such a case if it -contains references to external memory objects (e.g. variants, strings, interfaces). - -@@TBaseVirtualTree.OnGetCursor -Summary -Miscellaneous event. - -Description -This event is triggered from the WM_SETCURSOR message to allow the application use several individual cursors for a tree. -The Cursor property allows to set one cursor for the whole control but not to use separate cursors for different tree -parts. - -@@TBaseVirtualTree.OnGetHeaderCursor -Summary -Header and column support event. - -Description -This event is triggered from the WM_SETCURSOR message to allow the application to define individual cursors for the -header part of the tree control. - -@@TBaseVirtualTree.OnGetHelpContext -Summary -Miscellaneous event. - -Description -This event is usually triggered when the user pressed F1 while the tree has the focus. The tree is iteratively traversed -all the way up to the top level parent of the given node until a valid help context index is returned (via this event). -When the loop reaches the top level without getting a help index then the tree control's help index is used. If the tree -itself does not have a help context index then a further traversal is initiated going up parent by parent of each control -in the current window hierarchy until either a valid index is found or there is no more window parent. - -@@TBaseVirtualTree.OnGetImageIndex -Summary -Display management event. - -Description -This event is triggered whenever the tree needs the index of an image, be it the normal, the selected or the state image. -The event should be as fast as possible because it is at times frequently called when the layout of the node must be -determined, e.g. while doing draw selection with the mouse or painting the tree. Kind determines which image is -needed and Column determines for which column of the node the image is needed. This value can be -1 to indicate -there is no column used. The parameter Ghosted can be set to true to blend the image 50% against the tree -background and can be used for instance in explorer trees to mark hidden file system objects. Additionally nodes are also -drawn with a ghosted icon if the are part of a cut set during a pending cut-to-clipboard operation. In this case changing -the ghosted parameter has no effect. - -Note -Blending nodes can be switched by using toUseBlendImages in TreeOptions.PaintOptions. - -@@TBaseVirtualTree.OnGetLineStyle -Summary -Display management event. - -Description -This event is used to customize the appearance of the tree and grid lines and is only triggered if the LineStyle property -is set to lsCustomStyle. The event must return a pointer to an array containing bits for an 8 x 8 pixel image with word -aligned entries. For more info see PrepareBitmaps and the Windows APIs CreateBitmap and CreatePatternBrush. - -Note -It is important that you do not use dynamically allocated memory in this event (also no local variables on the stack). If -you do so then either the memory is not valid on return of the event (if allocated on stack) or will never be freed (if -allocated with a memory manager). Instead use a constant array and return its address. -See Also -PrepareBitmaps - -@@TBaseVirtualTree.OnGetNodeDataSize -Summary -Data management event. - -Description -Triggered when access to a node's data happens the first time but the actual data size is not yet set. Usually you would -specify the size of the data you want to have added to each node by NodeDataSize, e.g. SizeOf(TMyRecord) is quite usual -there (where TMyRecord is the structure you want to have stored in the node). Sometimes, however it is not possible to -determine the node size in advance, so you can leave NodeDataSize being -1 (the default value) and the OnGetNodeDataSize -event is triggered as soon as the first regular node is created (the hidden root node does not have user data but -\internal data which is determined by other means). - -See Also -NodeDataSize, - -@@TBaseVirtualTree.OnGetPopupMenu -Summary -Miscellaneous event. - -Description -This event allows the application to return a popup menu which is specific to a certain node. The tree does an automatic -traversal all the way up to the top level node which is the parent of a given node to get a popup menu. If Menu is -set then the traversal stops. Otherwise it continues until either a menu is set, AskParent is set to False or the top -level parent has been reached. - -@@TBaseVirtualTree.OnGetUserClipboardFormats -Summary -Drag'n drop and clipboard support event. - -Description -Whenever the tree needs to specify the available clipboard formats for a clipboard or drag'n drop operation it calls this -event too, to allow the application or descentants (which would override DoGetUserClipboardFormats) to specify own -formats which can be rendered. Since the build-in data object does not know how to render formats which are specified -here you have to supply a handler for the OnRenderOLEData event or an own IDataObject implementation to fully support -your own formats. - - - -Use the Formats parameter which is an open array and add the identifiers of your formats (which you got when you -registered the format). - -@@TBaseVirtualTree.OnHeaderClick -Summary -Header & column support event. - -Description -This event is triggered when the user clicks on a header button and is usually a good place to set the current SortColumn -and SortDirection. - -See Also -SortColumn, SortDirection - -@@TBaseVirtualTree.OnHeaderDblClick -Summary -Header & column support event. - -Description -Unlike OnHeaderClick this event is triggered for double clicks on any part of the header and comes with more detailed -information like shift state, which mouse button caused the event and the mouse position. - -See Also -OnHeaderClick - -@@TBaseVirtualTree.OnHeaderDragged -Summary -Header & column support event. - -Description -Triggered after the user has released the left mouse button when a header drag operation was active. Column -contains the index of the column which was dragged. Use this index for the Columns property of the header to find out the -current position. OldPosition is the position which Column occupied before it was dragged around. - -@@TBaseVirtualTree.OnHeaderDraggedOut -Summary -Header & column support event. - -Description -When during a header drag operation the mouse moves out of the header rectangle and the mouse button is released then an -OnHeaderDraggedOut event will be fired with the target mouse position in screen coordinates. - -@@TBaseVirtualTree.OnHeaderDragging -Summary -Header & column support event. - -Description -Triggered just before dragging of a header button starts. Set Allowed to False if you want to prevent the drag -\operation of the given column. - -@@TBaseVirtualTree.OnHeaderDraw -Summary -Header & column support event. - -Description -If you set the hoOwnerDraw style in TVTHeader.Options and a column has been set to vsOwnerDraw (see also -TVirtualTreeColumn.Style) then OnDrawHeader is called whenever a column needs painting. - -@@TBaseVirtualTree.OnHeaderDrawQueryElements -Summary -Header & column support event. - -Description -Used for advanced header painting to query the application for the elements, which are drawn by it and which should be -drawn by the tree. - - - -See Also -OnAdvancedHeaderDraw - -@@TBaseVirtualTree.OnHeaderMouseDown -Summary -Header & column support event. - -Description -This event is similar to OnHeaderClick but comes with more detailed information like shift state, which mouse button -caused the event and the mouse position. - -@@TBaseVirtualTree.OnHeaderMouseMove -Summary -Header & column support event. - -Description -This event is triggered when the mouse pointer is moved over the header area. - -@@TBaseVirtualTree.OnHeaderMouseUp -Summary -Header & column support event. - -Description -This event is very much like OnHeaderMouseDown but is triggered when a mouse button is released. - -@@TBaseVirtualTree.OnHotChange -Summary -Navigation support event. - -Description -This event is triggered if hot tracking is enabled (see also TreeOptions.PaintOptions) and when the mouse pointer moves -from one node caption to another. In full row select mode most parts of a node are considered as being part of the -caption. - -@@TBaseVirtualTree.OnIncrementalSearch -Summary -Miscellaneous event. - -Description -This event is integral part of the incremental search functionality (see also Keyboard, hotkeys and incremental search). -It is triggered during search for a node which matches the given string. Similar to other compare routines return a value -\< 0 if the node's caption is considered as being before the given text, = 0 if it is the same and \> 0 if it is -considered being after the given text. - - - -procedure TfrmProperties.VST3IncrementalSearch(Sender: TBaseVirtualTree; Node: PVirtualNode; const Text: WideString; - var Result: Integer); - -var - S, PropText: string; - - -begin - // Note: This code requires a proper Unicode/WideString comparison routine which I did not want to link here for - // size and clarity reasons. For now strings are (implicitly) converted to ANSI to make the comparison work. - // Search is not case sensitive. - S := Text; - if Node.Parent = Sender.RootNode then - begin - // root nodes - if Node.Index = 0 then - PropText := 'Description' - else - PropText := 'Origin'; - end - else - begin - PropText := PropertyTexts[Node.Parent.Index, Node.Index, ptkText]; - end; - - // By using StrLIComp we can specify a maximum length to compare. This allows us to find also nodes - // which match only partially. - \Result := StrLIComp(PChar(S), PChar(PropText), Min(Length(S), Length(PropText))) -end; - -Note -Usually incremental search allows to match also partially. Hence it is recommended to do comparison only up to the length -\of the shorter string. - -@@TBaseVirtualTree.OnInitChildren -Summary -Node management event. - -Description -In order to allow the tree only to fill content where needed it is possible to set the vsHasChildren style in a node's -initializaton whithout really adding any child nodes. These child nodes must be initialized first when they are about to -be displayed or another access (like search, iteration etc.) occurs. - - - -The application usually prepares data needed to fill child nodes when they are initialized and retrieves the actual -number. Set ChildCount to the number of children you want. - -See Also - - -@@TBaseVirtualTree.OnInitNode -Summary -Node management event. - -Description -This event is important to connect the tree to your internal data. It is the ideal place to put references or whatever -you need into a node's data area. You can set some initial states like selection, expansion state or that a node has -child nodes. - -See Also - - -@@TBaseVirtualTree.OnKeyAction -Summary -Miscellaneous event. - -Description -This event is a convinient way for the application or descentant trees to change the semantic of a certain key stroke. It -is triggered when the user presses a key and allows either to process that key normally (leave DoDefault being -True) or change it to another key instead (set DoDefault to False then). This way a key press can change its -meaning or entirely be ignored (if CharCode is set to 0). - -@@TBaseVirtualTree.OnLoadNode -Summary -Streaming support event. - -Description -This event is typically triggered when serialized tree data must be restored, e.g. when loading the tree from file or -stream or during a clipboard/drag'n drop operation. You should only read in what you wrote out in OnSaveNode. For safety -there is a check in the loader code which tries to keep the internal serialization structure intact in case the -application does not read correctly. - -See Also -OnSaveNode, LoadFromStream, SaveToStream, AddFromStream, VTTreeStreamVersion, TVTHeader.LoadFromStream, -TVTHeader.SaveToStream - -@@TBaseVirtualTree.OnMeasureItem -Summary -Miscellaneous event. - -Description -Virtual Treeview supports individual node heights. However it might sometimes unpractical to set this height in advance -(e.g. during OnInitNode). Another scenario might be that multi line nodes must size themselves to accomodate the entire -node text without clipping. For such and similar cases the event OnMeasureItem is for. It is queried once for each node -and allows to specify the node's future height. If you later want to have a new height applied (e.g. because the node's -text changed) then call InvalidateNode for it and its vsHeightMeasured state is reset causing so the tree to trigger the -OnMeasureItem event again when the node is painted the next time. - - - -See Also -InvalidateNode, vsHeightMeasured - -@@TBaseVirtualTree.OnNodeCopied -Summary -Miscellaneous event. - -Description -This event is triggered during drag'n drop after a node has been copied to a new location. Sender is the target tree -where the copy operation took place. - -@@TBaseVirtualTree.OnNodeCopying -Summary -Miscellaneous event. - -Description -This event is triggered when a node is about to be copied to a new location. Use Allowed to allow or deny the -action. Sender is the target tree where the copy operation will take place. - -@@TBaseVirtualTree.OnNodeMoved -Summary -Miscellaneous event. - -Description -This event is very much like OnNodeCopied but used for moving nodes instead. - -@@TBaseVirtualTree.OnNodeMoving -Summary -Miscellaneous event. - -Description -This event is very much like OnNodeCopying but used for moving nodes instead. - -@@TBaseVirtualTree.OnPaintBackground -Summary -Paint support event. - -Description -This event is triggered when the tree has finished its painting and there is an area which is not covered by nodes. For -nodes there are various events to allow background customizaton. For the free area in the tree window there is this -event. - -@@TBaseVirtualTree.OnRenderOLEData -Summary -Drag'n drop and clipboard support event. - -Description -This event is triggered when the data in a clipboard or drag'n drop operation must be rendered but the built-in data -\object does not know the requested format. This is usually the case when the application (or descentants) have specified -their own formats in OnGetUserClipboardFormats. - -@@TBaseVirtualTree.OnResetNode -Summary -Node management event. - -Description -For large trees or simply because the content changed it is sometimes necessary to discard a certain node and release all -its children. This can be done with ResetNode which will trigger this event. - -See Also -ResetNode - -@@TBaseVirtualTree.OnSaveNode -Summary -Streaming support event. - -Description -This event is triggered whenever a certain node must be serialized into a stream, e.g. for saving to file or for copying -to another tree/node during a clipboard or drag'n drop operation. Make sure you only store non-transient data into the -stream. Pointers (including long/wide string references) are transient and the application cannot assume to find the data -a pointer references on saving at the same place when the node is loaded (see also OnLoadNode). This is even more -essential for nodes which are moved or copied between different trees in different processes (applications). Storing -strings however is easily done by writing the strings as a whole into the stream. - -Note -For exchanging data between different trees and for general stability improvement I strongly recommend that you insert a -kind of identifier as first stream entry when saving a node. This identifier can then be used to determine what data will -follow when loading the node later and does normally not required to be stored in the node data. -See Also -OnLoadNode, LoadFromStream, SaveToStream, AddFromStream, VTTreeStreamVersion, TVTHeader.LoadFromStream, -TVTHeader.SaveToStream - -@@TBaseVirtualTree.OnScroll -Summary -Miscellaneous event. - -Description -This event is triggered when the tree is scrolled horizontally or vertically. You can use it to synchronize scrolling of -several trees or other controls. - -See Also -OffsetXY - -@@TBaseVirtualTree.OnStateChange -Summary -Miscellaneous event. - -Description -For special effects or in order to increase performance it is sometimes useful to know when the tree changes one of its -\internal states like tsIncrementalSearching or tsOLEDragging. The OnStateChange event is triggered each time such a -change occurs letting so the application take measures for it. - -@@TBaseVirtualTree.OnStructureChange -Summary -Miscellaneous event. - -Description -This event is triggered when a change in the tree structure is made. That means whenever a node is created or destroyed -\or a node's child list is change (because a child node was moved, copied etc.) then OnStructureChange is executed. - -@@TBaseVirtualTree.OnUpdating -Summary -Miscellaneous event. - -Description -This event is triggered when the application or the tree call BeginUpdate or EndUpdate and indicate so when a larger -update operation takes place. This can for instance be used to show a hour glass wait cursor. - -@@TBaseVirtualTree.RootNode -Summary -Reference to the internal root node which is the anchor of the entire tree node hierarchy. - -Description -For anchoring the tree hierarchy an internal tree node is maintained which is mostly just like any other tree node but -has sometimes differently handled. The root node is always expanded and initialized. Its parent member points to the -treeview to which the node belongs to and its PreviousSibling and NextSibling members point to the root node itself to -make it possible to actually recognize this node. - -Note -You should not use the root node to iterate through the tree. It is only publicly accessible because it is the parent of -all top level nodes and can be used to test a node whether it is a top level node or not. - -@@TBaseVirtualTree.RootNodeCount -Summary -Read or set the number of nodes on the top level. - -Description -Usually setting RootNodeCount is all what is needed to initially fill the tree. When one of the top level nodes is -initialized you can set its ivsHasChildren style. This will then cause to ask to initialize the child nodes. Recursively -applied, you can use this principle to create tree nodes on demand (e.g. when their parent is expanded). - -@@TBaseVirtualTree.ScrollBarOptions -Summary -Reference to the scroll bar options class. - -Description -Like many other aspects in Virtual Treeview also scrollbars can be customized. See the class itself for further -descriptions. - -@@TBaseVirtualTree.SearchBuffer -Summary -Current input string for incremental search. - -Description -When incremental search is active you can use SearchBuffer to get the input string typed by the user, which created the -last match. - -See Also -IncrementalSearch - -@@TBaseVirtualTree.Selected -Summary -Property to modify or determine the selection state of a node. - -Description -This array property is used to test whether a given node is selected or to switch its selection state. Note that the -selection state has nothing to do with the . Only one node can be -focused while any number of nodes can be selected (read: can be marked with the selection flag to paint their caption -differently). Selection is mainly used to mark nodes for clipboard and drag'n drop operations. - -@@TBaseVirtualTree.SelectedCount -Summary -Contains the number of selected nodes. - -Description -If multiselection is enabled (toMultiSelect) then SelectedCount will contain the actual number of selected nodes. In -\order to change the selection state of a node use Selected or AddToSelection/RemoveFromSelection. - -@@TBaseVirtualTree.SelectionBlendFactor -Summary -Read or set the current blend factor for the multi selection rectangle and the node selection rectangle. - -Description -For a visually appealing tree some operations use alpha blending. One of these operations is multi selection using the -mouse. Another one is the rectangle drawn around the caption of selected nodes. Both rectangles use the -SelectionBlendFactor to determine how much of the underlying tree image and how much of the rectangles should be seen. -The factor can be in the range of [0..255] where 0 means the rectangle is fully transparent and 255 it is fully opaque. - - - -If you don't like to use blended node selection rectangles then switch them off by removing toUseBlendedSelection from -TVTPaintOptions. For selecting a certain multi selection rectangle style use DrawSelectionMode. - - - -Note -Alpha blending is only enabled when the current processor supports MMX instructions. If MMX is not supported then a -dotted draw selection rectangle and an opaque node selection rectangle is used. -See Also -DrawSelectionMode, TVTPaintOptions - -@@TBaseVirtualTree.SelectionCurveRadius -Summary -Read or set the current corner radius for node selection rectangles. - -Description -This is a special property to determine the radius of the corners of the selection rectangle for a node caption. Virtual -Treeview supports not only simple rectangular selection marks but also such with rounded corners. This feature, however, -is only available if blended node selection rectangles are disabled. - -See Also -SelectionBlendFactor, DrawSelectionMode, TVTPaintOptions - -@@TBaseVirtualTree.StateImages -Summary -Reference to the images list which is used for the state images. - -Description -Each node can (in each column) have several images. One is the check image which is supplied by internal image lists or a -special external list (see also CustomCheckImages). Another one is the state image and yet another one the -normal/selected image. - - - -See Also -CheckImages, Images - -@@TBaseVirtualTree.TextMargin -Summary -Read or set the distance of the node caption to its borders. - -Description -TextMargin is used to define a border like area within the content rectangle of a node. This rectangle is the area of the -node less the space used for indentation, images, lines and node margins and usually contains the text of a node. In -\order to support finer adjustment there is another margin, which only applies to the left and right border in the -content rectangle. This is the text margin. - - - -See Also -Margin - -@@TBaseVirtualTree.TopNode -Summary -The top node is the node which is currently at the top border of the client area. - -Description -This property is a reference to the node which is the first node which is at least partially visible in the client area. - -@@TBaseVirtualTree.TotalCount -Summary -\Returns the number of nodes in the tree. - -Description -Use this property to get the overall number of nodes currently in the tree. This will validate all nodes in the control -so that also not yet created child nodes are counted. - - - -Note -This property is quite counter productive as it causes the entire tree to be validated when queried. This means that each -node is initialized, including its children and grandchildren etc. creating so a full blown treeview (if not already -done) which might keep much memory allocated (not counted the time necessary to validate all nodes). Therefore I -discourage the use of the property unless it is really necessary. - -@@TBaseVirtualTree.TotalInternalDataSize -Summary -Keeps the currently accumulated data size for one node. - -Description -Each node in the tree not only supports user data but also an interal area where TVirtualBaseTree descentants can store -their own data per node. This internal data area must be allocated by a tree class, that means it must register its need -for internal data. The internal data size registered by each descendant is accumulated in the TotalInternalDataSize -member and is used to compute the user data offset in the node record. - - - -See Also - - -@@TBaseVirtualTree.TreeOptions -Summary -Reference to the tree's options. - -Description -The tree options are one of the main switchs to modify a treeview's behavior. Virtual Treeview supports customizing tree -\options by descentants. This allows very fine adjustments for derived tree classes, including the decision which -properties should be published. For more information about the base options see TCustomVirtualTreeOptions and its -descentants. - -@@TBaseVirtualTree.TreeStates -Summary -Property which keeps a set of flags which indicate current operation and states of the tree. - -Description -Often it is extremly helpful to know what action is currently happening in the tree. TreeStates gives you this -information, be it that the caches are currently validated, a drag operation is in progress, the tree has delayed data on -the clipboard or a large update operation is under work. You can greatly optimize your code with this knowledge. - - - -See Also -OnStateChange - -@@TBaseVirtualTree.VerticalAlignment -Summary -Used to set a node's vertical button aligment with regard to the entire node rectangle. - -Description -The given value is interpreted differently depending on the value of NodeAlignment. By default the alignment used -relatively with regard to the top bound. In this case a range of 0 through 100 must be used which denotes the relative -pixel amount in percent. The other variants work with absolute pixel values from top or bottom bound. - -@@TBaseVirtualTree.VisibleCount -Summary -Number of currently visible nodes. - -Description -Visible nodes are those nodes which have the vsVisible flag set in their states. - -@@TBaseVirtualTree.VisiblePath -Summary -Property to set or determine a node parent's expand states. - -Description -A node has a visible path when all of its parent nodes are expanded. Setting this property to True will expand all parent -nodes of Node if not yet done. - - - -See Also -Visible - -@@TBaseVirtualTree.WantTabs -Summary -Read or set whether the tree wants to process tabs on its own. - -Description -Usually tab kex strokes advance the input focus from one control to another on a form. For special processing however it -is necessary to let the control decide what to do with the given tabulator character. Virtual Treeview needs this -character mainly for its grid emulation. - -@@TBaseVirtualTree.AbsoluteIndex@PVirtualNode -Summary -Reads the overall index of a node. - -Description -Indicates the index of the tree node relative to the first tree node in a tree. - - - -Note -Similar to TotalCount also with AbsoluteIndex the entire tree will be validated, with all consequences like high memory -usage etc. And since Virtual Treeview is a highly changing environment there is not much sense to use the absolute index. -You cannot use it in any method or property of the control. - -@@TBaseVirtualTree.AddChild@PVirtualNode@Pointer -Summary -Creates and adds a new child node to given node. - -Description -The new node will be created as last child of Parent and is returned as result. - -Note -Using AddChild is not recommended. The method is merely there for easier migration from TTreeview. The reason is that the -method has to validate the node and does some other processing, which prevents the tree from utilizings its virtual -paradigm. Important advantages will so disappear. If possible you should restructure your design and try to use the right -way: via OnInitNode and OnInitChildren. - -See Also -InsertNode, OnInitNode, OnInitChildren - -@@TBaseVirtualTree.AddFromStream@TStream@PVirtualNode -Summary -Adds the content from the given stream to the given node. - -Description -AddFromStream restores the subtree stored in Stream and adds it to TargetNode. The content of the stream must have been -saved previously with SaveToStream. - - - -See Also -SaveToStream - -@@TBaseVirtualTree.AddToSelection@PVirtualNode -Summary -Adds one or more nodes to the current selection. - -Description -AddToSelection either takes a single node or an array of nodes and adds them to the current seletion in the tree. In this -process also the vsSelected state of the node is set. NewLength is the amount of nodes to add (necessary to allow NewItems -to be larger than the actual used entries). ForceInsert is true if nodes must be inserted without consideration of -level select constraint or already set selected flags (e.g. when loading from stream). - -Note -In the case ForceInsert is true the caller is responsible for making sure the new nodes aren't already in the -selection array! - -@@TBaseVirtualTree.AddToSelection@TNodeArray@Integer@Boolean - - -@@TBaseVirtualTree.AdjustPaintCellRect@TVTPaintInfo@TColumnIndex -Summary -Used in descentants to modify the clip rectangle of the current column while painting a certain node. - -Description -The rectangle for the given cell (node, column pair in PaintInfo) can be adjusted by descendants to make room for -special drawings, if necessary. - -@@TBaseVirtualTree.AdjustPanningCursor@Integer@Integer -Summary -Loads the proper cursor which indicates into which direction scrolling is done. - -Description -Wheel mice support a special mode for their wheel, which is used in many applications. By pressing the wheel (which is -also a button) you can start so called wheell panning. In this mode the tree window is smoothly scrolled in the -direction to which the mouse pointer is moved. As soon as you release the wheel button wheel panning is stopped. A second -form of this feature is referred to as wheel scrolling. It is basically the same as wheel panning but is entered -when you release the wheel button before you moved the mouse. In this mode you can move the mouse and do the tree -scrolling without holding the wheel all the time. To stop this mode simple turn the wheel, or click any mouse button. -Also pressing ESC will cause to leave the wheel scrolling mode. - - - -Depending on the direction the tree content is scroll also the mouse cursor must be adjusted to indicate this direction. -AdjustPanningCursor does this. - -@@TBaseVirtualTree.AdviseChangeEvent@Boolean@PVirtualNode@TChangeReason -Summary -Used to register a delayed change event. - -Description -Often there can be many change events in a row and calling the application for each of them might be too time costly. So -they are by default accumulated until a certain time has elapsed (ChangeDelay) or, if BeginUpdate was called, until -EndUpdate is executed. If StructureChange is False then we have a selection change event (without a specific -reason) otherwise it is a structure change. - - - -There are two possibilities to avoid delayed change events. One is the permanent way by setting ChangeDelay to 0, the -\other one is to enter the synchronous mode by calling BeginSynch. - -@@TBaseVirtualTree.AllocateInternalDataArea@Cardinal -Summary -Registration method to allocate tree internal data per node. - -Description -This method is used for descentants to specify their need for internal data. Each node contains some extra reserved bytes -between the node's normal members and the user data area. This internal area can be used to cache additional information, -e.g. the string tree keeps here the width of the node's caption in the main column for quick hit tests when doing draw -selection with the mouse. - - - -A tree implementation must call this method only once and before any node is created (except the hidden root node which -is handled accordingly). The result value is the offset from the start of the node to the internal data area of the node -for this tree class. I recommend to implement an access method called InternalData (as shown in TCustomVirtualStringTree) -which does the pointer mathematic. - - - -See Also -, TotalInternalDataSize - -@@TBaseVirtualTree.Animate@Cardinal@Cardinal@TVTAnimationCallback@Pointer -Summary -Support method for animated actions in the tree view. - -Description -This method is a general purpose helper to do an animation and is used for hint fading, animated node toggling etc. The -method automatically takes care that the animation is done within the specified time interval. For each step in the -animation loop the provided callback is called which gets Data passed as parameter. - -@@TBaseVirtualTree.Assign@TPersistent -Summary -Used to copy properties from another Virtual Treeview. - -Description -Although this method assignes most tree properties it does not assign the header and the nodes to the new tree. There is -an own method (TVTHeader.Assign) for the header assignment. In order to copy the nodes you must save them to a stream and -restore them in the other control- - -@@TBaseVirtualTree.BeginDrag@Boolean@Integer -Summary -Starts an OLE drag'n drop operation. - -Description -This method is called within the mouse down handler when DragMode is set to dmAutomatic. Manual start of a drag operation -is not recommended as it confuses the correct mouse down handling which is quite complex in Virtual Treevew. If you -selectively want to allow to start a drag operation then use the OnDragAllowed event which is called when DragMode is -dmManual. - -@@TBaseVirtualTree.BeginSynch -Summary -Enters the tree into a special synchronized mode. - -Description -Similar to BeginUpdate does BeginSynch provide a mechanism to bring certain events into a common line. That means, -whenever you need to make sure change events are called before a modification in the tree is finished (e.g. when changing -the focus or selection) then use the synchronous mode started with BeginSynch (and stopped with EndSynch). - -@@TBaseVirtualTree.BeginUpdate -Summary -Locks the tree view to perform several update operations. - -Description -Call this method when a long lasting operation begins which might involve manipulation of many nodes. - -@@TBaseVirtualTree.CalculateSelectionRect@Integer@Integer -Summary -Support method for draw selection. - -Description -Recalculates old and new selection rectangle given that X, Y are new mouse coordinates. The function returns true if -there was a change since the last call. - -@@TBaseVirtualTree.CanAutoScroll -Summary -Determines whether the tree can currently auto scroll its window. - -Description -This method was created because the conditions when the tree may automatically scroll its content are quite complex. -Additionally, tree descendants might want to add further limitations. Thus the determination has been put into an own -method which returns true if the tree is allowed to scroll, otherwise False. - -@@TBaseVirtualTree.CancelCutOrCopy -Summary -Canceles any pending cut or copy clipboard operation. - -Description -This method is used to stop any pending clipboard operation. No data is transfered nor are nodes deleted. - -@@TBaseVirtualTree.CancelEditNode -Summary -Cancel the current edit operation, if there is any. - -Description -Used to stop the current edit operation.The node editor will get a CancelEdit call so that the node is not changed. - -@@TBaseVirtualTree.CanEdit@PVirtualNode@TColumnIndex -Summary -Determines whether a node can be edited or not. - -Description -The method is called when the tree is about to start a node edit operation. Returns true if editing is allowed, otherwise -false. - -@@TBaseVirtualTree.CanFocus -Summary -Support method to determine whether the tree window can receive the input focus. - -Description -The method adds a check for the parent form of the control. - -@@TBaseVirtualTree.CanShowDragImage -Summary -Determines whether a drag image should be shown. - -Description -This overridable method is used to determine whether a drag image can be shown or not. - -@@TBaseVirtualTree.Change@PVirtualNode -Summary -Central method called when a node's selection state changes. - -Description -The Change method is called to trigger the change notifcation chain. Depending on the sync and the update states of the -tree as well as the ChangeDelay value either the application is directly notified about the change or a timer is started -to accumulate several change events into one. - - - -See Also -BeginSynch, EndSynch, BeginUpdate, EndUpdate, ChangeDelay - -@@TBaseVirtualTree.ChangeScale@Integer@Integer -Summary -Helper method called by the VCL when control resizing is due. - -Description -ChangeScale is a method introduced by TControl. In Virtual Treeview it is responsible to change the tree's and the -header's fonts as well as to compute the new default node height. - - - -See Also -TVTHeader.ChangeScale, DefaultNodeHeight - -@@TBaseVirtualTree.CheckParentCheckState@PVirtualNode@TCheckState -Summary -Helper method for recursive check state changes. - -Description -Checks all siblings of node to determine which check state Node's parent must get. - -@@TBaseVirtualTree.Clear -Summary -Clears the tree and removes all nodes. - -Description -All pending operations are stopped and the tree is ready to receive new nodes. - -@@TBaseVirtualTree.ClearSelection -Summary -Removes all nodes from the current selection. - -Description -ClearSelection empties the internal selection cache and resets the vsSelected state from all nodes, which were in this -array. - -@@TBaseVirtualTree.ClearTempCache -Summary -Helper method to clear the internal temporary node cache. - -Description -The internal node cache is used when more than one node is involved in certain operations (e.g. including a range of -nodes into the current selection). - -@@TBaseVirtualTree.ColumnIsEmpty@PVirtualNode@TColumnIndex -Summary -Used to determine if a cell is considered as being empty. - -Description -An empty cell might be used for the automatic column spanning feature. Descentants can override this method to modify the -tree's behavior. - - - -See Also -toAutoSpanColumns - -@@TBaseVirtualTree.CopyTo@PVirtualNode@TBaseVirtualTree@TVTNodeAttachMode@Boolean - - -@@TBaseVirtualTree.CopyTo@PVirtualNode@PVirtualNode@TVTNodeAttachMode@Boolean -Summary -Copies Source and all its child nodes to Target. - -Description -Mode is used to specify further where to add the new node actually (as sibling of Target or as child of -Target). Result is the newly created node to which source has been copied if ChildrenOnly is False or just -contains Target in the other case. ChildrenOnly determines whether to copy also the source node or only its -child nodes. - - - -The variant taking a tree reference as target can be used to transfer nodes to a different tree, without determining its -root node first. However one can also pass in any virtual tree node as target, as long as it belongs to a tree. The -\owning tree is automatically determined. - -@@TBaseVirtualTree.MoveTo@PVirtualNode@TBaseVirtualTree@TVTNodeAttachMode@Boolean - - -@@TBaseVirtualTree.MoveTo@PVirtualNode@PVirtualNode@TVTNodeAttachMode@Boolean -Summary -Moves Source and all its child nodes to Target. - -Description -Moves the given node (and all its children) to Target. Source must belong to the tree instance which calls -this MoveTo method. Mode determines how to connect Source to Target. This method might involve a -change of the tree if Target belongs to a different tree than Source. - - - -The variant taking a tree reference as target can be used to transfer nodes to a different tree, without determining its -root node first. However one can also pass in any virtual tree node as target, as long as it belongs to a tree. The -\owning tree is automatically determined and an optimized path is taken if the operation happens within one tree. In this -case simply the source node is disconnected from the old place and reconnected at the new location. - -@@TBaseVirtualTree.CopyToClipBoard -Summary -Copies all currently selected nodes to the clipboard. - -Description -CopyToClipboard causes the tree to copy the currently selected nodes to the clipboard. Actually, Virtual Treeview -maintains socalled delayed rendering. This means the participating nodes are marked as being in the current clipboard set -(see vsCutOrCopy in TVirtualNodeStates) and only an IDataObject interface is placed onto the clipboard but no data yet. -This avoids not only possibly huge memory requirements but it also avoids rendering data in a format which is not -necessary. The application which pastes the clipboard content later will get the IDataObject interface and requests the -format it can handle. The actual data is then rendered when the target application calls IDataObject.GetData, which -results in a call to RenderOLEData. - -@@TBaseVirtualTree.CutToClipBoard -Summary -Copies the currently selected nodes to the clipboard and removes them once a consumer has taken the data. - -Description -Similar to CopyToClipboard only the nodes are deleted after they have been pasted into the target. - -@@TBaseVirtualTree.DeleteChildren@PVirtualNode@Boolean -Summary -Removes all child nodes from the given node. - -Description -The method works recursively: all grandchildren and their children are removed as well. - -@@TBaseVirtualTree.DeleteNode@PVirtualNode@Boolean -Summary -Removes the given node from the tree. - -Description -This method deletes the given node. If the node was initialized or had gotten initial data via the AddChild or InsertNode -then the event OnFreeNode is called to allow the application to free any user data attached to a node. - -@@TBaseVirtualTree.DeleteSelectedNodes -Summary -Removes all currently selected nodes form the tree. - -Description -All nodes in the current selection are affected. - -@@TBaseVirtualTree.DoCancelEdit -Summary -Called when the tree should stop editing without accepting changed values. - -Description -This method calls the edit link's IEditLink.CancelEdit method and stops the edit mode if this call returns True. If -stopping is allowed then the event OnEditCancelled is triggered and a message is sent to release the edit link -asynchronously. - -@@TBaseVirtualTree.DoDragging@TPoint -Summary -\Internal method which handles drag' drop. - -Description -This method starts the OLE drag'n drop operation and returns after this operation is finished. - -@@TBaseVirtualTree.DoEdit -Summary -Initiates editing of the currently set focused column and edit node. - -Description -This method takes care for editor creation and initialization. You can look for tsEditing in TreeStates to know whether -editing is currently active. - - - -See Also -tsEditing, OnCreateEditor, IVTEditLink - -@@TBaseVirtualTree.DoEndEdit -Summary -Stops the current edit operation and takes over the new content. - -Description -The method also sends a message to the tree window to asynchronously release the edit link which communicates to the -actual editor. The edit link is responsible to propagate any changes made in its node editor to the tree. - - - -Note -TVirtualStringTree overrides this method to tell the application about the new caption by calling OnNewText. - -See Also -DoEdit, OnNewText, EditNode - -@@TBaseVirtualTree.DoFocusNode@PVirtualNode@Boolean -Summary -\Internal method to set the focused node. - -Description -This methods is called by the property setter for the focused node as well as from other places to do the actual change. -It takes the parameter Ask to optionally switch off (Ask = False) triggering the OnFocusChanging event. - -@@TBaseVirtualTree.DoGetAnimationType -Summary -Determines the type of animation to be used. - -Description -Windows 98 and Windows 2000 introduced two ways of animating hints when they appear: a sliding window and a fading -window. Virtual Treeview implements both animation types and also supports system dependent animations. This allows to -use the animation type enabled in the particular system on which the tree currently runs. Additonally, there is a check -for MMX to do a fallback if fade animation is specified but no MMX available. In this case sliding is used. Starting with -Windows 2000 and Windows ME the hint animation can even be be switched off entirely. Also this case is handled by this -method. - - - -@@TBaseVirtualTree.DoGetNodeWidth@PVirtualNode@TColumnIndex@TCanvas -Summary -Overridable method which always retuns 0. - -Description -Descentants override this method to return a value which describes the width of a node. This is the inner width of the -node excluding tree lines etc. So TVirtualStringTree returns the width of the node caption (plus text margin). - -@@TBaseVirtualTree.DoGetPopupMenu@PVirtualNode@TColumnIndex@TPoint -Summary -Overridable method which triggers the OnGetPopup event. - -Description -This method does an automatic parent traversal in the tree hierarchy to find a matching popup menu. - -@@TBaseVirtualTree.DoPaintDropMark@TCanvas@PVirtualNode@TRect -Summary -Overridable method which draws the small line on top of a nodes image depending on the current drop state. - -Description -This method draws a simple polyline using Colors.DropMarkColor. Descentant can override this method to customize the -appearance of the drop mark. - -@@TBaseVirtualTree.DoPaintNode@TVTPaintInfo -Summary -Overridable method which does nothing. - -Description -Descentants override this method to paint the content of the node. For instance string trees draw the node's caption. - -@@TBaseVirtualTree.DoPopupMenu@PVirtualNode@TColumnIndex@TPoint -Summary -Overridable method which shows the popup menu for the given node. - -Description -Node and Column describe the cell for which the menu should be shown. Position determines the place -(in client coordinates of the tree window) where to show the menu. - -@@TBaseVirtualTree.DoScroll@Integer@Integer -Summary -Overridable method which triggers the OnScroll event. - -Description -This method is the ideal place if you want to synchronize other controls with the tree. The event is triggered whenever -the tree is scrolled (by the user or programmatically). DeltaX and DeltaY contain the relative values the -position changed about. - -@@TBaseVirtualTree.DoSetOffsetXY@TPoint@TScrollUpdateOptions@PRect -Summary -\Internal core routine to set the tree's scroll position. - -Description -The method takes the Value structure which contains the new absolute scroll positions, both horizontal and -vertical. Options specifies what should happen in the update process. A combination of the following values is -possible: - - - - * suoRepaintHeader, If suoUpdateNCArea is also set then invalidate the header to refresh its - screen image, otherwise it is ignored. - * suoRepaintScrollbars, If suoUpdateNCArea is also set then repaint both scrollbars after - updating them, otherwise it is ignored. - * suoScrollClientArea, Scroll and invalidate the proper part of the client area. - * suoUpdateNCArea, Update non-client area (scrollbars, header). - -@@TBaseVirtualTree.DoTimerScroll -Summary -Callback method which is triggered whenever the scroll timer fires. - -Description -This method is called to do an automatic tree scroll when the user selects nodes with the mouse (multiselection only). - -@@TBaseVirtualTree.DragDrop@IDataObject@Integer@TPoint@Integer -Summary -Helper method, which is used when a drag operation is finished. - -Description -This method is called by the TVTDragManager.Drop and prepares the list of available clipboard formats to be passed to -DoDragDrop. - -@@TBaseVirtualTree.DragFinished -Summary -Called when a drag operation is finished (accepted or cancelled). - -Description -This method is nternally used ito make up for the swallowed mouse-up messages during drag' drop. - -@@TBaseVirtualTree.Dragging -Summary -\Returns true if a drag'n drop operation is in progress. - -Description -The method returns true if currently a drag'n drop operation is in progress, which involves this tree view. - -@@TBaseVirtualTree.EditNode@PVirtualNode@TColumnIndex -Summary -Starts editing the given node if allowed to. - -Description -This method can be used by the application to manually start editiing of a particular node. Column determines hereby in -which column the node should be edited. This parameter determines the target column regardless whether toExtendedFocus is -set in TreeOptions.SelectionOptions or not. The given node must be enabled, otherwise edit start fails. - - - -See Also -DoEdit - -@@TBaseVirtualTree.EndEditNode -Summary -Stops node editing if it was started before. - -Description -EndEditNode stops node editing and accepts the result (which must be set by the edit link). - - - -See Also -, EditNode, DoEdit - -@@TBaseVirtualTree.EndSynch -Summary -Counterpart to BeginSynch. - -Description -Counts down the internal synchronous mode counter and ends synchronous mode when this counter reaches zero. - - - -See Also -BeginSynch, BeginUpdate, EndUpdate - -@@TBaseVirtualTree.EndUpdate -Summary -Resets the update lock set by BeginUpdate. - -Description -This method is the counterpart to BeginUpdate and decreases the internal update count value. If this value reaches 0 then -updates of the tree window will be allowed again. Additionally, some pending operations, which might be started during -the update lock, are finished. This includes tasks like updating the selection list, validating the cache and sorting the -tree if in auto sort mode. - -@@TBaseVirtualTree.FindNodeInSelection@PVirtualNode@Integer@Integer@Integer -Summary -Helper method to find the given node in the current selection. - -Description -This method does a binary search of the given node in the internal selection array which is sorted by memory references. -The search is limited to the area given by LowBound and HighBound. If the node could be found then true is -returned and Index is set to the found node position. - -@@TBaseVirtualTree.FinishCutOrCopy -Summary -Stops any pending cut or copy clipboard operation. - -Description -This method is used by the tree (and can be used by the application too) to stop any pending cut or copy clipboard -\operation. If a cut operation is pending then nodes currently marked with the vsCutOrCopy state are deleted. - -@@TBaseVirtualTree.FlushClipboard -Summary -Renders all pending clipboard data. - -Description -Used to render the data which is currently on the clipboard and finishes so the delayed rendering. This method is useful -if the tree is about to be destroyed but data from this tree is still on the clipboard and should stay there. If this -method is not used then any pending clipboard operation is cancelled on tree destruction (by the tree instance which -currently has data on the clipboard) and the clipboard itself is cleared. - -@@TBaseVirtualTree.FullCollapse@PVirtualNode -Summary -Collapses all nodes in the tree. - -Description -Call this method to bring all nodes in the tree into a collapsed state. This method is used to reset the vsExpanded state -in all nodes in the tree. Nodes which are not yet initialized are also not expanded by definition and therefore do not -need initialization. - - - -See Also -FullExpand - -@@TBaseVirtualTree.FullExpand@PVirtualNode -Summary -Expands all nodes in the tree. - -Description -Call this method to bring all nodes in the tree into an expanded state. This method expands every node in the tree and -initializes nodes which are not yet initialized to expand them too if necessary. Since this will validate every node in -the tree it is counterproductive and against the . - -@@TBaseVirtualTree.GetColumnClass -Summary -\Returns the class to be used to manage columns in the tree. - -Description -GetColumnClass is a special purpose method to return a certain class which is used by the tree for the columns. -TVirtualBaseTree always returns TVirtualTreeColumn but descentants can override this method to return own classes. - -@@TBaseVirtualTree.GetDisplayRect@PVirtualNode@TColumnIndex@Boolean@Boolean -Summary -\Returns the visible region used by the given node in client coordinates. - -Description -If the given node cannot be found (because one of its parents is collapsed or it is invisible) then an empty rectangle is -returned. If TextOnly is true then only the text bounds are returned, that is, the resulting rectangle's left and -right border are updated according to the bidi mode, alignment and text width of the node. If Unclipped is true -(which only makes sense if also TextOnly is true) then the calculated text rectangle is not clipped if the text -does not entirely fit into the text space. This is special handling needed for hints. - - - -If Column is NoColumn then the entire client width is used before determining the node's width otherwise the bounds of -the particular column are used. - -Note -Column must be a valid column and is used independent of whether the header is visible or not. - -@@TBaseVirtualTree.GetFirst -Summary -Group of node navigation functions. - -Description -This group of navigation functions is used to return the first node in the tree or first sub node with various -properties. - -GetFirst First node in the tree with initialization. -GetFirstChild First child node with initialization. -GetFirstCutCopy First node in cut/copy set (no initialization needed). -GetFirstInitialized First initialized node in the tree (no initialization needed). -GetFirstNoInit First node in the tree without initialization. -GetFirstVisible First visible node in the tree with initialization. -GetFirstVisibleChild First visible child of a node with initialization. -GetFirstVisibleChildNoInit First visible child of a node without initialization. -GetFirstVisibleNoInit First visible node in the tree without initialization. -
- -@@TBaseVirtualTree.GetFirstChild@PVirtualNode - - -@@TBaseVirtualTree.GetFirstCutCopy - - -@@TBaseVirtualTree.GetFirstInitialized - - -@@TBaseVirtualTree.GetFirstNoInit - - -@@TBaseVirtualTree.GetFirstSelected - - -@@TBaseVirtualTree.GetFirstVisible - - -@@TBaseVirtualTree.GetFirstVisibleChild@PVirtualNode - - -@@TBaseVirtualTree.GetFirstVisibleChildNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetFirstVisibleNoInit - - -@@TBaseVirtualTree.GetHeaderClass -Summary -\Returns the header class to be used by the tree. - -Description -As with several other classes in Virtual Treeview (e.g. drag manager, options etc.) also a customized header class is -supported, which allows applications or descendant classes to implement their very own header class with special -behavior. This is a further element to make Virtual Treeview as flexible as possible. - -@@TBaseVirtualTree.GetHitTestInfoAt@Integer@Integer@Boolean@THitInfo -Summary -\Returns information about the node at the given position. - -Description -This method returns information about the given hit position. If the position is not within the client area then the -\result is either of hiAbove, hiBelow, hiToLeft or hiToRight, depending on the side. If the position is within the client -area but no node is hit (e.g. when the tree is empty) then hiNowhere is returned, otherwise the node is examined and HitInfo -is filled with information about which node is hit by this position, which column is involved and where on the node is -the hit (e.g. the caption, the expand/collapse button or the state image). - - - -The parameter Relative is used to tell the method how to interpret the given coordinates. If this property is true -then X and Y are given in client coordinates of the tree window, otherwise they represent absolute -coordinates of the . - - -@@TBaseVirtualTree.GetLast@PVirtualNode -Summary -Group of node navigation functions. - -Description -This group of navigation functions is used to return the last node in the tree or last sub node with various properties. - -GetLast Last node in the tree with initialization. -GetLastChild Last child node with initialization. -GetLastChildNoInit Last child node without initialiization. -GetLastInitialized Last initialized node in the tree (no initialization needed). -GetLastNoInit Last node in the tree without initialization. -GetLastVisible Last visible node in the tree with initialization. -GetLastVisibleChild Last visible child of a node with initialization. -GetLastVisibleChildNoInit Last visible child of a node without initialization. -GetLastVisibleNoInit Last visible node in the tree without initialization. -
- -@@TBaseVirtualTree.CountLevelDifference@PVirtualNode@PVirtualNode -Summary -Determines the level difference of two nodes. - -Description -This method counts how many indentation levels the given nodes are apart. If both nodes have the same parent then the -difference is 0 otherwise the result is basically GetNodeLevel(Node2) - GetNodeLevel(Node1), but with sign. If the result -is negative then Node2 is less intended than Node1. - -@@TBaseVirtualTree.CountVisibleChildren@PVirtualNode -Summary -Determines the number of visible child nodes of the given node. - -Description -CountVisibleChildren iterates through all child nodes of Node and counts how many of them have the vsVisible state -set. - -@@TBaseVirtualTree.Create@TComponent -Summary -Constructor of the control - -Description -The constructor initializes certain properties to their default values. - -@@TBaseVirtualTree.CreateParams@TCreateParams -Summary -Prepares the creation of the controls window handle. - -Description -CreateParams is overriden to allow to set certain window styles for the control. - -@@TBaseVirtualTree.CreateWnd -Summary -Initializes data, which depends on the window handle. - -Description -Some properties must be preset first after the window handle was created. CreateWnd is the perfect place for this. - -@@TBaseVirtualTree.DefineProperties@TFiler -Summary -Helper method to customize loading and saving persistent tree data. - -Description -There were heavy changes in some properties during development of VT. This method helps to make migration easier by -reading old properties manually and put them into the new properties as appropriate. These old properties are never -written again and silently disappear. - - - -Another task of this method is to work around the problem that TCollection is not streamed correctly when using Visual -Form Inheritance (VFI). - -@@TBaseVirtualTree.Destroy -Summary -Destructor of the control. - -Description -Frees any allocated data in the tree. All pending operations will be stopped and any remaining node is freed. - -@@TBaseVirtualTree.DetermineHiddenChildrenFlag@PVirtualNode -Summary -Determines whether all children of a given node are hidden. - -Description -Virtual Treeview supports a feature, which is called node button auto hide. What happens is that when -all children of a node are hidden then the expand button for this node is automatically removed. In order to know about -the visibility state of the child nodes an internal flag is maintained, which allows to quickly decide about the button -display. DetermineHidenChildren is the update method for cases where more than one child node changed. - -See Also -vsVisible, toAutoHideButtons - -@@TBaseVirtualTree.DetermineHiddenChildrenFlagAllNodes -Summary -Determines whether all children of all nodes are hidden. - -Description -As extension to DeterminHiddenChildren this method iteratively determines the hidden children flag for all existing nodes -in the tree. This is only used for large updates. No node will be initialized in this process. - -@@TBaseVirtualTree.DetermineHitPositionLTR@THitInfo@Integer@Integer@TAlignment -Summary -Determines the hit position within a node with left-to-right and right-to-left orientation. - -Description -This method, together with its counter part DetermineHitPositionRTL, is used in the process of figuring out where the a -given position is located in relation to a node. - -@@TBaseVirtualTree.DetermineHitPositionRTL@THitInfo@Integer@Integer@TAlignment - - -@@TBaseVirtualTree.DoAutoScroll@Integer@Integer -Summary -Enables or disables the auto scroll timer. - -Description -This method determines whether the tree needs to be scrolled (the mouse is near the borders) and enables or disables the -\internal scroll timer which triggers the DoTimerScroll method. - -@@TBaseVirtualTree.DragCanceled -Summary -Called by the VCL when a drag'n drop operation was canceled by the user. - -Description -DragCanceled is used to do some housekeeping in the tree. - -@@TBaseVirtualTree.GetLastChild@PVirtualNode - - -@@TBaseVirtualTree.GetLastChildNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetLastInitialized@PVirtualNode - - -@@TBaseVirtualTree.GetLastNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetLastVisible@PVirtualNode - - -@@TBaseVirtualTree.GetLastVisibleChild@PVirtualNode - - -@@TBaseVirtualTree.GetLastVisibleChildNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetLastVisibleNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetMaxColumnWidth@TColumnIndex -Summary -\Returns the width of the largest node in the given column. - -Description -This method is mainly used to determine a minimal width of the given column without having to shorten a node caption. -Since the method has to go through all visible nodes and initialize them to learn about their width it might be time -consuming to call this method and circumvents also the virtual approach of the tree. - -@@TBaseVirtualTree.GetMaxRightExtend -Summary -Determines the maximum with of the currently visible part of the tree. - -Description -This method is similar to GetMaxColumnWidth, but determines the width of the tree if no columns are used. This method is -used for determining the horizontal scroll range for the columnless case. - -@@TBaseVirtualTree.GetNativeClipboardFormats@TFormatEtcArray -Summary -Used to let descendants and the application add their own supported clipboard formats. - -Description -GetNativeClipboardFormats returns the supported clipboard formats of the tree in the native CF_* form as used in -IDataObject. This includes all formats which are listed in the ClipboardFormats property as well as any changes made by -the OnGetUserClipboardFormats event if a handler for it is attached. - -@@TBaseVirtualTree.GetNext@PVirtualNode -Summary -Group of node navigation functions. - -Description -This group of navigation functions is used to return the next node relative to a given node in the tree with various -properties. - -GetNext Next node in the tree with initialization. -GetNextCutCopy Next node in the cut/copy set (no initialization needed). -GetNextInitialized Next initialized node in the tree (no initialization needed). -GetNextNoInit Next node in the tree without initialization. -GetNextSelected Next selected node (no initialization needed). -GetNextSibling Next sibling node with initialization. -GetNextVisible Next visible node in the tree with initialization. -GetNextVisibleNoInit Next visible node in the tree without initialization. -GetNextVisibleSibling Next visible sibling node with initialization. -GetNextVisibleSiblingNoInit Next visible sibling node without initialization. -
- -@@TBaseVirtualTree.GetNextCutCopy@PVirtualNode - - -@@TBaseVirtualTree.GetNextInitialized@PVirtualNode - - -@@TBaseVirtualTree.GetNextNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetNextSelected@PVirtualNode - - -@@TBaseVirtualTree.GetNextSibling@PVirtualNode - - -@@TBaseVirtualTree.GetNextVisible@PVirtualNode - - -@@TBaseVirtualTree.GetNextVisibleNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetNextVisibleSibling@PVirtualNode - - -@@TBaseVirtualTree.GetNextVisibleSiblingNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetNodeData@PVirtualNode -Summary -\Returns the address of the user data area of the given node. - -Description -GetNodeData returns the address of the user data area for Node. It is strongly recommended to use this method -instead directly accessing @Node.Data. Some trees require internal data for their own use which is also stored after -Node.Data and the actual user data (application data) follows then this internal data. GetNodeData takes care of this -situation. - -@@TBaseVirtualTree.GetNodeLevel@PVirtualNode -Summary -\Returns the indentation level of the given node. - -Description -GetNodeLevel returns the level of Node. This level is determined by the number of parent nodes (excluding the -hidden root node). Top level nodes have the level 0, their direct child nodes have level 1 etc. - -@@TBaseVirtualTree.GetOptionsClass -Summary -Customization helper to determine which options class the tree should use. - -Description -GetOptionsClass is a special purpose method to return a certain class which is used by the tree for its options. -TVirtualBaseTree always returns TCustomVirtualTreeOptions but descendants can override this method to return own classes. - - - -For ease of use it makes much sense to always use the same name for the tree's options (which is TreeOptions). By using a -customized options class, however, the wrong type is returned by this property. Hence it is meaningful to override -TreeOptions and return the derived options class. To make this work the tree descendant must additionally provide new -access methods for this property. An example can be seen in TVirtualStringTree: - - - - TVirtualStringTree = class(TCustomVirtualStringTree) - private - function GetOptions: TStringTreeOptions; - procedure SetOptions(const Value: TStringTreeOptions); - protected - function GetOptionsClass: TTreeOptionsClass; override; - public - property Canvas; - published - ... - property TreeOptions: TStringTreeOptions read GetOptions write SetOptions; - ... - end; - - ... - -//----------------- TVirtualStringTree --------------------------------------------------------------------------------- - -function TVirtualStringTree.GetOptions: TStringTreeOptions; - -begin - \Result := FOptions as TStringTreeOptions; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualStringTree.SetOptions(const Value: TStringTreeOptions); - -begin - FOptions.Assign(Value); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVirtualStringTree.GetOptionsClass: TTreeOptionsClass; - -begin - \Result := TStringTreeOptions; -end; - - - -@@TBaseVirtualTree.GetPrevious@PVirtualNode -Summary -Group of node navigation functions. - -Description -This group of navigation functions is used to return the previous node relative to a given node in the tree with various -properties. - -GetPrevious Previous node in the tree with initialization. -GetPreviousInitialized Previous initialized node in the tree (no initialization needed). -GetPreviousNoInit Previous node in the tree without initialization. -GetPreviousSibling Previous sibling node with initialization. -GetPreviousVisible Previous visible node in the tree with initialization. -GetPreviousVisibleNoInit Previous visible node in the tree without initialization. -GetPreviousVisibleSibling Previous visible sibling node with initialization. -GetPreviousVisibleSiblingNoInit Previous visible sibling node without initialization. -
- -@@TBaseVirtualTree.GetPreviousInitialized@PVirtualNode - - -@@TBaseVirtualTree.GetPreviousNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetPreviousSibling@PVirtualNode - - -@@TBaseVirtualTree.GetPreviousVisible@PVirtualNode - - -@@TBaseVirtualTree.GetPreviousVisibleNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetPreviousVisibleSibling@PVirtualNode - - -@@TBaseVirtualTree.GetPreviousVisibleSiblingNoInit@PVirtualNode - - -@@TBaseVirtualTree.GetSortedCutCopySet@Boolean -Summary -\Returns a sorted list of nodes, which are marked for s cut or copy clipboard operation. - -Description -\Returns a list of nodes which are flagged with vsCutOrCopy, sorted in logical order, that is, as they appear in the -tree. If Resolve is true then nodes which are children of other cut/copy nodes are not put into the new array. -This feature is particularly important when doing drag'n drop as in this case all selected node plus their children need -to be considered. A selected node, which is a child (grand child etc.) of another selected node is then automatically -included and doesn't need to be explicitly mentioned in the returned selection array. - -Note -The caller is responsible for freeing the array. Allocation is done here. Usually, though, freeing the array doesn't need -additional attention as it is automatically freed by Delphi when it gets out of scope. - -@@TBaseVirtualTree.GetSortedSelection@Boolean -Summary -\Returns a sorted list of all currently selected nodes. - -Description -\Returns a list of selected nodes sorted in logical order, that is, as they appear in the tree. If Resolve is true -then nodes which are children of other selected nodes are not put into the new array. This feature is in particuar -important when doing drag'n drop as in this case all selected node plus their children need to be considered. A selected -node which is child (grand child etc.) of another selected node is then automatically included and doesn't need to be -explicitely mentioned in the returned selection array. - -Note -The caller is responsible for freeing the array. Allocation is done here. Usually, though, freeing the array doesn't need -additional attention as it is automatically freed by Delphi when it gets out of scope. - -@@TBaseVirtualTree.GetTextInfo@PVirtualNode@TColumnIndex@TFont@TRect@WideString -Summary -Helper method for node editors, hints etc. - -Description -GetTextInfo is used to define a base access method for node data and the associated font from node editors and for hints. - -@@TBaseVirtualTree.GetTreeFromDataObject@IDataObject -Summary -OLE drag'n drop and clipboard support method. - -Description -\Returns the owner/sender of the given data object by means of a special clipboard format or nil if the sender is in -another process or no virtual tree at all. - -@@TBaseVirtualTree.GetTreeRect -Summary -\Returns the size of the virtual tree image. - -Description -GetTreeRect can be used to determine the full size of the as used for painting etc. - -@@TBaseVirtualTree.GetVisibleParent@PVirtualNode -Summary -\Returns the first (nearest) parent node, which is visible. - -Description -GetVisibleParent returns the first (nearest) parent node of Node which is visible. This method is one of the -seldom cases (if not the only one) where the hidden root node could be returned. - -@@TBaseVirtualTree.HasAsParent@PVirtualNode@PVirtualNode -Summary -Determines if the given node has got another node as one of its parents. - -Description -Determines whether Node has got PotentialParent as one of its parents. - -@@TBaseVirtualTree.HasPopupMenu@PVirtualNode@TColumnIndex@TPoint -Summary -Determines whether there is a pop up menu assigned to the tree. - -Description -This overridable method is used to determine whether there is a pop up menu assigned to the tree or can be retrieve via -the OnGetPopupMenu event for a particular node. This is necessary for the tree to know how to deal with various condition -in an mouse button down event. - -@@TBaseVirtualTree.InsertNode@PVirtualNode@TVTNodeAttachMode@Pointer -Summary -Inserts a new node and returns it to the caller. - -Description -Adds a new node relative to Node. The final position is determined by Mode. UserData can be used to -set the first 4 bytes of the user data area to an initial value, which can be used in OnInitNode and will also cause to -trigger the OnFreeNode event (if \<\> nil) even if the node is not yet "officially" initialized. - -InsertNode is a compatibility method and will implicitly validates the given node if the new node is to be added as child -node. This is however against the virtual paradigm and hence I dissuade from its usage. - -@@TBaseVirtualTree.InternalData@PVirtualNode -Summary -\Returns the address of the internal data for a tree class. - -Description -In TBaseVirtualTreeview this method returns nil but should be overridden in descendants to allow proper access to the -\internal data of Node if the descendant tree has allocated internal data. - - - -See Also - - -@@TBaseVirtualTree.InvalidateCache -Summary -Empties the internal node cache and marks it as invalid. - -Description -Marks the internal node cache as being invalid. This will cause a cache validation run next time ValidateCache is called. - - - -The internal node cache is used to speed up display in Virtual Treeview. It contains node references with a distance of -CacheThreshold nodes along with their vertical absolute position, which makes it possible to quickly find the position of -a node for display, hit tests and so on. - -@@TBaseVirtualTree.InvalidateChildren@PVirtualNode@Boolean -Summary -Invalidates all children of the given node. - -Description -Invalidates Node and its immediate children. If Recursive is true then all grandchildren are invalidated as -well. The node itself is initialized if necessary and its child nodes are recreated (and initialized too if Recursive -is true). - -@@TBaseVirtualTree.InvalidateColumn@TColumnIndex -Summary -Invalidates the client area part of a column. - -Description -Invalidates the client area part of a column. - -@@TBaseVirtualTree.InvalidateNode@PVirtualNode -Summary -Invalidates the given node. - -Description -InvalidateNode initiates repaint of the given node by calling InvalidateRect with the node's display rectangel and -\returns this rectangle. - -@@TBaseVirtualTree.InvalidateToBottom@PVirtualNode -Summary -Invalidates the client area starting with the top position of the given node. - -Description -InvalidateToBottom initiates repaint of client area starting at given node. If this node is not visible or not yet -initialized then nothing happens. - -@@TBaseVirtualTree.InvertSelection@Boolean -Summary -Inverts the current selection. - -Description -InvertSelection inverts the current selection, so nodes, which are selected become unselected and vice versa. If VisibleOnly -is true then only visible nodes are considered. - -@@TBaseVirtualTree.IsEditing -Summary -Tells the caller whether the tree is currently in edit mode. - -Description -Just a simple shortcut to test the tsEditing state. - -@@TBaseVirtualTree.IsMouseSelecting -Summary -Tell the caller whether the tree is currently in draw selection mode. - -Description -IsMouseSelecting returns true if draw selection by the user is active or pending. - -@@TBaseVirtualTree.IterateSubtree@PVirtualNode@TVTGetNodeProc@Pointer@TVirtualNodeStates@Boolean@Boolean -Summary -Iterator method to go through all nodes of a given sub tree. - -Description -IterateSubtree iterates through all children and grandchildren etc. of Node (or the entire tree if Node = -nil) and calls for each node the provided callback method (which must not be empty). Filter determines which nodes -are to be considered (an empty set denotes all nodes). If DoInit is true then nodes which aren't initialized yet -will be initialized. - - - -During execution of the callback the application can set Abort to true. In this case the iteration is stopped and -the last accessed node (the one on which the callback set Abort to true) is returned to the caller. Otherwise (no -abort) nil is returned. - -Note -An application should not modify the content of the tree (e.g. delete nodes) during the iteration, otherwise the -\outcome is unpredictable and may result in an access violation. - -@@TBaseVirtualTree.LoadFromFile@TFileName -Summary -Loads previously streamed out tree data back in again. - -Description -LoadFromFile clears the current content of the tree and loads a new structure from the given file. - - - -See Also -AddFromStream - -@@TBaseVirtualTree.LoadFromStream@TStream - - -@@TBaseVirtualTree.Paint -Summary -TControl's Paint method used here to display the tree. - -Description -Overriden method to paint the tree image. The actual work is however done in PaintTree. - -@@TBaseVirtualTree.PaintTree@TCanvas@TRect@TPoint@TVTInternalPaintOptions@TPixelFormat -Summary -Main paint routine for the tree image. - -Description -PaintTree is the core paint routine used to draw any part of the tree image to any canvas. It is responsible for -maintaining the paint cycles per node as well as coordinating drawing of the various parts of the tree image. TargetCanvas -is the canvas to which to draw the tree image. This is usually the tree window itself but could well be a bitmap or -printer canvas. Window determines which part of the entire tree image to draw. The full size of the virtual image -is determined by GetTreeRect. Target is the position in TargetCanvas where to draw the tree part specified -by Window. PaintOptions determines what of the tree to draw. For different tasks usually different parts -need to be drawn, with a full image in the window, selected only nodes for a drag image etc. - -See Also - - -@@TBaseVirtualTree.PasteFromClipboard -Summary -Inserts the content of the clipboard into the tree. - -Description -PasteFromClipboar reads what is currently on the clipboard into the tree (if the format is supported). If the application -wants to have text or special formats to be inserted then it must implement its own code (OLE). Here only the native tree -format is accepted. - -@@TBaseVirtualTree.ProcessDrop@IDataObject@PVirtualNode@Integer@TVTNodeAttachMode -Summary -Helper method to ease OLE drag'n drop operations. - -Description -ProcessDrop can be used in a OnDragDrop handler to let the tree view handle a drop operation of native tree data. The -method only prepares some variables and calls then the more universal ProcessOLEData method. - -@@TBaseVirtualTree.ProcessOLEData@TBaseVirtualTree@IDataObject@PVirtualNode@TVTNodeAttachMode@Boolean -Summary -Takes serialized OLE tree data and reconstructs the former structure. - -Description -PrcessOLEData recreates the (sub) tree structure serialized into memory and provided by DataObject. The new nodes are -attached to the passed node or the hidden root node if TargetNode is nil, according to Mode. Optimized -can be set to true if the entire operation happens within the same process (i.e. sender and receiver of the OLE operation -are located in the same process). Optimized = true makes only sense if the operation to carry out is a move hence -it is also the indication of the operation to be done here. Source is the source of the OLE data and only of use -(and usually assigned) when an OLE operation takes place in the same application. - - - -The function returns true on success, i.e. the CF_VIRTUALTREE format is supported by the data object and the structure -could be recreated, otherwise false. - -@@TBaseVirtualTree.ReinitChildren@PVirtualNode@Boolean -Summary -Forces all child nodes of Node to be reinitialized. - -Description -ReinitChildren forces all child nodes of Node to be reinitialized. If Recursive is true then also the -grandchildren are reinitialized. - -@@TBaseVirtualTree.ReinitNode@PVirtualNode@Boolean -Summary -Forces a reinitialization of the given node. - -Description -ReinitNode forces Node and all its children (if Recursive is true) to be initialized again without -modifying any data in the nodes nor deleting children (unless the application requests a different amount). - -@@TBaseVirtualTree.RemoveFromSelection@PVirtualNode -Summary -Removes the given node from the current selection. - -Description -Removes the vsSelected style from Node's states and also removes Node from the internal selection array. - -@@TBaseVirtualTree.RenderOLEData@TFormatEtc@TStgMedium@Boolean -Summary -Renders pending OLE data. - -Description -RenderOLData is called by TVTDataObject.GetData when a consumer of clipboard data actually requests the data. The base -tree view only renders the native tree format, which is a chunk based stream of node data. The format to be rendered is -specified in FormatEtcIn.cfFormat and is one of the formats which are returned from GetNativeClipboardFormats. - - - -Descendants may override RenderOLEData in order to render other formats like HTML text. In TBaseVirtualTreeview this -method calls the OnRenderOLEData event for all formats, except CF_VIRTUALTREE. - -@@TBaseVirtualTree.RepaintNode@PVirtualNode -Summary -Causes the treeview to repaint the given node. - -Description -RepaintNode causes an immediate repaint of Node and returns once repainting has finished. - -@@TBaseVirtualTree.ResetNode@PVirtualNode -Summary -Resets the given node to uninitialized. - -Description -ResetNode deletes all children of Node and marks it as being uninitialized. - -@@TBaseVirtualTree.SaveToFile@TFileName -Summary -Saves the entire content of the tree into a file or stream. - -Description -Saves the entire content of the tree into a file or stream. - -See Also -LoadFromStream, AddFromStream - -@@TBaseVirtualTree.SaveToStream@TStream@PVirtualNode - - -@@TBaseVirtualTree.ScrollIntoView@PVirtualNode@Boolean@Boolean -Summary -Scrolls the tree so that the given node comes in the client area. - -Description -ScrollIntoView scrolls the tree so that the given node is in the client area and returns true if the tree really has been -scrolled (e.g. to avoid further updates) else it returns false. If extened focus is enabled then the tree will also -horizontally scrolled if needed. All collapsed parents of the node are expanded, forming so a visible path to Node. - -@@TBaseVirtualTree.SelectAll@Boolean -Summary -Selects all nodes in the tree. - -Description -SelectAll select all existing nodes in the tree. If VisibleOnly is true then only visible nodes are selected. - -@@TBaseVirtualTree.SelectNodes@PVirtualNode@PVirtualNode@Boolean -Summary -Selects a range of nodes. - -Description -SelectNodes selects a range of nodes and unselects all other possibly selected nodes which are not in this range if AddOnly -is false. EndNode must be visible while StartNode does not necessarily, as in the case where the last -focused node is the start node but it is a child of a node which has been collapsed previously. In this case the first -visible parent node is used as start node. StartNode can be nil in which case the very first node in the tree is -used. - -@@TBaseVirtualTree.Sort@PVirtualNode@TColumnIndex@TSortDirection@Boolean -Summary -Sorts the given node. - -Description -Sort sorts the child nodes of Node. The application is queried about how to sort via the OnCompareNodes event. Column -is simply passed to the the compare function so the application can also sort in a particular column. In order to free -the application from taking care about the sort direction the parameter Direction is used. This way the -application can always compare as would the node be sorted in increasing direction , while Sort reorders nodes according -to this flag. - -@@TBaseVirtualTree.SortTree@TColumnIndex@TSortDirection@Boolean -Summary -Sorts the entire tree view. - -Description -SortTree sorts the entire tree by applying Sort to every node which has got children. - -Note -This method initializes all nodes in the tree which may not only take quite a while but is also against the -and therefore usually not recommended. - -@@TBaseVirtualTree.ToggleNode@PVirtualNode -Summary -Changes a node's expand state to the opposite state. - -Description -Toggle node expands Node if it is collapsed currently and vice versa. - -@@TBaseVirtualTree.ToggleSelection@PVirtualNode@PVirtualNode -Summary -Toggles the selection state of a range of nodes. - -Description -ToggleSelection switchs the selection state of a range of nodes, so selected nodes become unselected and vice versa. This -method is specifically designed to help selecting ranges with the keyboard and considers therefore the range anchor. - -@@TBaseVirtualTree.UnselectNodes@PVirtualNode@PVirtualNode -Summary -Deselects a range of nodes. - -Description -UnselectNodes deselects a given range of nodes. EndNode must be visible while StartNode is not required to -be so as in the case where the last focused node is the start node but it is a child of a node which has been collapsed -previously. In this case the first visible parent node is used as start node. StartNode can be nil in which case -the very first node in the tree is used. - -@@TBaseVirtualTree.UpdateHorizontalScrollBar@Boolean - - -@@TBaseVirtualTree.UpdateScrollBars@Boolean -Summary -Applies changes to the horizontal and vertical scrollbars. - -Description -UpdateScrollbars (and its counterparts for vertical and horizontal scrollbars) is the core method to set the scrollbar's -properties like range, page size etc. - -@@TBaseVirtualTree.UpdateVerticalScrollBar@Boolean - - -@@TBaseVirtualTree.UseRightToLeftReading -Summary -Helper method for right-to-left layout. - -Description -UseRightToLeftReading had to be overriden in order to overcome a limitation introduced by the VCL. The VCL only allows a -window to be in right-to-left reading order if the operating system is prepared to handle this (e.g. an arabic Windows -98). Virtual Treeview however does most of the RTL stuff handle itself, also on non-RTL system. - -@@TBaseVirtualTree.ValidateCache -Summary -Initiates the validation of the internal node cache. - -Description -If the node cache is marked as being invalid then this method puts the tree into the worker thread's list and awakes then -the thread so that the validation is performed in the background. - -See Also -InvalidateCache - -@@TBaseVirtualTree.ValidateChildren@PVirtualNode@Boolean -Summary -Validates all children of a given node. - -Description -ValidateChildren ensures that the children of the given node (and all their children, if Recursive is true) are -initialized. Node must already be initialized. If nil is passed to the method the hidden root node is used -(which makes only sense if Recursive is true, in which case the entire tree is validated). - -@@TBaseVirtualTree.ValidateNode@PVirtualNode@Boolean -Summary -Validates a given node. - -Description -ValidateNode ensures that the given node (and all its children, if Recursive is true) are initialized. If Node -is nil then the hidden root node is used (which makes only sense if Recursive is true, in which case the -entire tree is validated). - -@@TBaseVirtualTree.ValidateNodeDataSize@Integer -Summary -Helper method for node data size initalization. - -Description -ValidateNodeDataSize is called from MakeNewNode if the currently set node data size is -1, which indicates it has not yet -been determined. The method calls the event OnGetNodeDataSize allowing so the application to compute now its data -requirement. - -@@TBaseVirtualTree.WndProc@TMessage -Summary -Redirected window procedure to do some special processing. - -Description -WndProc has been overriden to allow the header to handle certain messages (which are forwarded by the tree) as well as to -do some other special handling internal to the tree. - -@@TBaseVirtualTree.WriteChunks@TStream@PVirtualNode -Summary -Writes the core chunks for the given node to the given stream. - -Description -WriteChunks is part of the streaming system in Virtual Treeview and writes the core chunks for Node into Stream. -Descentants can optionally override this method to add other node specific chunks. This streaming is used when the -tree must be saved to disk or a stream used e.g. for clipboard operations. - -Note -Keep in mind that this method is also called for the hidden root node. Using this fact in descendants you can create a -kind of "global" chunk set not directly bound to a specific node. - -See Also -WriteNode, SaveToStream - -@@TBaseVirtualTree.WriteNode@TStream@PVirtualNode -Summary -Writes the cover (envelop) chunk for the given node to the given stream. - -Description -WriteNode writes the cover chunk for Node to Stream and initiates writing child nodes and chunks. This -method is part of the streaming system used in Virtual Treeview. - - - -See Also -WriteChunks, WriteToStream - -@@TClipboardFormats.Create@TBaseVirtualTree -Summary -Constructor of the class. - -Description -Create initializes the class. - -@@TCustomStringTreeOptions -Summary -Enhanced options class for string trees. - -Description -This class enhances the base class TCustomVirtualTreeOptions by options related to a string tree. - -@@TCustomStringTreeOptions.AssignTo@TPersistent -Summary -Used to copy the options class. - -Description -You can either call this method directly or use the Assign method of the target class to do the assignment. Implementing -AssignTo instead of Assign allows for future enhancements. TPersistent will automatically call AssignTo if there was no -Assign method. - -@@TCustomStringTreeOptions.Create@TBaseVirtualTree -Summary -The constructor of the class. - -Description -The constructor initializes the class. - -@@TCustomStringTreeOptions.StringOptions -Summary -The new options introduced by the class. - -Description -StringOptions provides access to the newly introduced options by which the base class is extended. - -@@TCustomVirtualDrawTree -Summary -Simple owner draw descendant of the base tree. - -Description -TCustomVirtualDrawTree is a simple TBaseVirtualTree descendant, which publishes the paint method through an event. This -allows an application for self drawn tree views without overriding the base class. - -@@TCustomVirtualDrawTree.OnDrawHint -Summary -Triggered when a node hint or tooltip must be drawn. - -Description -Use an event handler for OnDrawHint to draw the hint or tooltip for the given node. You must implement this event and -OnGetHintSize to get a hint at all. - -@@TCustomVirtualDrawTree.DoDrawHint@TCanvas@PVirtualNode@TRect@TColumnIndex -Summary -Overridable method which triggers OnDrawHint. - -Description -You can override DoDrawHint to customize the behavior for this request. - -@@TCustomVirtualDrawTree.DoGetHintSize@PVirtualNode@TColumnIndex@TRect -Summary -Overridable method which triggers OnGetHintSize. - -Description -You can override OnGetHintSize to customize the behavior for this request. - -@@TCustomVirtualDrawTree.DoGetNodeWidth@PVirtualNode@TColumnIndex@TCanvas -Summary -Overridable method which triggers OnGetNodeWidth. - -Description -You can override OnGetNodeWidth to customize the behavior for this request. - -@@TCustomVirtualDrawTree.DoPaintNode@TVTPaintInfo -Summary -Overridable method which triggers OnPaintNode. - -Description -You can override OnPaintNode to customize the behavior for this request. - -@@TCustomVirtualDrawTree.OnDrawNode -Summary -Triggered when a node must be drawn. - -Description -Use an event handler for OnDrawNode to draw the actual content for the given node. - -@@TCustomVirtualDrawTree.OnGetHintSize -Summary -Triggered when a node hint or tooltip is about to show. - -Description -Use an event handler for OnGetHintSize to return the size of the tooltip/hint window for the given node. You must -implement this event and OnDrawHint to get a hint at all. - -@@TCustomVirtualDrawTree.OnGetNodeWidth -Summary -Triggered when a node is about to be drawn. - -Description -Use an event handler for OnGetNodeWidth to return your calculated width for the given node. Since the draw does not know -the width of a node you have to tell it yourself. - -@@TCustomVirtualStringTree -Summary -Descendant of TBaseVirtualTree, which is able to manage node captions on its own - -Description -TCustomVirtualStringTree enhances the base tree to display and edit node captions. It implements a generic node editor -which can be used as reference to build your own one. - -@@TCustomVirtualStringTree.OnGetHint -Summary -Virtual string tree event to query for a custom hint text. - -Description -Write an event handler for this event to specify a custom hint for the passed node and column. The TextType will always -be ttNormal. This event will only be fired if HintMode is not hmTooltip. The delay for hints can be set as usual: adjust -the properties HintPause and HintShortPause of the global Application object. - -@@TCustomVirtualStringTree.OnGetText -Summary -Virtual string tree event to query for a node's normal or static text. - -Description -This is one of the fundamental string tree events which must always be handled. The string tree will fire this event -every time when it needs to know about the text of a specific node and column. This is mainly the case when the node -appears in the visible area of the tree view (in other words it is not scrolled out of view) but also on some other -\occasions, including streaming, drag and drop and calculating the width of the node. - - - -The node text is distinguished between two text types: - - - - * Normal text: If TextType is ttNormal return the main node caption for the specified column. - * Static text: All text that you return when TextType is ttStatic will be displayed right beside the normal text (or - left to it if the column's BidiMode is not bdLeftToRight, i.e. the column has right-to-left layout). Static text is used - \only for informational purposes; it cannot be selected or dragged and if the column is not wide enough to show all text - it will not be shortened with an ellipsis (...) as normal text. The string tree will only query for static text if the - StringOptions (see TreeOptions) include toShowStaticText. This is off by default. - - - -When this event is fired the text parameter will always be initialized with the value of property DefaultText. To handle -the event get your node data and then extract the string for the appropriate column and TextType. - -See Also -OnPaintText - -Note -Be sure that your event handler only contains absolutely necessary code. This event will be fired very often - easily a -few hundred times for medium sized trees with some columns defined when the tree is repainted completely. -For example it is far too slow to use Locate() on some Dataset, a database query result or table, and then get the text -from some TField. This may only work with in-memory tables or a client dataset. When you initialize your node data do -some caching and use these cached values to display the data. - -@@TCustomVirtualStringTree.OnNewText -Summary -Virtual string tree event to pass edited text. - -Description -A string tree will fire this event after a node has been edited successfully (not canceled with Escape). The event -handler must store the new text in the node data. - - - -This event will only be used for the default node caption editor. Other custom node editors may or may not use this event -to pass their edited data to the application. Editing for the whole tree is only possible if the MiscOptions (see -TreeOptions) include toEditable. If only certain columns or nodes should be editable write an event handler for -OnEditing. - - - -See Also -OnCreateEditor, OnEdited - -@@TCustomVirtualStringTree.OnPaintText -Summary -Event to change text formatting for particular nodes. - -Description -Write an event handler for this event to render nodes with different fonts, font sizes, styles or colors. According to -the parameters each column of each node and even normal and static text can be painted in different ways. - -Note -The string tree view manages an internal width for each node's main column. This is done because computing this width is -quite costly and the width is needed on several occasions. If you change the font which is used to paint a node's text, -for example to bold face style, its width changes but the tree view does not know this - it still relies on its cached -node width. This may result in cut off selection rectangles among others. -Hence if the width of a node changes after its initialization because it is now formatted differently than before force a -recalculation of the node width by calling InvalidateNode (when the conditions for the changed formatting are met - not -in the event handler for OnPaintText). -See Also - - -@@TCustomVirtualStringTree.OnShortenString -Summary -String tree event for custom handling of string abbreviations. - -Description -If the text of a node does not fit into its cell (in grid mode) or is too wide for the width of the tree view it is being -abbreviated with an ellipsis (...). By default the ellipsis is added to the end of the node text. - -Occasionally you may want to shorten the node text at a different position, for example if the node text is a path string -and not the last folder or filename should be cut off but rather some mid level folders if possible. - -In the handler S must be processed (shortened) and returned in Result. If Done is set to true (default value is false) -the tree view takes over the shortening. This is useful if not all nodes or columns need - -@@TCustomVirtualStringTree.AdjustPaintCellRect@TVTPaintInfo@TColumnIndex -Summary -Method which can be used by descentants to adjust the given rectangle during a paint cycle. - -Description -For some special behaviour, like the auto span column feature, it is necessary to tell the base treeview which rectangle -is to be considered as the current paint cell when drawing the tree. ClipRect is set to a rectangle which -corresponds to the current node and the current column in the paint cycle. - -@@TCustomVirtualStringTree.ContentToHTML@TVSTTextSourceType@WideString - - -@@TCustomVirtualStringTree.ContentToRTF@TVSTTextSourceType - - -@@TCustomVirtualStringTree.ContentToText@TVSTTextSourceType@Char - - -@@TCustomVirtualStringTree.ContentToUnicode@TVSTTextSourceType@WideChar - - -@@TCustomVirtualTreeOptions -Summary -Organizes all tree options into subproperties for easier managment. - -Description -There are a lot of options available which control certain aspects of Virtual Treeview. Because there might only be at -most 32 members in a published set and also for better overview these options have been splitted into several subsets, -each related to a particular feature group like painting or node selection. With this implementation you can even derive -an own option class and modify which options should be shown in Delphi's object inspector for your class. - -@@TCustomVirtualTreeOptions.AnimationOptions -Summary -Options related to animations. - -Description -These options can be used to switch certain animation effects in a tree. - -@@TCustomVirtualTreeOptions.AutoOptions -Summary -Options related to automatic actions. - -Description -These options can be used to switch certain actions in a tree which happen automatically under certain circumstances. - -@@TCustomVirtualTreeOptions.MiscOptions -Summary -Options not related to any other category. - -Description -These options can be used to switch miscellanous aspects in a tree. - -@@TCustomVirtualTreeOptions.Owner -Summary -Owner tree to which the property class belongs. - -Description -Owner tree to which the property class belongs. - -@@TCustomVirtualTreeOptions.PaintOptions -Summary -Options related to painting. - -Description -These options can be used to switch visual aspects of a tree. - -@@TCustomVirtualTreeOptions.SelectionOptions -Summary -Options related to the way nodes can be selected. - -Description -These options can be used to switch the way how nodes can be selected in a tree. - -@@TCustomVirtualTreeOptions.AssignTo@TPersistent -Summary -Used to copy this option class to another option collection. - -Description -This is the usual method to support streaming or simply copying of this class. To stay open for future enhancements in -form of new descentants not Assign but AssignTo has been used. AssignTo is called by TPersistent if there is no Assign -method. - -@@TCustomVirtualTreeOptions.Create@TBaseVirtualTree -Summary -Constructor of the class. - -Description -Used to assign default values to all sub lists. - -@@TStringEditLink.Create -Summary -Constructor of the class. - -Description -The constructor of the edit link also creates an instance of a simple node editor control. It is by default hidden and -first displayed if the tree directs the link to do so. - -@@TStringEditLink.Destroy -Summary -Destructor of the class. - -Description -Frees the internal editor control. - -@@TVirtualTreeColumn -Summary -Represents a column in a Virtual Treeview. - -Description -This enhanced collection item, which is organized within the TCollection descentant TVirtualTreeColumns, manages all -aspects of a single column. - -@@TVirtualTreeColumns -Summary -Collection class, which holds the columns for the tree. - -Description -This class is an enhanced collection which manages general aspects of columns like ordering, traversion, streaming, -painting, dragging etc. - -@@TVirtualStringTree -Summary -Descentant of TBaseVirtualTree which is able to manage node captions on its own. - -Description -TVirtualStringTree adds no new functionality to TCustomVirtualStringTree but is publicly available version and appears in -the component palette. - -@@TBaseVirtualTree.LastClickPos -Summary -Used for retained drag start and wheel mouse scrolling. - -Description -This internal positions is made public to allow descendants to modify mainly the right click behavior of the tree -control. - -@@TBaseVirtualTree.OnGetCellIsEmpty -Summary -Triggered when the tree control needs to know whether a given column is empty. - -Description -Virtual Treeview supports the concept of column spanning where one cell with too much text to fit into its own space can -expand to the right cell neighbors if they are empty. To make this work it is necessary to know if a cell is considered -as being empty, whatever this means to an application. The string tree descendant simply checks the text for the given -cell and calls back its ancestor if there is no text to further refine if the cell must stay as if it contained -something. The ancestor (TBaseVirtualTree) now triggers OnGetCellIsEmpty to let the application decide. - - -@@TVTColors.DropMarkColor -Summary -Color of the drop mark. - -Description -Since the drop metapher has been extended to include dropping on node, above a node or below a node -(e.g. to determine adding as child, previous sibling or next sibling) there must be an indication where the node would -actually be placed when it would be dropped. This indication is the drop mark, whose color can be set via the -DropMarkColor property. - -@@TVTDataObject -Summary -Implementation of an IDataObject interface. - -Description -This class is used for OLE drag'n drop and clipboard operations. It allows not only to transfer various kinds of data -between trees but also to transfer this data between different processes. Additionally, every OLE aware application (like -Word) can take part in the data transfer. This makes it easy to copy some of the tree's content for documentation -purposes. - -@@TVTDataObject.CanonicalIUnknown@IUnknown -Summary -Helper method for setting data in the IDataObject. - -Description -In SetData the class can get a circular reference if the client calls GetData then calls SetData with the same StgMedium. -Because the unkForRelease for the IDataObject can be marshalled it is necessary to get pointers that can be correctly -compared. CanonicalIUknown uses COM object identity for this task. An explicit call to the IUnknown::QueryInterface -method, requesting the IUnknown interface, will always return the same pointer. See the IDragSourceHelper article by -Raymond Chen at MSDN. - -@@TVTDataObject.Create@TBaseVirtualTree@Boolean -Summary -Constructor of the class. - -Description -Create is used only for initialization. - -@@TVTDataObject.DAdvise@TFormatEtc@Integer@IAdviseSink@Integer -Summary -Implementation of the IDataObject.DAdvise method. - -Description -Advise sinks are used to have an opportunity for clients to get notified if something changes in the data object. -TVTDataObject uses the data advise holder APIs to provide the advise sink service. - -@@TVTDataObject.Destroy -Summary -Destructor of the class. - -Description -Cleans up the object. - -@@TVTDataObject.DUnadvise@Integer -Summary -Implementation of the IDataObject.DUnAdvise method. - -Description -DUnadvice reverses the call to DAdvise. - -@@TVTDataObject.EnumDAdvise@IEnumStatData -Summary -Implementation of the IDataObject.EnumDAdvise method. - -Description -EnumDAdvice does nothing but forwards the call to the internal advise holder class, which the responds accordingly. -That's why we use data advise holders after all. - -@@TVTDataObject.EnumFormatEtc@Integer@IEnumFormatEtc -Summary -Implementation of the IDataObject.EnumFormatEtc method. - -Description -This method creates a FormatEtc enumerator class which is used to enumerate all data formats supported by the owner tree. - -@@TVTDataObject.EqualFormatEtc@TFormatEtc@TFormatEtc -Summary -Compares two TFormatEtc structures. - -Description -\Returns true if both records are considered the same. That means if they have at least one common storage format and all -\other entries have the same values. - -@@TVTDataObject.FindFormatEtc@TFormatEtc@TFormatEtcArray -Summary -Searchs the given array for a the given format. - -Description -\Returns true if the given format is part of the array. - -@@TVTDataObject.FindInternalStgMedium@TClipFormat -Summary -\Returns a storage medium for a given clipboard format. - -Description -The class keeps an internal list of clipboard format/storage medium relations. For some operations data is set in certain -formats which is later retrieve by locating it using this method. - -@@TVTDataObject.GetCanonicalFormatEtc@TFormatEtc@TFormatEtc -Summary -Implementation of the IDataObject.GetCanonicalFormatEtc method. - -Description -The implementation of this method simply consists of a result value telling the caller to use the EnumFormatEtc method. - -@@TVTDataObject.GetData@TFormatEtc@TStgMedium -Summary -Implementation of the IDataObject.GetData method. - -Description -Whenever drag'n drop or clipboard data actually needs to be rendered then this method is called by the OLE subsystem. The -class automatically returns the CF_VTREFERENCE format and any data previously set by the SetData method (e.g. by the -Shell). For any other format the owner tree is asked to render the OLE data. - -See Also -RenderOLEData - -@@TVTDataObject.GetDataHere@TFormatEtc@TStgMedium -Summary -Implementation of the IDataObject.GetDataHere method. - -Description -GetDataHere is an alternative data retrival method to GetData, but the caller provides the storage place where to store -the actual data. Since Virtual Treeview has a very limited spectrum of what it can use this method is not fully -implmented. - -@@TVTDataObject.HGlobalClone@THandle -Summary -Helper method for SetData. - -Description -This method copies a HGlobal memory block. - -@@TVTDataObject.QueryGetData@TFormatEtc -Summary -Implementation of the IDataObject.QueryGetData method. - -Description -This method is called by OLE subsystem to determine which data formats are offered by the owner tree. It uses the -\internal clipboard format list to get a list of available and allowed formats. Currently following formats are -supported: - - - -TBaseVirtualTree - - * Virtual Treeview reference and process identifier - * native serialized tree data - - - -TCustomVirtualStringTree - - * generic Unicode text - * generic ANSI text - * HTML formatted text (UTF-8 format) - * RTF text (UTF-16 format) - * CSV (comma separated values) but with customizable separators - -@@TVTDataObject.RenderInternalOLEData@TFormatEtc@TStgMedium@HResult -Summary -Helper method to return data previously stored by SetData. - -Description -For some operations (e.g. shell transfers with IDropTargetHelper interface) data is stored in the class. -RenderInternalOLEData returns this data when queried later. - -@@TVTDataObject.SetData@TFormatEtc@TStgMedium@BOOL -Summary -Implementation of the IDataObject.SetData method. - -Description -This method is used to add or replace data in the data object. - -@@TVTDataObject.StgMediumIncRef@TStgMedium@TStgMedium@Boolean@IDataObject -Summary -Central managing method to copy OLE data. - -Description -This method is called when data must be copied from or to the data object. For each supported storage medium a different -(and appropriate) action is taken. - - -@@AlphaBlend@HDC@HDC@TRect@TPoint@TBlendMode@Integer@Integer -Summary -General purpose procedure to blend one bitmap to another. - -Description -This is an optimized alpha blend procedure using MMX instructions to perform as quick as possible. For this procedure to -work properly it is important that both source and target bitmap use the 32 bit color format (pf32Bit for TBitmap). R -describes the source rectangle to work on, while Target is the place (upper left corner) in the target bitmap -where to blend to. Note that source width + X offset must be less or equal to the target width. Similar for the height. - - - -If Mode is bmConstantAlpha then the blend operation uses the given ConstantAlpha value for all pixels. - -If Mode is bmPerPixelAlpha then each pixel is blended using its individual alpha value (the alpha value of the -source). - -If Mode is bmMasterAlpha then each pixel is blended using its individual alpha value multiplied by ConstantAlpha. - -If Mode is bmConstantAlphaAndColor then each destination pixel is blended using ConstantAlpha but also a constant -color which will be obtained from Bias. In this case no offset value is added, otherwise Bias is used as offset. - - - -Blending of a color into target only (bmConstantAlphaAndColor) ignores Source (the DC) and Target (the -position). - - - -Note -This procedure does not check whether MMX instructions are actually available! Call it only if MMX is really usable, -\otherwise a process exception for unknown op codes is thrown. - -@@DrawTextW@HDC@PWideChar@Integer@TRect@Cardinal@Boolean -Summary -Paint support procedure. - -Description -This procedure implements a subset of Window's DrawText API for Unicode which is not available for Windows 95, 98 and ME. -For a description of the parameters see DrawText in the online help. - - - -Supported flags are currently: - - * DT_LEFT - * DT_TOP - * DT_CALCRECT - * DT_NOCLIP - * DT_RTLREADING - * DT_SINGLELINE - * DT_VCENTER - -Differences to the DrawTextW Windows API: - -The additional parameter AdjustRight determines whether to adjust the right border of the given rectangle to -accomodate the largest line in the text. It has only a meaning if also DT_CALCRECT is specified. - -Note -When running on any NT windows version (Windows NT 4.0, Windows 2000., Windows XP and up) the native windows API is used -instead of this method as it also supports word wrapping properly. - -@@TreeFromNode@PVirtualNode -Summary -General purpose routine to get the tree to which a node belongs. - -Description -For obvious reasons it makes no sense to store the reference to a tree in each node record, but sometimes there might -arise the need to know to which tree a node belongs. This is not often the case but is necessary e.g. for optimized moves -in drag'n drop or cut'n paste operations. - - - -Each node contains a reference to its parent to allow fast traversal. The hidden root node, however, does not need this -reference because it does not have a node parent. Instead it contains the reference of the tree to which it belongs. To -determine which node is the root node (when you don't know its tree) a special case of sibling reference is used. Since -the root node does neither have a previous nor a next sibling the corresponding pointers are set to the root node, making -the root so pointing to itself. This case will never happen in "normal" nodes, so it is a reliable way to detect the root -node. - -@@TVTButtonFillMode -Summary -Determines how the interior of nodes buttons should be drawn. - -Description -Usually the little plus and minus buttons have just the color of the treeview but sometimes it looks better to use -another kind of painting. This is particularly important when simulating Windows XP buttons on non-XP systems. The image -below shows how the various modes look like: - - - - - -======================== - -
- -@@CFSTR_CSV -Summary -Contains the registration string for certain clipboard formats. - -Description -Some of the clipboard formats in the system, like CF_HDROP, are registered by Windows itself. For rich text, html, csv -and other data first the formats must be registered with the clipboard. The identifier returned by the registration code -is used to unregister the format later and to identify the format when transferring data or enumerating the clipboard -formats. The following formats are registered by Virtual Treeview: - - * CVS: comma separated values, a tabular data format. - * HTML: text data with text formatting and structured like a big table. Unicode is supported as well (UTF-8). - * RTF: rich text format, similar to HTML, but more complex and also a bit older. - * RTFNOOBJS: like RTF but without embedded objects (not used by Virtual Treeview). - * VIRTUALTREEVIEW: serialized treeview data. This is the native tree format and the only one directly accepted by the - control. - * VTREFERENCE: a special format to pass on a reference of the sender treeview. If both, sender and receiver, live in - the same process this reference can be used to directly access the sender treeview, without COM interecption. - -@@CFSTR_HTML - - -@@TVTDragImage.DragTo@TPoint@Boolean -Summary -Moves the drag image to a new position, which is determined from the passed point P and the previous mouse -position. - -Description -ForceRepaint is true if something on the screen changed and the back image must be refreshed. - -@@TVTDragImage.GetDragImageRect -Summary -\Returns the current size and position of the drag image (screen coordinates). - -Description -\Returns the current size and position of the drag image (screen coordinates). - -@@TVTDragImage.InternalShowDragImage@HDC -Summary -Frequently called helper routine to actually do the blend and put it onto - -Description -Frequently called helper routine to actually do the blend and put it onto the screen. Only used if the system does not -support drag images. - - -@@TVTDragImage.PrepareDrag@TBitmap@TPoint@TPoint@IDataObject -Summary -Creates all necessary structures to do alpha blended dragging using the given image. - -Description -ImagePostion and Hotspot are given in screen coordinates. The first determines where to place the drag -image while the second is the initial mouse position. This method also determines whether the system supports drag images -natively. If so then only minimal structures are created. - -@@TVTDragImage.WillMove@TPoint -Summary -Add a summary here... - -Description -This method determines whether the drag image would "physically" move when DragTo would be called with the same target -point. Always returns false if the system drag image support is available. - -@@TVTDragManager.ForceDragLeave -Summary -This method calls the drop target helper's DragLeave method to ensure it removes the drag image from screen. - -Description -This method calls the drop target helper's DragLeave method to ensure it removes the drag image from screen. - -@@TVTHeader.DragTo@TPoint -Summary -Moves the drag image to a new position, which is determined from the passed point P and the previous mouse -position. - -Description -Moves the drag image to a new position, which is determined from the passed point P and the previous mouse -position. - -@@TVTHeader.GetColumnsClass -Summary -\Returns the class to be used for the actual column implementation. - -Description -Descentants may optionally override this and return their own class. - -@@TVTHeader.HandleMessage@TMessage -Summary -General message handler for the header. - -Description -The header gets here the opportunity to handle certain messages before they reach the tree. This is important because the -tree needs to handle various non-client area messages for the header as well as some dragging/tracking events. By -returning True the message will not be handled further, otherwise the message is then dispatched to the proper message -handlers. - -@@TVTHeader.InHeader@TPoint -Summary -Determines whether the given point (client coordinates!) is within the header rectangle (non-client coordinates). - -Description -Determines whether the given point (client coordinates!) is within the header rectangle (non-client coordinates). - -@@TVTHeader.Invalidate@TVirtualTreeColumn@Boolean -Summary -Invalidates the entire header or parts of it so they are repainted. - -Description -Because the header is in the non-client area of the tree it needs some special handling in order to initiate its -repainting. If ExpandToRight is true then not only the given column but everything to its right will be -invalidated (useful for resizing). This makes only sense when a column is given. - -@@TVTHeader.LoadFromStream@TStream -Summary -Restores the state of the header from the given stream. - -Description -Restores the state of the header from the given stream. - -@@TVTHeader.PrepareDrag@TPoint@TPoint -Summary -Initializes dragging of the header, P is the current mouse postion and Start the initial mouse position. - -Description -Initializes dragging of the header, P is the current mouse postion and Start the initial mouse position. - -@@TVTHeader.RecalculateHeader -Summary -Initiate a recalculation of the non-client area of the owner tree. - -Description -Initiate a recalculation of the non-client area of the owner tree. - -@@TVTHeader.RestoreColumns -Summary -Restores all columns to their width which they had before they have been auto fitted. - -Description -Restores all columns to their width which they had before they have been auto fitted. - -@@TVTHeader.SaveToStream@TStream -Summary -Saves the complete state of the header into the provided stream. - -Description -Saves the complete state of the header into the provided stream. - -@@TVTHeader.UpdateMainColumn -Summary -Called once the load process of the owner tree is done. - -Description -Called once the load process of the owner tree is done. - - -@@RegisterVTClipboardFormat@Word@TVirtualTreeClass@Cardinal - - -@@RegisterVTClipboardFormat@string@TVirtualTreeClass@Cardinal@Integer@PDVTargetDevice@Integer@Integer -Summary -Methods to register a certain clipboard format for a given tree class. - -Description -Registration with the clipboard is done here too and the assigned ID returned by the function. tymed may contain or'ed -TYMED constants which allows to register several storage formats for one clipboard format. - - -@@CacheThreshold -Summary -Number of nodes a tree must at least have to start caching and at the same time the maximum number of nodes between two -cache entries. - -Description -Number of nodes a tree must at least have to start caching and at the same time the maximum number of nodes between two -cache entries. - -@@CFSTR_RTF - - -@@CFSTR_RTFNOOBJS - - -@@CFSTR_VIRTUALTREE - - -@@CFSTR_VTREFERENCE - - -@@ShadowSize -Summary -Size in pixels of the hint shadow. - -Description -This value has no influence on Win2K and XP systems as those OSes have native shadow support. Set it to 0 if you don't -want shadows on the other systems. - - -@@IVTEditLink -Summary -Interface which is used for communication between the treeview and a node editor. - -Description -Due to the virtual nature of the tree it is necessary to supply a kind of plug in interface for application defined node -editors. TCustomVirtualStringTree is the first class which implements a node editor. This is just a generic editor to -edit a node's caption just like TTreeview does it. Because of the lack of support under Win9x system this editor only can -edit ANSI text. You have to create an own editor to make also Unicode string editing available for node captions. - - - -All node editors must implement this interface to allow the treeview to communicate with the node editor. Node editors -are small components or forms. If a node shall be edited (for instance when the user presses F2) the treeview will fire -the event OnCreateEditor. The application must determine which node editor must be used for the data in the given node -and column. Then it creates and returns an instance of the appropriate node editor. - -The life cycle of the node editor object is handled via reference counting. This means that the application must not -destroy the node editor explicitly - this will happen automatically when the node editor is not used anymore. - -@@IVTEditLink.BeginEdit -Summary -This function will be called by the virtual tree when the editing starts. - -Description -Write code to actually display the node editor here. This might be something like Visible := True or Show. The return -value should be true if editing can start or false otherwise. Before this function is called PrepareEdit and SetBounds -are executed. - -@@IVTEditLink.CancelEdit -Summary -This function will be called by the virtual tree when the current editing is about to be cancelled. - -Description -Hide the node editor here. This might be something like Visible := False or Hide. The return value should be True if the -editing can be cancelled. Return false if the node editor is in an internal state which does not allow to cancel the -editing right now. - - - -Do not destroy the node editor instance because this will be done implicitly via reference counting. - -Note -If the edited tree is changed during this function, i.e. focus change, node deletion and so on, CancelEdit might be -called again by the tree which can lead to access violations. It is therefore advisable to block reentrancy with a -boolean variable. Example: - -function TStringEditLink.CancelEdit: Boolean; -begin - \Result := not FStopping; - if Result then - begin - FStopping := True; - FEdit.Hide; - FTree.CancelEditNode; - end; -end; - - - -@@IVTEditLink.EndEdit -Summary -This function will be called by the virtual tree when the current editing is being finished. - -Description -Hide the node editor here. This might be something like Visible := False or Hide. The return value should be true if the -editing can be finished. Return false if the node editor is in an internal state which does not allow to finish the -editing right now - possibly because there is no valid value available at the moment. If the editing can be finished -transmit the edited value to the tree or to the data structure which is displayed in the tree. - - - -Do not destroy the node editor instance because this will be done implicitly via reference counting. - -Note -If the edited tree is changed during this function, i.e. focus change, node deletion and so on, EndEdit might be called -again by the tree which can lead to access violations. It is therefore advisable to block reentrancy with a boolean -variable. Example: - - -function TStringEditLink.EndEdit: Boolean; -begin - \Result := not FStopping; - if Result then - try - FStopping := True; - if FEdit.Modified then - FTree.DoNewText(FNode, FColumn, FEdit.Caption); - FEdit.Hide; - except - FStopping := False; - raise; - end; -end; - - - - -@@IVTEditLink.GetBounds -Summary -The virtual tree can use this function to get the current bounding rect of the node editor. - -Description -The bounding rect of the node editor may change during the editing to reflect the changed edit contents. The tree uses -this function to query the current bounding rect of the editor. VCL components derived from TControl have a BoundsRect -property which can be used as a return value here. - -@@IVTEditLink.PrepareEdit@TBaseVirtualTree@PVirtualNode@TColumnIndex -Summary -This function is called by a virtual tree to initialize the node editor. - -Description -Use PrepareEdit to initialize the node editor. This includes getting the node content in the specified column which will -be needed later when the editor is shown. BeginEdit may be called anytime after this function returns. If the -initialization fails simply return false (exceptions should be trapped). - -@@IVTEditLink.ProcessMessage@TMessage -Summary -This function is used to forward messages being directed to the virtual tree. - -Description -Some node editors might need to trap some messages which are directed to the treeview window. This function remedies the -need to subclass the virtual tree via its WindowProc property. If these messages are not needed leave the function body -empty. - -@@IVTEditLink.SetBounds@TRect -Summary -The virtual tree calls this function to initialize the bounding rectangle of the node editor. - -Description -This function is usually called after PrepareEdit and before BeginEdit in order to place the node editor exactly over the -node which is about to be edited. Use the R parameter to set the bounding rect of the editor. If the treeview is in grid -mode R will be equal to the cell rectangle of the to be edited cell. Otherwise R is the bounding rectangle of the actual -node text. - -Note -SetBounds is also a method of TControl. Hence if your node editor is implemented by a descendant of TControl you must use -a method resolution clause to avoid a name clash. The clause can look similar to this: - -procedure EditLinkSetBounds(R: TRect); stdcall; -procedure IVTEditLink.SetBounds = EditLinkSetBounds; - - -@@TCheckImageKind.ckSystemDefault -System defined check images. - @@THeaderState.hsResizing -multi column resizing in progress - -@@THeaderState.hsColumnWidthTrackPending -left button is down, user might want to start resize a column - -@@THeaderState.hsColumnWidthTracking -column resizing is in progress + + + + + +@@ckButtonDisabled + + +@@ckCheckMixedDisabled + + +simple button + +@@ckEmpty + + +an empty image used as place holder radio buttons + +@@ckRadioCheckedDisabled + + +check boxes + + + + + + + + + + + + + + + + + +@@TBaseVirtualTree +TBaseVirtualTree is the main and base class for all other Virtual Treeview descendants. This class implements most of the +base features and abilities and can be used to derive new classes, which want to hide most of the details of the tree, +which other descendants like TVirtualStringTree publish. Do not use the base treeview as object. It is not meant to be +instantiated directly, instead via an descendant. + +@@TBaseVirtualTree.AdjustCoordinatesByIndent@TVTPaintInfo@Integer +During painting of the main column some coordinates must be adjusted due to the tree lines. +The offset resulting from the tree lines and indentation level is given in Indent. + +@@TBaseVirtualTree.AdjustImageBorder@TCustomImageList@TBidiMode@Integer@TRect@TVTImageInfo +Depending on the width of the image list as well as the given bidi mode R must be adjusted. + +@@TBaseVirtualTree.AdjustTotalCount@PVirtualNode@Integer@Boolean +Sets a node's total count to the given value and recursively adjusts the parent's total count +(actually, the adjustment is done iteratively to avoid function call overheads). + +@@TBaseVirtualTree.AdjustTotalHeight@PVirtualNode@Integer@Boolean +Sets a node's total height to the given value and recursively adjusts the parent's total height. + +@@TBaseVirtualTree.CalculateCacheEntryCount +Calculates the size of the position cache. + +@@TBaseVirtualTree.CalculateVerticalAlignments@Boolean@Boolean@PVirtualNode@Integer@Integer +Calculates the vertical alignment of the given node and its associated expand/collapse button during +a node paint cycle depending on the required node alignment style. + +@@TBaseVirtualTree.ChangeCheckState@PVirtualNode@TCheckState +Sets the check state of the node according to the given value and the node's check type. +If the check state must be propagated to the parent nodes and one of them refuses to change then +nothing happens and False is returned, otherwise True. + +@@TBaseVirtualTree.ClearNodeBackground@TVTPaintInfo@Boolean@Boolean@TRect +Erases a nodes background depending on what the application decides to do. +UseBackground determines whether or not to use the background picture, while Floating indicates +that R is given in coordinates of the small node bitmap or the superordinated target bitmap used in PaintTree. + +@@TBaseVirtualTree.CMDenySubclassing@TMessage +If a Windows XP Theme Manager component is used in the application it will try to subclass all controls which do not +explicitly deny this. Virtual Treeview knows how to handle XP themes so it does not need subclassing. + +@@TBaseVirtualTree.CMHintShow@TCMHintShow +Determines hint message (tooltip) and out-of-hint rect. +Note: A special handling is needed here because we cannot pass wide strings back to the caller. + I had to introduce the hint data record anyway so we can use this to pass the hint string. + We still need to set a dummy hint string in the message to make the VCL showing the hint window. + +@@TBaseVirtualTree.CMHintShowPause@TCMHintShowPause +Tells the application that the tree (and only the tree) does not want a delayed tool tip. +Normal hints / header hints use the default delay (except the first time). + +@@TBaseVirtualTree.CollectSelectedNodesLTR@Integer@Integer@Integer@TAlignment@TRect@TRect +Helper routine used when a draw selection takes place. This version handles left-to-right directionality. +In the process of adding or removing nodes the current selection is modified which requires to pack it after +the function returns. Another side effect of this method is that a temporary list of nodes will be created +(see also InternalCacheNode) which must be inserted into the current selection by the caller. + +@@TBaseVirtualTree.CollectSelectedNodesRTL@Integer@Integer@Integer@TAlignment@TRect@TRect +Helper routine used when a draw selection takes place. This version handles right-to-left directionality. +See also comments in CollectSelectedNodesLTR. + + +@@TBaseVirtualTree.DrawLineImage@TVTPaintInfo@Integer@Integer@Integer@Integer@TVTLineType@Boolean +Draws (depending on Style) one of the 5 line types of the tree. +If Reverse is True then a right-to-left column is being drawn, hence horizontal lines must be mirrored. +X and Y describe the left upper corner of the line image rectangle, while H denotes its height (and width). + +@@TBaseVirtualTree.FAlignment + + +@@TBaseVirtualTree.FAnimationDuration + + +@@TBaseVirtualTree.FAnimationType + + +@@TBaseVirtualTree.FAutoExpandDelay +amount of milliseconds to wait until a node is expanded if it is the +drop target + +@@TBaseVirtualTree.FAutoScrollDelay +amount of milliseconds to wait until autoscrolling becomes active + +@@TBaseVirtualTree.FAutoScrollInterval +determines speed of auto scrolling + +@@TBaseVirtualTree.FBackground +A background image loadable at design and runtime. + +@@TBaseVirtualTree.FButtonFillMode +for rectangular tree buttons only: how to fill them + +@@TBaseVirtualTree.FButtonStyle +style of the tree buttons + +@@TBaseVirtualTree.FChangeDelay +used to delay OnChange event + + +@@TBaseVirtualTree.FCheckImageKind +light or dark, cross marks or tick marks + +@@TBaseVirtualTree.FCheckImages +Reference to global image list to be used for the check images. + +@@TBaseVirtualTree.FCheckNode +node which "captures" an check event + +@@TBaseVirtualTree.FClipboardFormats +a list of clipboard format descriptions enabled for this tree + +@@TBaseVirtualTree.FColors +class comprising all customizable colors in the tree + +@@TBaseVirtualTree.FDefaultPasteMode +Used to determine where to add pasted nodes to. + +@@TBaseVirtualTree.FDottedBrush +used to paint dotted lines without special pens + +@@TBaseVirtualTree.FDragImage +drag image management + +@@TBaseVirtualTree.FDragImageKind +determines whether or not and what to show in the drag image + +@@TBaseVirtualTree.FDragManager +drag'n drop, cut'n paste + +@@TBaseVirtualTree.FDragOperations +determines which operations are allowed during drag'n drop + +@@TBaseVirtualTree.FDragScrollStart +Contains the start time when a tree does auto scrolling as drop target. + +@@TBaseVirtualTree.FDragSelection +temporary copy of FSelection used during drag'n drop + +@@TBaseVirtualTree.FDragThreshold +used to determine when to actually start a drag'n drop operation + +@@TBaseVirtualTree.FDragType +used to switch between OLE and VCL drag'n drop + +@@TBaseVirtualTree.FDrawSelectionMode +determines the paint mode for draw selection + +@@TBaseVirtualTree.FDrawSelShiftState +keeps the initial shift state when the user starts selection with +the mouse + +@@TBaseVirtualTree.FDropTargetNode +node currently selected as drop target + +@@TBaseVirtualTree.FEditDelay +determines time to elapse before a node goes into edit mode + +@@TBaseVirtualTree.FEditLink +used to comunicate with an application defined editor + +@@TBaseVirtualTree.FFontChanged +flag for keeping informed about font changes in the off screen buffer + +@@TBaseVirtualTree.FHeaderRect +Space which the header currently uses in the control (window coords). + +@@TBaseVirtualTree.FHintData +used while preparing the hint window + +@@TBaseVirtualTree.FHintMode +determines the kind of the hint window + +@@TBaseVirtualTree.FHotCursor +can be set to additionally indicate the current hot node + +@@TBaseVirtualTree.FIncrementalSearch +Used to determine whether and how incremental search is to be used. + +@@TBaseVirtualTree.FindInPositionCache@Cardinal@Cardinal +Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal +to the given vertical position. +The returned node does not necessarily occupy the given position but is the nearest one to start +iterating from to approach the real node for a given position. CurrentPos receives the actual position of the found +node which is needed for further iteration. + +@@TBaseVirtualTree.FindInPositionCache@PVirtualNode@Cardinal +Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal +to the position of the given node. + +@@TBaseVirtualTree.FLastClickPos +Used for retained drag start and wheel mouse scrolling. + +@@TBaseVirtualTree.FLastDropMode +set while dragging and used to track changes + +@@TBaseVirtualTree.FLastHintRect +Area which the must must leave to reshow a hint. + +@@TBaseVirtualTree.FLastSearchNode +Reference to node which was last found as search fit. + +@@TBaseVirtualTree.FLastSelectionLevel +keeps the last node level for constrained multiselection + +@@TBaseVirtualTree.FLastStructureChangeReason +used for delayed structur change event + +@@TBaseVirtualTree.FLastVCLDragTarget +A node cache for VCL drag'n drop (keywords: DragLeave on DragDrop). + +@@TBaseVirtualTree.FLineMode +tree lines or bands etc. + +@@TBaseVirtualTree.FLineStyle +style of the tree lines + +@@TBaseVirtualTree.FMargin +horizontal border distance + +@@TBaseVirtualTree.FNodeAlignment +determines how to interpret the align member of a node + +@@TBaseVirtualTree.FNodeDataSize +number of bytes to allocate with each node (in addition to its base +structure and the internal data), if -1 then do callback + + +@@TBaseVirtualTree.FOldFontChange +helper method pointer for tracking font changes in the off screen buffer + +@@TBaseVirtualTree.FOnAdvancedHeaderDraw +Used when owner draw is enabled for the header and a column +is set to owner draw mode. But only if OnHeaderDrawQueryElements +returns at least one element to be drawn by the application. +In this case OnHeaderDraw is not used. + +@@TBaseVirtualTree.FOnAfterCellPaint +triggered after a column of an item has been painted + +@@TBaseVirtualTree.FOnAfterItemErase +triggered after an item's background has been erased + +@@TBaseVirtualTree.FOnAfterItemPaint +triggered after an item has been painted + +@@TBaseVirtualTree.FOnBeforeCellPaint +triggered when a column of an item is about to be painted + +@@TBaseVirtualTree.FOnBeforeItemErase +triggered when an item's background is about to be erased + +@@TBaseVirtualTree.FOnBeforeItemPaint +triggered when an item is about to be painted + +@@TBaseVirtualTree.FOnChange +selection change + +@@TBaseVirtualTree.FOnChecking +called just before a node's check state is changed + +@@TBaseVirtualTree.FOnCompareNodes +used during sort + +@@TBaseVirtualTree.FOnCreateDataObject +called to allow for app./descentant defined data objects + +@@TBaseVirtualTree.FOnCreateDragManager +called to allow for app./descendant defined drag managers + +@@TBaseVirtualTree.FOnCreateEditor +called when a node goes into edit mode, this allows applications +to supply their own editor + +@@TBaseVirtualTree.FOnDragAllowed +used to get permission for manual drag in mouse down + +@@TBaseVirtualTree.FOnDragDrop +called on release of mouse button (if drop was allowed) + +@@TBaseVirtualTree.FOnDragOver +called for every mouse move + +@@TBaseVirtualTree.FOnEditCancelled +called when editing has been cancelled + +@@TBaseVirtualTree.FOnEdited +called when editing has successfully been finished + +@@TBaseVirtualTree.FOnEditing +called just before a node goes into edit mode + +@@TBaseVirtualTree.FOnFocusChanged +called when the focus goes to a new node and/or column + +@@TBaseVirtualTree.FOnFocusChanging +called when the focus is about to go to a new node and/or column +(can be cancelled) + +@@TBaseVirtualTree.FOnFreeNode +called when a node is about to be destroyed, user data can and should +be freed in this event + +@@TBaseVirtualTree.FOnGetCursor +called to allow the app. to set individual cursors + +@@TBaseVirtualTree.FOnGetHeaderCursor +triggered to allow the app. to use customized cursors for the header + +@@TBaseVirtualTree.FOnGetHelpContext +called when a node specific help theme should be called + +@@TBaseVirtualTree.FOnGetImage +used to retrieve the image index of a given node + +@@TBaseVirtualTree.FOnGetLineStyle +triggered when a custom line style is used and the pattern brush +needs to be build + +@@TBaseVirtualTree.FOnGetNodeDataSize +called if NodeDataSize is -1 + +@@TBaseVirtualTree.FOnGetPopupMenu +called when the popup for a node needs to be shown + +@@TBaseVirtualTree.FOnGetUserClipboardFormats +gives application/descentants the opportunity to +add own clipboard formats on the fly + +@@TBaseVirtualTree.FOnHeaderDragged +header (column) drag'n drop + +@@TBaseVirtualTree.FOnHeaderDraggedOut +header (column) drag'n drop, which did not result in a valid drop. + +@@TBaseVirtualTree.FOnHeaderDragging +header (column) drag'n drop + +@@TBaseVirtualTree.FOnHeaderDraw +Used when owner draw is enabled for the header and a column is set +to owner draw mode. + +@@TBaseVirtualTree.FOnHeaderDrawQueryElements +Used for advanced header painting to query the +application for the elements, which are drawn by it and which should +be drawn by the tree. + +@@TBaseVirtualTree.FOnHotChange +called when the current "hot" node (that is, the node under the mouse) +changes and hot tracking is enabled + +@@TBaseVirtualTree.FOnIncrementalSearch +triggered on every key press (not key down) + +@@TBaseVirtualTree.FOnInitChildren +called when a node's children are needed (expanding etc.) + +@@TBaseVirtualTree.FOnInitNode +called when a node needs to be initialized (child count etc.) + +@@TBaseVirtualTree.FOnKeyAction +used to selectively prevent key actions (full expand on Ctrl+'+' etc.) + +@@TBaseVirtualTree.FOnMeasureItem +Triggered when a node is about to be drawn and its height was not yet +determined by the application. + +@@TBaseVirtualTree.FOnNodeCopied +call after a node has been copied + +@@TBaseVirtualTree.FOnNodeCopying +called when an node is copied to another parent node (probably in +another tree, but within the same application, can be cancelled) + +@@TBaseVirtualTree.FOnNodeMoved +called after a node and its children have been moved to another +parent node (probably another tree, but within the same application) + +@@TBaseVirtualTree.FOnNodeMoving +called just before a node is moved from one parent node to another +(this can be cancelled) + +@@TBaseVirtualTree.FOnPaintBackground +triggered if a part of the tree's background must be erased which is +not covered by any node + +@@TBaseVirtualTree.FOnRenderOLEData +application/descendant defined clipboard formats + +@@TBaseVirtualTree.FOnResetNode +called when a node is set to be uninitialized + +@@TBaseVirtualTree.FOnScroll +called when one or both paint offsets changed + +@@TBaseVirtualTree.FOnStateChange +Called whenever a state in the tree changes. + +@@TBaseVirtualTree.FOnStructureChange +structural change like adding nodes etc. + +@@TBaseVirtualTree.FOnUpdating +called from BeginUpdate, EndUpdate, BeginSynch and EndSynch + +@@TBaseVirtualTree.FPanningCursor +Current wheel panning cursor. + +@@TBaseVirtualTree.FPanningImage +A little 32x32 bitmap to indicate the panning reference point. + +@@TBaseVirtualTree.FPanningWindow +Helper window for wheel panning + +@@TBaseVirtualTree.FPendingCheckState +the new state the check node will get if all wents fine + +@@TBaseVirtualTree.FPositionCache +array which stores node references ordered by vertical positions +(see also DoValidateCache for more information) + +@@TBaseVirtualTree.FRangeAnchor +anchor node for selection with the keyboard, determines start of a +selection range + +@@TBaseVirtualTree.FScrollBarOptions +common properties of horizontal and vertical scrollbar + +@@TBaseVirtualTree.FScrollDirections +directions to scroll client area into depending on mouse position + +@@TBaseVirtualTree.FSearchBuffer +Collects a sequence of keypresses used to do incremental searching. + +@@TBaseVirtualTree.FSearchDirection +Direction to incrementally search the tree. + +@@TBaseVirtualTree.FSearchStart +Where to start iteration on each key press. + +@@TBaseVirtualTree.FSearchTimeout +Number of milliseconds after which to stop incremental searching. + +@@TBaseVirtualTree.FSelection +list of currently selected nodes + +@@TBaseVirtualTree.FSelectionBlendFactor +Determines the factor by which the selection rectangle is to be +faded if enabled. + +@@TBaseVirtualTree.FSelectionCount +number of currently selected nodes (size of FSelection might differ) + +@@TBaseVirtualTree.FSelectionCurveRadius +radius for rounded selection rectangles + +@@TBaseVirtualTree.FSingletonNodeArray +Contains only one element for quick addition of single nodes +to the selection. + +@@TBaseVirtualTree.FStartIndex +index to start validating cache from + +@@TBaseVirtualTree.FStates +various active/pending states the tree needs to consider + +@@TBaseVirtualTree.FSynchUpdateCount +synchronizer, causes all events which are usually done via timers +to happen immediately, regardless of the normal update state + +@@TBaseVirtualTree.FTempNodeCache +used at various places to hold temporarily a bunch of node refs. + +@@TBaseVirtualTree.FTempNodeCount +number of nodes in FTempNodeCache + +@@TBaseVirtualTree.FTextMargin +space between the node's text and its horizontal bounds + +@@TBaseVirtualTree.FTotalInternalDataSize +Cache of the sum of the necessary internal data size for all tree +classes derived from this base class. + +@@TBaseVirtualTree.FUpdateCount +update stopper, updates of the tree control are only done if = 0 + +@@TBaseVirtualTree.FVCLDragEffect +A cache for VCL drag'n drop to keep the current drop effect. + +@@TBaseVirtualTree.FVisibleCount +number of currently visible nodes + +@@TBaseVirtualTree.FWantTabs +If True then the tree also consumes the tab key. + +@@TBaseVirtualTree.GetDragManager +Returns the internal drag manager interface. If this does not yet exist then it is created here. + +@@TBaseVirtualTree.GetFullyVisible@PVirtualNode +Determines whether the given node has the visibility flag set as well as all its parents are expanded. + +@@TBaseVirtualTree.GetVisible@PVirtualNode +Determines if the given node marked as being visible. + +@@TBaseVirtualTree.GetVisiblePath@PVirtualNode +Determines if all parents of the given node are expanded and have the visibility flag set. + + +@@TBaseVirtualTree.HandleDrawSelection@Integer@Integer +Handles multi-selection with a focus rectangle. +Result is True if something changed in selection. + +@@TBaseVirtualTree.HasVisibleNextSibling@PVirtualNode +Helper method to determine if the given node has a visible sibling. This is needed to +draw correct tree lines. + +@@TBaseVirtualTree.InitializeFirstColumnValues@TVTPaintInfo +Determines initial index, position and cell size of the first visible column. + + +@@TBaseVirtualTree.InitRootNode@Cardinal +Reinitializes the root node. + +@@TBaseVirtualTree.InterruptValidation +Waits until the worker thread has stopped validating the caches of this tree. + +@@TBaseVirtualTree.IsFirstVisibleChild@PVirtualNode@PVirtualNode +Helper method to check if Node is the same as the first visible child of Parent. + +@@TBaseVirtualTree.IsLastVisibleChild@PVirtualNode@PVirtualNode +Helper method to check if Node is the same as the last visible child of Parent. + + +@@TBaseVirtualTree.PrepareBitmaps@Boolean@Boolean +initializes the contents of the internal bitmaps + +@@TBaseVirtualTree.PrepareCell@TVTPaintInfo@Integer@Integer +This method is called immediately before a cell's content is drawn und is responsible to paint selection colors etc. + +@@TBaseVirtualTree.ReadOldOptions@TReader +Migration helper routine to silently convert forms containing the old tree options member into the new +sub-options structure. + +@@TBaseVirtualTree.SetChildCount@PVirtualNode@Cardinal +Changes a node's child structure to accomodate the new child count. This is used to add or delete +child nodes to/from the end of the node's child list. To insert or delete a specific node a separate +routine is used. + +@@TBaseVirtualTree.SetFullyVisible@PVirtualNode@Boolean +This method ensures that a node is visible and all its parent nodes are expanded and also visible +if Value is True. Otherwise the visibility flag of the node is reset but the expand state +of the parent nodes stays untouched. + +@@TBaseVirtualTree.SetVisible@PVirtualNode@Boolean +Sets the visibility style of the given node according to Value. + +@@TBaseVirtualTree.SetVisiblePath@PVirtualNode@Boolean +If Value is True then all parent nodes of Node are expanded. + +@@TBaseVirtualTree.TileBackground@TBitmap@TCanvas@TPoint@TRect +Draws the given source graphic so that it tiles into the given rectangle which is relative to the target bitmap. +The graphic is aligned so that it always starts at the upper left corner of the target canvas. +Offset gives the position of the target window in an possible superordinated surface. + +@@TBaseVirtualTree.WMContextMenu@TWMContextMenu +This method is called when a popup menu is about to be displayed. +We have to cancel some pending states here to avoid interferences. + +@@TBaseVirtualTree.WMHScroll@TWMHScroll +local functions + +@@TBaseVirtualTree.WMKeyDown@TWMKeyDown +Keyboard event handling for node focus, selection, node specific popup menus and help invokation. +For a detailed description of every action done here read the help. + + +@@TBlendMode.bmConstantAlpha +apply given constant alpha + +@@TBlendMode.bmConstantAlphaAndColor +blend the destination color with the given constant color und the constant alpha value + +@@TBlendMode.bmMasterAlpha +use alpha value of source pixel and multiply it with the constant alpha value + +@@TBlendMode.bmPerPixelAlpha +use alpha value of the source pixel + +@@TChangeReason.crAccumulated +used for delayed changes + +@@TChangeReason.crChildAdded +one or more child nodes have been added + +@@TChangeReason.crChildDeleted +one or more child nodes have been deleted + +@@TChangeReason.crIgnore +used as placeholder + +@@TChangeReason.crNodeAdded +a node has been added + +@@TChangeReason.crNodeCopied +a node has been duplicated + +@@TChangeReason.crNodeMoved +a node has been moved to a new place + + +@@TCheckImageKind +Summary +Determines which images should be used for checkboxes and radio buttons. + +Description +Provided with the tree are nine different image sets for the check images used when toCheckSupport is enabled in +TreeOptions. + + +
+ + +Eight of the nine lists are predefined while one is a custom check image list, which can be filled by the application. +Use ckCustom as CheckImageKind value and assign an image list to the CustomCheckImages property to enable custom images. + + + +The order of the images in the image lists is always as listed below. Make sure you have the same amount of images in +your custom image list, if you want own check images. + + + + * empty image (ckEmpty) + +Radio buttons: + + * uncheck normal (ckRadioUncheckedNormal) + * unchecked hot (ckRadioUncheckedHot) + * unchecked pressed (ckRadioUncheckedPressed) + * unchecked disabled (ckRadioUncheckedDisabled) + * checked normal (ckRadioCheckedNormal) + * checked hot (ckRadioCheckedHot) + * checked pressed (ckRadioCheckedPressed) + * checked disabled (ckRadioCheckedDisabled) + +Check boxes: + + * unchecked normal (ckCheckUncheckedNormal) + * unchecked hot (ckCheckUncheckedHot) + * unchecked pressed (ckCheckUncheckedPressed) + * unchecked disabled (ckCheckUncheckedDisabled) + * checked normal (ckCheckCheckedNormal) + * checked hot (ckCheckCheckedHot) + * checked pressed (ckCheckCheckedPressed) + * checked disabled (ckCheckCheckedDisabled) + * mixed normal (ckCheckMixedNormal) + * mixed hot (ckCheckMixedHot) + * mixed pressed (ckCheckMixedPressed) + * mixed disabled (ckCheckMixedDisabled) + +Node buttons: + + * button normal (ckButtonNormal) + * button hot (ckButtonHot) + * button pressed (ckButtonPressed) + * button disabled (ckButtonDisabled) + +@@TCheckImageKind.ckCustom +application defined check images + +@@TCheckImageKind.ckDarkCheck +black cross + +@@TCheckImageKind.ckDarkTick +black tick mark + +@@TCheckImageKind.ckFlat +flat images (no 3D border) + +@@TCheckImageKind.ckLightCheck +gray cross + +@@TCheckImageKind.ckLightTick +gray tick mark + + +@@TCheckImageKind.ckSystemFlat +Flat system defined check images. + +@@TCheckImageKind.ckXP +Windows XP style + +@@TCheckState.csCheckedNormal +checked and not pressed + +@@TCheckState.csCheckedPressed +checked and pressed + +@@TCheckState.csMixedNormal +3-state check box and not pressed + +@@TCheckState.csMixedPressed +3-state check box and pressed + +@@TCheckState.csUncheckedNormal +unchecked and not pressed + +@@TCheckState.csUncheckedPressed +unchecked and pressed + + +@@TClipboardFormatList.Add@string@TVirtualTreeClass@Cardinal@TFormatEtc +Adds the given data to the internal list. The priority value is used to sort formats for importance. Larger priority +values mean less priority. + +@@TClipboardFormatList.EnumerateFormats@TVirtualTreeClass@TFormatEtcArray@TClipboardFormats +Returns a list of format records for the given class. If assigned the AllowedFormats is used to limit the +enumerated formats to those described in the list. + +@@TClipboardFormatList.EnumerateFormats@TVirtualTreeClass@TStrings +Returns a list of format descriptions for the given class. + +@@TClipboardFormatList.Sort +local function + +@@TClipboardFormatListEntry.Description +The string used to register the format with Windows. + +@@TClipboardFormatListEntry.FormatEtc +The definition of the format in the IDataObject. + +@@TClipboardFormatListEntry.Priority +Number which determines the order of formats used in IDataObject. + +@@TClipboardFormatListEntry.TreeClass +The tree class which supports rendering this format. + +@@TClipboardFormats +Summary +List of strings describing clipboard formats. + + + +Description +This class is an extended string list which allows to enter description strings for clipboard formats which are checked +agains registered formats and only accepted if the particular format could be found. This way there is an unambiqious and +portable description of allowed clipboard formats possible. + +@@TClipboardFormats.Add@string +Summary +Adds a new format to the internal list. + + + +Description +Adds or inserts a new format to the internal list but restricts additions to the clipbard formats to only those which are +registered with the owner tree or one of its ancestors. + +@@TClipboardFormats.Insert@Integer@string + + + +@@TCustomVirtualStringTree.FDefaultText +text to show if there's no OnGetText event handler (e.g. at design time) + +@@TCustomVirtualStringTree.FEllipsisWidth +width of '...' for the current font + +@@TCustomVirtualStringTree.FInternalDataOffset +offset to the internal data of the string tree + +@@TCustomVirtualStringTree.FOnNewText +used to notify the application about an edited node caption + +@@TCustomVirtualStringTree.FOnPaintText +triggered before either normal or fixed text is painted to allow +even finer customization (kind of sub cell painting) + +@@TCustomVirtualStringTree.FOnShortenString +used to allow the application a customized string shortage + +@@TCustomVirtualStringTree.FTextHeight +true size of the font + + +@@TEnumFormatEtc + + + + + +@@THeaderState.hsAutoSizing +auto size chain is in progess, do not trigger again on WM_SIZE + +@@THeaderState.hsDragging +header dragging is in progress (only if enabled) + +@@THeaderState.hsDragPending +left button is down, user might want to start dragging a column + +@@THeaderState.hsLoading +The header currently loads from stream, so updates are not necessary. + + + +@@THintAnimationType.hatFade +fade in the hint/tooltip, like in Windows 2000 + +@@THintAnimationType.hatNone +no animation at all, just display hint/tooltip + +@@THintAnimationType.hatSlide +slide in the hint/tooltip, like in Windows 98 + +@@THintAnimationType.hatSystemDefault +use what the system is using (slide for Win9x, slide/fade for Win2K+, depends on settings) + + +@@THitPosition.hiAbove +above the client area (if relative) or the absolute tree area + +@@THitPosition.hiBelow +below the client area (if relative) or the absolute tree area + +@@THitPosition.hiNowhere +no node is involved (possible only if the tree is not as tall as the client area) + +@@THitPosition.hiOnItem +on the bitmaps/buttons or label associated with an item + +@@THitPosition.hiOnItemButton +on the button associated with an item + +@@THitPosition.hiOnItemCheckbox +on the checkbox if enabled + +@@THitPosition.hiOnItemIndent +in the indentation area in front of a node + +@@THitPosition.hiOnItemLabel +on the normal text area associated with an item + +@@THitPosition.hiOnItemLeft +in the area to the left of a node's text area (e.g. when right aligned or centered) + +@@THitPosition.hiOnItemRight +in the area to the right of a node's text area (e.g. if left aligned or centered) + +@@THitPosition.hiOnNormalIcon +on the "normal" image + +@@THitPosition.hiOnStateIcon +on the state image + +@@THitPosition.hiToLeft +to the left of the client area (if relative) or the absolute tree area + +@@THitPosition.hiToRight +to the right of the client area (if relative) or the absolute tree area + + +@@TItemEraseAction.eaColor +Use the provided color to erase the background instead the one of the tree. + +@@TItemEraseAction.eaDefault +The tree should erase the item's background (bitmap or solid). + +@@TItemEraseAction.eaNone +Do nothing. Let the application paint the background. + + + +@@TScrollBarOptions + + +@@TScrollBarOptions.FScrollBars +used to hide or show vertical and/or horizontal scrollbar + +@@TScrollBarOptions.FScrollBarStyle +kind of scrollbars to use + + +@@TStringEditLink +Summary +TStringEditLink is the standard node editor of a TVirtualStringTree. + +Description +TStringEditLink implements the interface IVTEditLink. This is a simple node editor which wraps a TEdit and is not Unicode +aware. A virtual string tree will use this node editor if the event OnCreateEditor is not handled and a node must be +edited. After the node's text has been edited the event OnNewText will be fired and the application should replace the +\old text with the new and edited text. + + + +The node editor instance will automatically be destroyed via reference counting when it is not needed anymore. Never +destroy it explicitly - except when you know what you are doing. + + + +Remarks +If you want to modify some aspects of how the node editor works, i.e. suppress some characters or initialize it with a +different text but the node's text, you can inherit your own class from TStringEditLink and return an instance of it in +the OnCreateEditor event. + +@@TStringEditLink.BeginEdit +Summary +This function will be called by the virtual string tree when the editing starts. + +Description +Please see interface IVTEditLink for a detailed explanation of this interface function. + +@@TStringEditLink.CancelEdit +Summary +This function will be called by the virtual string tree when the current editing is about to be cancelled. + +Description +Please see interface IVTEditLink for a detailed explanation of this interface function. + +@@TStringEditLink.EndEdit +Summary +This function will be called by the virtual string tree when the current editing is being finished. + +Description +Please see interface IVTEditLink for a detailed explanation of this interface function. + +@@TStringEditLink.FColumn +The column of the node. + +@@TStringEditLink.FEdit +A normal custom edit control. + +@@TStringEditLink.FNode +The node to be edited. + +@@TStringEditLink.FStopping +Set to True when the edit link requests stopping the edit action. + +@@TStringEditLink.FTextBounds +Smallest rectangle around the text. + +@@TStringEditLink.FTree +A back reference to the tree calling. + +@@TStringEditLink.GetBounds +Summary +The virtual string tree uses this function to get the current bounding rect of the node editor. + +Description +Please see interface IVTEditLink for a detailed explanation of this interface function. + +@@TStringEditLink.PrepareEdit@TBaseVirtualTree@PVirtualNode@TColumnIndex +Summary +This function is called by a virtual string tree to initialize the node editor. + +Description +Please see interface IVTEditLink for a detailed explanation of this interface function. + +@@TStringEditLink.ProcessMessage@TMessage +Summary +This function is used to forward messages being directed to the virtual string tree. + +Description +Please see interface IVTEditLink for a detailed explanation of this interface function. + +@@TStringEditLink.SetBounds@TRect +Summary +The virtual string tree calls this function to initialize the bounding rect of the node editor. + +Description +Please see interface IVTEditLink for a detailed explanation of this interface function. + +@@TStringTreeOptions +Summary +Options class used in the string tree and its descentants. + +Description +This options class publishes all properties inherited from its ancestor and does not add any further functionality. + +@@TToggleAnimationData.Brush +the brush to be used to erase uncovered parts + +@@TToggleAnimationData.DC +the DC of the window to erase unconvered parts + + +@@TToggleAnimationData.Window +copy of the tree's window handle + +@@TVirtualDrawTree +Summary +Descendant of TBaseVirtualTree, which passes node paint events through to the application (similar to a draw grid) + +Description +This tree implementation enhances the base tree to allow the application to draw its own stuff into the tree window. + + +@@TVirtualNode.Align +line/button alignment + +@@TVirtualNode.CheckState +indicates the current check state (e.g. checked, pressed etc.) + +@@TVirtualNode.CheckType +indicates which check type shall be used for this node + +@@TVirtualNode.Data +this is a placeholder, each node gets extra data determined by NodeDataSize + +@@TVirtualNode.Dummy +dummy value to fill DWORD boundary + +@@TVirtualNode.NodeHeight +height in pixels + +@@TVirtualNode.States +states describing various properties of the node (expanded, initialized etc.) + +@@TVirtualNodeState.vsAllChildrenHidden +Set if vsHasChildren is set and no child node has the vsVisible flag set. + +@@TVirtualNodeState.vsChecking +Node's check state is changing, avoid propagation. + +@@TVirtualNodeState.vsClearing +A node's children are being deleted. Don't register structure change event. + +@@TVirtualNodeState.vsCutOrCopy +Node is selected as cut or copy and paste source. + +@@TVirtualNodeState.vsDeleting +Set when the node is about to be freed. + +@@TVirtualNodeState.vsDisabled +Set if node is disabled. + +@@TVirtualNodeState.vsExpanded +Set if the node is expanded. + +@@TVirtualNodeState.vsHasChildren +Indicates the presence of child nodes without actually setting them. + +@@TVirtualNodeState.vsHeightMeasured +Node height has been determined and does not need a recalculation. + +@@TVirtualNodeState.vsInitialized +Set after the node has been initialized. + +@@TVirtualNodeState.vsInitialUserData +Set if (via AddChild or InsertNode) initial user data has been set which requires OnFreeNode. + +@@TVirtualNodeState.vsMultiline +Node text is wrapped at the cell boundaries instead of being shorted. + +@@TVirtualNodeState.vsSelected +Set if the node is in the current selection. + +@@TVirtualNodeState.vsVisible +Indicate whether the node is visible or not (independant of the expand states of its parents). + + +@@TVirtualTreeColumn.ComputeHeaderLayout@HDC@TRect@Boolean@Boolean@TPoint@TPoint@TRect +Description +The layout of a column header is determined by a lot of factors. This method takes them all into account and determines +all necessary positions and bounds: + + * for the header text + * the header glyph + * the sort glyph + +Summary +Calculates the layout of a column header. + +@@TVirtualTreeColumn.GetRect +Summary +\Returns the rectangle this column occupies in the header (relative to (0, 0) of the non-client area). + +Description +\Returns the rectangle this column occupies in the header (relative to (0, 0) of the non-client area). + + +@@TVirtualTreeColumns.AdjustAutoSize@TColumnIndex@Boolean +Description +Called only if the header is in auto-size mode which means a column needs to be so large that it fills all the horizontal +space not occupied by the other columns. CurrentIndex (if not InvalidColumn) describes which column has just been +resized. + +Summary +Called when columns must be sized so that the fit the client area. + +@@TVirtualTreeColumns.AdjustDownColumn@TPoint +Description +If this column is allowed to be clicked then it is also kept for later use. + +Summary +Determines the column from the given position and returns it. + +@@TVirtualTreeColumns.AdjustHoverColumn@TPoint +Summary +Determines the new hover column index and returns true if the index actually changed else False. + +Description +Determines the new hover column index and returns true if the index actually changed else False. + +@@TVirtualTreeColumns.AdjustPosition@TVirtualTreeColumn@Cardinal +Summary +Reorders the column position array so that the given column gets the given position. + +Description +Reorders the column position array so that the given column gets the given position. + +@@TVirtualTreeColumns.AnimatedResize@TColumnIndex@Integer +Summary +Resizes the given column animated by scrolling the window DC. + +Description +Resizes the given column animated by scrolling the window DC. + +@@TVirtualTreeColumns.ColumnFromPosition@TColumnPosition +Summary +\Returns the index of the column at the given position. + +Description +\Returns the index of the column at the given position. + +@@TVirtualTreeColumns.ColumnFromPosition@TPoint@Boolean +Summary +Determines the current column based on the position passed in P. + +Description +Determines the current column based on the position passed in P. + +@@TVirtualTreeColumns.DrawXPButton@HDC@TRect@Boolean@Boolean@Boolean +Summary +Helper procedure to draw an Windows XP like header button. + +Description +Helper procedure to draw an Windows XP like header button. + +@@TVirtualTreeColumns.Equals@TVirtualTreeColumns +Summary +Compares itself with the given set of columns. + +Description +Equals returns true if all published properties are the same (including column order), otherwise false is returned. + +@@TVirtualTreeColumns.FClearing +True if columns are being deleted entirely. + +@@TVirtualTreeColumns.FClickIndex +last clicked column + +@@TVirtualTreeColumns.FDragIndex +index of column currently being dragged + +@@TVirtualTreeColumns.FDropBefore +True if drop position is in the left half of a column, False for the right +side to drop the dragged column to + +@@TVirtualTreeColumns.FDropTarget +current target column (index) while dragging + +@@TVirtualTreeColumns.FHeaderBitmap +backbuffer for drawing + +@@TVirtualTreeColumns.FixPositions +Summary +Fixes column positions after loading from DFM. + +Description +Fixes column positions after loading from DFM. + +@@TVirtualTreeColumns.FNeedPositionsFix +True if FixPositions must still be called after DFM loading. + +@@TVirtualTreeColumns.GetColumnAndBounds@TPoint@Integer@Integer@Boolean +Summary +\Returns the column where the mouse is currently in as well as the left and right bound of this column. + +Description +Left and Right are undetermined if no column is involved. + +@@TVirtualTreeColumns.GetColumnBounds@TColumnIndex@Integer@Integer +Summary +\Returns the left and right bound of the given column. + +Description +If Column is NoColumn then the entire client width is returned. + +@@TVirtualTreeColumns.GetFirstVisibleColumn +Summary +\Returns the index of the first visible column or "InvalidColumn" if either no columns are defined or all columns are +hidden. + +Description +\Returns the index of the first visible column or "InvalidColumn" if either no columns are defined or all columns are +hidden. + +@@TVirtualTreeColumns.GetLastVisibleColumn +Summary +\Returns the index of the last visible column or "InvalidColumn" if either no columns are defined or all columns are +hidden. + + + +Description +\Returns the index of the last visible column or "InvalidColumn" if either no columns are defined or all columns are +hidden. + +@@TVirtualTreeColumns.GetNextColumn@TColumnIndex +Summary +\Returns the next column in display order. Column is the index of an item in the collection (a column). + +Description +\Returns the next column in display order. Column is the index of an item in the collection (a column). + +@@TVirtualTreeColumns.GetNextVisibleColumn@TColumnIndex +Summary +\Returns the next visible column in display order, Column is an index into the columns list. + + + +Description +\Returns the next visible column in display order, Column is an index into the columns list. + +@@TVirtualTreeColumns.GetPreviousColumn@TColumnIndex +Summary +\Returns the previous column in display order, Column is an index into the columns list. + +Description +\Returns the previous column in display order, Column is an index into the columns list. + +@@TVirtualTreeColumns.GetPreviousVisibleColumn@TColumnIndex +Summary +\Returns the previous column in display order, Column is an index into the columns list. + +Description +\Returns the previous column in display order, Column is an index into the columns list. + +@@TVirtualTreeColumns.GetVisibleColumns +Summary +\Returns a list of all currently visible columns in actual order. + +Description +\Returns a list of all currently visible columns in actual order. + +@@TVirtualTreeColumns.HandleClick@TPoint@TMouseButton@Boolean@Boolean +Summary +Generates a click event if the mouse button has been released over the same column it was pressed first. + +Description +Alternatively, Force might be set to true to indicate that the down index does not matter (right, middle and +double click). + +@@TVirtualTreeColumns.IndexChanged@Integer@Integer +Summary +Called by a column when its index in the collection changes. + +Description +If NewIndex is -1 then the column is about to be removed otherwise it is moved to a new index. The method will +then update the position array to reflect the change. + +@@TVirtualTreeColumns.InitializePositionArray +Summary +Ensures that the column position array contains as much entries as columns are defined. + +Description +The array is resized and initialized with default values if needed. + +@@TVirtualTreeColumns.IsValidColumn@TColumnIndex +Summary +Determines whether the given column is valid or not, that is, whether it is one of the current columns. + +Description +Determines whether the given column is valid or not, that is, whether it is one of the current columns. + + +@@TVirtualTreeColumns.UpdatePositions@Boolean +Summary +Recalculates the left border of every column and updates their position property according to the PostionToIndex array, +which primarily determines where each column is placed visually. + +@@TVirtualTreeHintWindow +Summary +Internally used hint window class to support Unicode hints. + +Description +TVirtualTreeHintWindow replaces Delphi's own hint window, but only for the tree controls. For the rest of the application +the hint stays at it is. This means not the global HintWindowClass variable is changed but only the locally used class by +properly responding to CM_HINTSHOW. + +@@TVirtualTreeHintWindow.InternalPaint@Integer@Integer +local functions + +@@TVirtualTreeHintWindow.IsHintMsg@TMsg +The VCL is a bit too generous when telling that an existing hint can be cancelled. Need to specify further here. + +@@TVirtualTreeHintWindow.WMEraseBkgnd@TWMEraseBkgnd +The control is fully painted by own code so don't erase its background as this causes flickering. + +@@TVirtualTreeHintWindow.WMNCPaint@TMessage +The control is fully painted by own code so don't paint any borders. + +@@TVirtualTreeHintWindow.WMShowWindow@TWMShowWindow +Clear hint data when the window becomes hidden. + +@@TVirtualTreeOptions +Summary +Collects all binary options of the tree control into one place for easier access. + +Description +TVirtualTreeOptions does not add any new functionality to TCustomVirtualTreeOptions but is the publicly available class. + + +@@TVSTTextSourceType.tstAll +All nodes are rendered. Initialization is done on the fly. + +@@TVSTTextSourceType.tstCutCopySet +Only nodes currently marked as being in the cut/copy clipboard set are rendered. + +@@TVSTTextSourceType.tstInitialized +Only initialized nodes are rendered. + +@@TVSTTextSourceType.tstSelected +Only selected nodes are rendered. + +@@TVSTTextSourceType.tstVisible +Only visible nodes are rendered. + + +@@TVSTTextType.ttNormal +normal label of the node, this is also the text which can be edited + +@@TVSTTextType.ttStatic +static (non-editable) text after the normal text + +@@TVTAnimationOption.toAnimatedToggle +Expanding and collapsing a node is animated (quick window scroll). + + +@@TVTAutoOption.toAutoChangeScale +Change default node height automatically if the system's font scale is set to big fonts. + +@@TVTAutoOption.toAutoDeleteMovedNodes +Delete nodes which where moved in a drag operation (if not directed otherwise). + +@@TVTAutoOption.toAutoDropExpand +Expand node if it is the drop target for more than certain time. + +@@TVTAutoOption.toAutoExpand +Nodes are expanded (collapsed) when getting (losing) the focus. + +@@TVTAutoOption.toAutoFreeOnCollapse +Frees any child node after a node has been collapsed (HasChildren flag stays there). + +@@TVTAutoOption.toAutoHideButtons +Node buttons are hidden when there are child nodes, but all are invisible. + +@@TVTAutoOption.toAutoScroll +Scroll if mouse is near the border while dragging or selecting. + +@@TVTAutoOption.toAutoScrollOnExpand +Scroll as many child nodes in view as possible after expanding a node. + +@@TVTAutoOption.toAutoSort +Sort tree when Header.SortColumn or Header.SortDirection change or sort node if +child nodes are added. + +@@TVTAutoOption.toAutoSpanColumns +Large entries continue into next column(s) if there's no text in them (no clipping). + +@@TVTAutoOption.toAutoTristateTracking +Checkstates are automatically propagated for tri state check boxes. + +@@TVTAutoOption.toDisableAutoscrollOnFocus +Disable scrolling a column entirely into view if it gets focused. + + + +@@TVTButtonFillMode.fmShaded +color gradient, Windows XP style (legacy code, use toThemeAware on Windows XP instead) + +@@TVTButtonFillMode.fmTransparent +transparent color, use the item's background color + +@@TVTButtonFillMode.fmTreeColor +solid color, uses the tree's background color + +@@TVTButtonFillMode.fmWindowColor +solid color, uses clWindow + + +@@TVTButtonStyle.bsRectangle +traditional Windows look (plus/minus buttons) + +@@TVTButtonStyle.bsTriangle +traditional Macintosh look + +@@TVTColors +Summary +Collects all color related options for the tree control. + +Description +TVTColors makes it much more conventient to adjust Virtual Treeview's colors. Since everything is in one place you can +also easily compare all colors. + + +@@TVTDataObject.FAdviseHolder +Reference to an OLE supplied implementation for advising. + +@@TVTDataObject.FForClipboard +Determines which data to render with GetData. + +@@TVTDataObject.FInternalStgMediumArray +The available formats in the DataObject + +@@TVTDataObject.FOwner +The tree which provides clipboard or drag data. + + + + + + + + +@@TVTDragImage.FColorKey +color to make fully transparent regardless of any other setting + +@@TVTDragImage.FFade +determines whether to fade the drag image from center to borders or not + +@@TVTDragImage.FOwner +the actual drag image to blend to screen + +@@TVTDragImage.FRestriction +determines in which directions the drag image can be moved + +@@TVTDragImage.FStates +Determines the states of the drag image class. + +@@TVTDragImage.FTransparency +alpha value of the drag image (0 - fully transparent, 255 - fully opaque) + + +@@TVTDragImage.GetVisible +True if the drag image is currently hidden (used only when dragging) + +Returns True if the internal drag image is used (i.e. the system does not natively support drag images) and +the internal image is currently visible on screen. + + + + +@@TVTDragImage.RecaptureBackground@TBaseVirtualTree@TRect@HRGN@Boolean@Boolean +Note +The passed rectangle is given in client coordinates of the current drop target tree (given in Tree). The caller does not +check if the given rectangle is actually within the drag image. Hence this method must do all the checks. This method +does nothing if the system manages the drag image. +Summary +Notification by the drop target tree to update the background image because something in the tree has changed. + +@@TVTDragImage.ShowDragImage +Summary +Shows the drag image after it has been hidden by HideDragImage. + +Description +Also this method does nothing if the system manages the drag image. + + + +@@TVTDragImageKind.diComplete +show a complete drag image with all columns, only visible columns are shown + +@@TVTDragImageKind.diMainColumnOnly +show only the main column (the tree column) + +@@TVTDragImageKind.diNoImage +don't show a drag image at all + + +@@TVTDragManager.FDataObject +A reference to the data object passed in by DragEnter (only used when the owner +tree is the current drop target). + +@@TVTDragManager.FDropTargetHelper +Win2k > Drag image support + +@@TVTDragManager.FFullDragging +True, if full dragging is currently enabled in the system. + +@@TVTDragManager.FIsDropTarget +True if the owner is currently the drop target. + + + +@@TVTDrawSelectionMode.smBlendedRectangle +alpha blending, uses special colors (see TVTColors) + +@@TVTDrawSelectionMode.smDottedRectangle +same as DrawFocusRect + + +@@TVTHeader.DetermineSplitterIndex@TPoint +Note +The hit test is checking from right to left to make enlarging of zero-sized columns possible. +Description +\Result is rrue if column border was hit (with -3..+5 pixels tolerance). For continuous resizing the current track index +and the column's left border are set. + +Summary +Tries to find the index of that column whose right border corresponds to P. + + +@@TVTHeader.FDragImage +drag image management during header drag + +@@TVTHeader.FDragStart +initial mouse drag position + +@@TVTHeader.FImageChangeLink +connections to the image list to get notified about changes + +@@TVTHeader.FLeftTrackPos +left border of this column to quickly calculate its width on resize + +@@TVTHeader.FMainColumn +the column which holds the tree + +@@TVTHeader.FStates +used to keep track of internal states the header can enter + +@@TVTHeader.FStyle +button style + +@@TVTHeader.FTrackStart +client coordinates of the tracking start point + + + + + + + + + + + +@@TVTHeaderOption.hoAutoResize +adjust a column so that the header never exceeds client width of owner control + +@@TVTHeaderOption.hoColumnResize +resizing columns is allowed + +@@TVTHeaderOption.hoDblClickResize +allows a column to resize itself to its largest entry + +@@TVTHeaderOption.hoDrag +dragging columns is allowed + +@@TVTHeaderOption.hoHotTrack +header captions are highlighted when mouse is over a particular column + +@@TVTHeaderOption.hoOwnerDraw +header items with the owner draw style can be drawn by the application via event + +@@TVTHeaderOption.hoRestrictDrag +header can only be dragged horizontally + +@@TVTHeaderOption.hoShowHint +show application defined header hint + +@@TVTHeaderOption.hoShowImages +show images + +@@TVTHeaderOption.hoShowSortGlyphs +allow visible sort glyphs + +@@TVTHeaderOption.hoVisible +header is visible + +@@TVTHeaderStyle.hsFlatButtons +flatter look than hsThickButton, like an always raised flat TToolButton + +@@TVTHeaderStyle.hsPlates +flat TToolButton look and feel (raise on hover etc.) + +@@TVTHeaderStyle.hsThickButtons +TButton look and feel + +@@TVTHeaderStyle.hsXPStyle +Windows XP style + + +@@TVTHintData.DefaultHint +used only if there is no node specific hint string available +or a header hint is about to appear + +@@TVTHintData.HintRect +used for draw trees only, string trees get the size from the hint string + +@@TVTHintData.HintText +set when size of the hint window is calculated + +@@TVTHintMode.hmDefault +show the hint of the control + +@@TVTHintMode.hmHint +show node specific hint string returned by the application + +@@TVTHintMode.hmHintAndDefault +same as hmHint but show the control's hint if no node is concerned + +@@TVTHintMode.hmTooltip +show the text of the node if it isn't already fully shown + +@@TVTImageInfo.Ghosted +flag to indicate that the image must be drawn slightly lighter + +@@TVTImageInfo.Index +index in the associated image list + + + +@@TVTIncrementalSearch.isAll +search every node in tree, initialize if necessary + +@@TVTIncrementalSearch.isInitializedOnly +search only initialized nodes, skip others + +@@TVTIncrementalSearch.isNone +disable incremental search + +@@TVTIncrementalSearch.isVisibleOnly +search only visible nodes, initialize if necessary + + +@@TVTInternalPaintOption.poBackground +draw background image if there is any and it is enabled + +@@TVTInternalPaintOption.poColumnColor +erase node's background with the column's color + +@@TVTInternalPaintOption.poDrawDropMark +draw drop mark if a node is currently the drop target + +@@TVTInternalPaintOption.poDrawFocusRect +draw focus rectangle around the focused node + +@@TVTInternalPaintOption.poDrawSelection +draw selected nodes with the normal selection color + +@@TVTInternalPaintOption.poGridLines +draw grid lines if enabled + +@@TVTInternalPaintOption.poMainOnly +draw only the main column + +@@TVTInternalPaintOption.poSelectedOnly +draw only selected nodes + + + +@@TVTLineMode.lmBands +looks similar to a Nassi-Schneidermann diagram + +@@TVTLineMode.lmNormal +usual tree lines (as in TTreeview) + + +@@TVTLineStyle.lsCustomStyle +application provides a line pattern + +@@TVTLineStyle.lsDotted +usual dotted lines (default) + +@@TVTLineStyle.lsSolid +simple solid lines + + +@@TVTLineType.ltBottomRight +a line from bottom to the center and from there to the right + +@@TVTLineType.ltLeft +a line from top to bottom at the left + +@@TVTLineType.ltLeftBottom +a combination of ltLeft and a line at the bottom from left to right + +@@TVTLineType.ltNone +no line at all + +@@TVTLineType.ltRight +a line from center to the right + +@@TVTLineType.ltTopDown +a line from top to bottom + +@@TVTLineType.ltTopDownRight +a line from top to bottom and from center to the right + +@@TVTLineType.ltTopRight +a line from bottom to center and from there to the right +special styles for alternative drawings of tree lines + + +@@TVTMiscOption.toAcceptOLEDrop +Register tree as OLE accepting drop target + +@@TVTMiscOption.toCheckSupport +Show checkboxes/radio buttons. + +@@TVTMiscOption.toEditable +Node captions can be edited. + +@@TVTMiscOption.toFullRepaintOnResize +Fully invalidate the tree when its window is resized (CS_HREDRAW/CS_VREDRAW). + +@@TVTMiscOption.toGridExtensions +Use some special enhancements to simulate and support grid behavior. + +@@TVTMiscOption.toInitOnSave +Initialize nodes when saving a tree to a stream. + +@@TVTMiscOption.toReadOnly +The tree does not allow to be modified in any way. No action is executed and +node editing is not possible. + +@@TVTMiscOption.toReportMode +Tree behaves like TListView in report mode. + +@@TVTMiscOption.toToggleOnDblClick +Toggle node expansion state when it is double clicked. + +@@TVTMiscOption.toWheelPanning +Support for mouse panning (wheel mice only). This option and toMiddleClickSelect are +mutal exclusive, where panning has precedence. + + +@@TVTNodeAlignment.naFromBottom +the align member specifies amount of units (usually pixels) from top border of the node + +@@TVTNodeAlignment.naFromTop +align is to be measured from bottom + +@@TVTNodeAlignment.naProportional +align is to be measure in percent of the entire node height and relative to top + + +@@TVTNodeAttachMode.amAddChildFirst +add node as first child of destination + +@@TVTNodeAttachMode.amAddChildLast +add node as last child of destination + +@@TVTNodeAttachMode.amInsertAfter +insert node just after destionation (as sibling of destination) + +@@TVTNodeAttachMode.amInsertBefore +insert node just before destination (as sibling of destination) + +@@TVTNodeAttachMode.amNoWhere +just for simplified tests, means to ignore the Add/Insert command + + + + + + + + + +@@TVTPaintInfo.Alignment +how to align within the node rectangle + +@@TVTPaintInfo.BidiMode +directionality to be used for painting + +@@TVTPaintInfo.BrushOrigin +the alignment for the brush used to draw dotted lines + +@@TVTPaintInfo.Canvas +the canvas to paint on + +@@TVTPaintInfo.Column +the node's column index to paint + +@@TVTPaintInfo.ImageInfo +info about each possible node image + +@@TVTPaintInfo.Node +the node to paint + +@@TVTPaintInfo.NodeWidth +the actual node width + +@@TVTPaintInfo.PaintOptions +a copy of the paint options passed to PaintTree + +@@TVTPaintInfo.Position +the column position of the node + +@@TVTPaintOption.toAlwaysHideSelection +Do not draw node selection, regardless of focused state. + +@@TVTPaintOption.toFullVertGridLines +Display vertical lines over the full client area, not only the space occupied by nodes. +This option only has an effect if toShowVertGridLines is enabled too. + +@@TVTPaintOption.toGhostedIfUnfocused +Ghosted images are still shown as ghosted if unfocused (otherwise the become non-ghosted +images). + +@@TVTPaintOption.toHideFocusRect +Avoid drawing the dotted rectangle around the currently focused node. + +@@TVTPaintOption.toHideSelection +Selected nodes are drawn as unselected nodes if the tree is unfocused. + +@@TVTPaintOption.toHotTrack +Track which node is under the mouse cursor. + +@@TVTPaintOption.toPopupMode +Paint tree as would it always have the focus (useful for tree combo boxes etc.) + +@@TVTPaintOption.toShowBackground +Use the background image if there's one. + +@@TVTPaintOption.toShowButtons +Display collapse/expand buttons left to a node. + +@@TVTPaintOption.toShowDropmark +Show the dropmark during drag'n drop operations. + +@@TVTPaintOption.toShowHorzGridLines +Display horizontal lines to simulate a grid. + +@@TVTPaintOption.toShowRoot +Show lines also at top level (does not show the hidden/internal root node). + +@@TVTPaintOption.toShowTreeLines +Display tree lines to show hierarchy of nodes. + +@@TVTPaintOption.toShowVertGridLines +Display vertical lines (depending on columns) to simulate a grid. + +@@TVTPaintOption.toThemeAware +Draw UI elements (header, tree buttons etc.) according to the current theme if +enabled (Windows XP+ only, application must be themed). + +@@TVTPaintOption.toUseBlendedImages +Enable alpha blending for ghosted nodes or those which are being cut/copied. + +@@TVTPaintOption.toUseBlendedSelection +Enable alpha blending for node selections. + + + + + + +@@TVTSearchStart.ssAlwaysStartOver +always use the first/last node (depending on direction) to search from + +@@TVTSearchStart.ssFocusedNode +use the currently focused node + +@@TVTSearchStart.ssLastHit +use the last found node + +@@TVTSelectionOption.toCenterScrollIntoView +Center nodes vertically in the client area when scrolling into view. + +@@TVTSelectionOption.toDisableDrawSelection +Prevent user from selecting with the selection rectangle in multiselect mode. + +@@TVTSelectionOption.toExtendedFocus +Entries other than in the main column can be selected, edited etc. + +@@TVTSelectionOption.toFullRowSelect +Hit test as well as selection highlight are not constrained to the text of a node. + +@@TVTSelectionOption.toLevelSelectConstraint +Constrain selection to the same level as the selection anchor. + +@@TVTSelectionOption.toMiddleClickSelect +Allow selection, dragging etc. with the middle mouse button. This and toWheelPanning +are mutual exclusive. + +@@TVTSelectionOption.toMultiSelect +Allow more than one node to be selected. + +@@TVTSelectionOption.toRightClickSelect +Allow selection, dragging etc. with the right mouse button. + +@@TVTSelectionOption.toSiblingSelectConstraint +constrain selection to nodes with same parent + +@@TVTSelectionOption.toSimpleDrawSelection +Simplifies draw selection, so a node's caption does not need to intersect with the +selection rectangle. + + + +@@TVTStringOption.toAutoAcceptEditChange +Automatically accept changes during edit if the user finishes editing other then +VK_RETURN or ESC. If not set then changes are cancelled. + +@@TVTStringOption.toSaveCaptions +If set then the caption is automatically saved with the tree node, regardless of what is +saved in the user data. + +@@TVTStringOption.toShowStaticText +Show static text in a caption which can be differently formatted than the caption +but cannot be edited. + + +@@TVTUpdateState.usBegin +The tree just entered the update state (BeginUpdate call for the first time). + +@@TVTUpdateState.usBeginSynch +The tree just entered the synch update state (BeginSynch call for the first time). + +@@TVTUpdateState.usEnd +The tree just left the update state (EndUpdate called for the last level). + +@@TVTUpdateState.usEndSynch +The tree just left the synch update state (EndSynch called for the last level). + +@@TVTUpdateState.usSynch +Begin/EndSynch has been called but the tree did not change the update state. + +@@TVTUpdateState.usUpdate +Begin/EndUpdate has been called but the tree did not change the update state. + + + + + + +@@VirtualTrees.pas +Version 4.0.0 + + The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ + + Alternatively, you may redistribute this library, use and/or modify it under the terms of the + GNU Lesser General Public License as published by the Free Software Foundation; + either version 2.1 of the License, or (at your option) any later version. + You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/. + + Software distributed under the License is distributed on an "AS IS" basis, + WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the + specific language governing rights and limitations under the License. + + The original code is VirtualTrees.pas, released September 30, 2000. + + The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de), + written by Dipl. Ing. Mike Lischke (public@lischke-online.de, www.lischke-online.de). + + Portions created by digital publishing AG are Copyright + (C) 1999-2001 digital publishing AG. All Rights Reserved. +---------------------------------------------------------------------------------------------------------------------- + + September 2003 + - Improvement: Edit property of TStringEditLink promoted to public. + - Improvement: ShortenString better takes right-to-left contexts into account. + - Improvement: toAlwaysHideSelection introduced. Allows to hide node selections entirely. + - Improvement: toUseBlendedSelection introduced. Allows to have translucent node selections. + - Bug fix: Mantis bug entries #140, 144, 125, 122, 129. + - Improvement: Mantis feature request #113, toSimpleDrawSelection introduced. + - Improvement: ComputeNodeHeight introduced. Helper method to delegate node height calculation to the tree. + - Improvement: Alt key might be pressed when clicking in the tree. This allows to start drawing the selection + rectangle also on node captions and images (which would otherwise start dragging). + August 2003 + - Bug fix: ValidateCache was not always called in ToggleNode when InvalidateCache was used. + - Bug fix: FLastHintRect was sometimes not reset preventing so a new hint to appear. + - Bug fix: Redundant ChangeCheckState in HandleMouseDown removed. + - Bug fix: OnHeaderDblClick was triggered even if the colum was set to be unclickable. + - Bug fix: Wheel panning and scrolling was not possible if toAutoScroll was not set. This option has another meaning + and should not impact wheel handling. + - Bug fix: VT control could not be set as ActiveControl at design time. + - Bug fix: In method ContentToText it could be that the text contained the separator char as regular character, so + it was necessary to wrap the text with quotation marks then. + - Bug fix: Bidi mode and aligment was not correctly considered in UpdateEditBounds when grid extensions were enabled. + July 2003 + - Improvement: Check for nil hint data in TVirtualTreeHintWindow.CalcHintRect just to be on the safe side. + - Improvedment: TVirtualTreeColumn.ComputeHeaderLayout is now virtual to allow descentants to change the layout. + - Improvement: toFullVertGridLines, vertical grid lines can be drawn over the full client are height. + - Improvement: flickering on column resizing is gone. + - Improvement: System conformal border width calculation for certain tasks. + - Improvement: Animation parameter for TVTHeader.AutoFitColumns to avoid the size animation (default: True). + - Improvement: ParentFont property for the header. Default is False to stay compatible with older tree versions. + - Bug fix: cursor rectangle for spanned columns in normal hint mode was too small. + - Feature: the implementation is now more than 30.000 lines in size. + - Bug fix: Access violation fixed, which was sometimes caused by setting VT to edit mode if the old edit link + was not freed yet (because it was still handling a message). + - Improvement: Hint animation now does no longer stop quick switches to new hints. + - Improvement: ParentBackground property published. + - Bug fix: vsAllChildrenHidden and vsExpanded are now removed from a node's state if there are no child nodes anymore + - Improvement: column width limit to 10000 is now only applied on non-NT systems (Win9x/Me). + - Improvement: single letter mode in incremental search is not used if the current node also fits + the repeated character. + June 2003 + - Bug fix: correct theme change handling when switching to classic mode. + - Improvement: new event OnMeasureItem, new handling for application driven node heights. + TCustomVirtualStringTree.ComputeNodeHeight implementation to easy node height computation for multi line nodes. + - Improvement: Header is nil'ed when the tree is destroyed and checked before used in TBaseVirtualTree.Notification + in order to avoid potential problems accessing an invalid address. + - Bug fix: The cut and copy pending states in the tree and participating nodes were not removed. + - Bug fix: csPaintCopy was not considered when painting (used for TWinControl.PaintTo, e.g. in Form.Print). + - Bug fix: DT_NOPREFIX added for header text output. + May 2003 + - Bug fix: Thread safe check for current tree reference in the worker thread, as it can be reset before it was used. + - Bug fix: Color change for non-standard background colors after all columns were hidden. + - Improvement: new node background erase action (eaNone). + + For full document history see help file. + + Credits for their valuable assistance and code donations go to: + Freddy Ertl, Marian Aldenhövel, Thomas Bogenrieder, Jim Kuenemann, Werner Lehmann, Jens Treichler, + Paul Gallagher (IBO tree), Ondrej Kelle, Ronaldo Melo Ferraz, Heri Bender, Roland Bedürftig (BCB) + Anthony Mills, Alexander Egorushkin (BCB), Mathias Torell (BCB), Frank van den Bergh, Vadim Sedulin, Peter Evans, + Milan Vandrovec (BCB), Steve Moss (system check images), Joe White, David Clark (local node memory manager), + Anders Thomsen, Igor Afanasyev, Eugene Programmer + Beta testers: + Freddy Ertl, Hans-Jürgen Schnorrenberg, Werner Lehmann, Jim Kueneman, Vadim Sedulin, Moritz Franckenstein, + Wim van der Vegt, Franc v/d Westelaken + Indirect contribution (via publicly accessible work of those persons): + Alex Denissov, Hiroyuki Hori (MMXAsm expert) + Documentation: + Markus Spoettl and toolsfactory GbR (http://www.doc-o-matic.com/, sponsoring Virtual Treeview + with a free copy of the Doc-O-Matic help authoring system), Sven H. (Step by step tutorial) + CLX: + Dmitri Dmitrienko (initial developer) + + + + + + + + + + + + + +@@Check button image indices + + +@@ckButtonHot + + +@@ckButtonNormal + + +@@ckButtonPressed + + +@@ckCheckCheckedDisabled + + +@@ckCheckCheckedHot + + +@@ckCheckCheckedNormal + + +@@ckCheckCheckedPressed + + +@@ckCheckMixedHot + + +@@ckCheckMixedNormal + + +@@ckCheckMixedPressed + + +@@ckCheckUncheckedDisabled + + +@@ckCheckUncheckedHot + + +@@ckCheckUncheckedNormal + + +@@ckCheckUncheckedPressed + + +@@ckRadioCheckedHot + + +@@ckRadioCheckedNormal + + +@@ckRadioCheckedPressed + + +@@ckRadioUncheckedDisabled + + +@@ckRadioUncheckedHot + + +@@ckRadioUncheckedNormal + + +@@ckRadioUncheckedPressed + + +@@EVirtualTreeError +Description +EVirtualTreeError is a normal exception derivation especially for Virtual Treeview. This class does not add much value to +its parent class but is rather there to better tell when an exception particularly from Virtual Treeview was raised. + +@@TBufferedString + + +@@TBaseVirtualTree.Alignment +Summary +Determines the horizontal alignment of text if no columns are defined. + +Description +This property is only used if there are no columns defined and applies only to the node captions. Right alignment means +here the right client area border and left aligned means the node buttons/lines etc. (both less the text margin). + +@@TBaseVirtualTree.AnimationDuration +Summary +Determines the maximum duration the tree can use to play an animation. + +Description +The value is specified in milliseconds and per default there are 200 ms as time frame, which is the recommended duration +for such operations. On older systems (particularly Windows 95 and Windows 98) the animation process might not get enough +CPU time to avoid expensive animations to finish properly. Still the animation loop tries to stay as close as possible to +the given time. + +@@TBaseVirtualTree.AutoExpandDelay +Summary +Time delay after which a node gets expanded if it is the current drop target. + +Description +This value is specified in milliseconds and determines when to expand a node if it is the current drop target. This value +is only used if voAutoDropExpand in Options is set. + +@@TBaseVirtualTree.AutoScrollDelay +Summary +Time which determines when auto scrolling should start. + +Description +Once the mouse pointer has been moved near to a border a timer is started using the interval specified by +AutoScrollDelay. When the timer has fired auto scrolling starts provided it is enabled (see also TreeOptions). The value +is specified in milliseconds. + +@@TBaseVirtualTree.AutoScrollInterval +Summary +Time interval between scroll events when doing auto scroll. + +Description +This property determines the speed how the tree is scrolled vertically or horizontally when auto scrolling is in +progress. The value is given in milliseconds. + +@@TBaseVirtualTree.Background +Summary +Holds a background image for the tree. + +Description +Virtual Treeview supports a fixed background image which does not scroll but can be adjusted by BackgroundOffsetX and +BackgroundOffsetY. + +@@TBaseVirtualTree.BackgroundOffsetX +Summary +Horizontal offset of the background image. + +Description +Determines the horizontal offset of the left border of the background image. This value is relative to the target canvas +where the tree is painted to (usually the tree window). + +@@TBaseVirtualTree.BackgroundOffsetY +Summary +Vertical offset of the background image. + +Description +Determines the vertical offset of the top border of the background image. This value is relative to the target canvas +where the tree is painted to (usually the tree window). + +@@TBaseVirtualTree.BorderStyle +Summary +Same as TForm.BorderStyle. + +Description +See TForm.BorderStyle. + +@@TBaseVirtualTree.ButtonFillMode +Summary +Determines how to fill the background of the node buttons. + +Description +This property is used to specify how the interior of the little plus and minus node buttons should be drawn, if +ButtonStyle is bsTriangle. + +@@TBaseVirtualTree.ButtonStyle +Summary +Determines the look of node buttons. + +Description +Determines the look of node buttons. + +@@TBaseVirtualTree.ChangeDelay +Summary +Time which determines when the OnChange event should be triggered after the actual change event. + +Description +In order to accumulate many quick changes in the tree you can use this delay value to specify after which wait time the +OnChange event should occur. A value of 0 means to trigger OnChange immediately after the change (usually a selection or +focus change) happend. Any value \> 0 will start a timer which then triggers OnChange. + + + +\Note that there is the synchronous mode (started by BeginSynch) which effectively circumvents the change delay for the +duration of the synchronous mode (stopped by EndSynch) regardless of the ChangeDelay setting. + +@@TBaseVirtualTree.CheckImageKind +Summary +Determines which images should be used for checkboxes and radio buttons. + +Description +CheckImageKind can be used to switch the image set, which should be used for the tree. Read the description about +TCheckImageKind for a list of all images, which can be used. CheckImageKind can also be set to ckCustom, which allows to +supply a customized set of images to the tree. In order to have that working you must assign an image list +(TCustomImageList) to the CustomCheckImages property. + +@@TCheckState +Summary +\Returns the current state of a node's check box, radio button or node button. + +Description +The check states include both, transient and fluent (temporary) states. The only temporary state defined so far is the +pressed state. + +@@TBaseVirtualTree.CheckState +Summary +Read or set the check state of a node. + +Description +The CheckState property can be used to read the current check state of a node or to set a new one. Virtual Treeview +ensures that invalid check states (e.g. csMixedPressed for radio buttons) do not cause an error. + +@@TBaseVirtualTree.CheckType +Summary +Read or set the check type of a node. + +Description +The CheckType property can be used to read the current check type of a node or to set a new one. Setting a new check type +will reset a the node's check state to csUncheckedNormal. + +@@TBaseVirtualTree.ChildCount +Summary +Read or set the number of child nodes of a node. + +Description +ChildCount can be used to read the current number of child nodes or to change it. Assigning a lower value than there was +before will automatically delete as many child nodes (starting from the last child) as there are more than what was set. +Increasing the value will add new child nodes. Note: code behind this property is very effective, so it using ChildCount +is highly recommended over manipulating the child count using AddChild, InsertNode and DeleteNode. + +@@TBaseVirtualTree.ChildrenInitialized +Summary +Read whether a node's child count has been initialized already. + +Description +This read only property is used to determine whether a node's child count has been set. Alternatively, the child count +value is not considered if vsHasChildren is not in the node states. + +@@TBaseVirtualTree.ClipboardFormats +Summary +Special class to keep a list of clipboard format descriptions. + +Description +This TStringList descendant is used to keep a number of clipboard format descriptions, which are usually used to register +clipboard formats with the system. Using a string list for this task allows to store enabled clipboard formats in the +DFM. + +@@TBaseVirtualTree.Colors +Summary +A collection of colors used in the tree. + +Description +This property holds an instance of the TVTColors class, which is used to customize many of the colors used in a tree. +Placing them all in a specialized class helps organizing the colors in the object inspector and improves general +management. + +@@TBaseVirtualTree.CustomCheckImages +Summary +Assign your own image list to get the check images you like most. + +Description +The CustomCheckImages property is used when custom check images are enabled (see also ckCustom in TCheckImageKind). + +See Also +TCheckImageKind + +@@TBaseVirtualTree.DefaultNodeHeight +Summary +Read or set the height new nodes get as initial value. + +Description +This property allows to read the current initial height for new nodes and to set a new value. Note that changing the +property value does not change the height of existing nodes. Only new nodes are affected. + +@@TBaseVirtualTree.DefaultPasteMode +Summary +Read or set the value, which determines where to add pasted nodes to. + +Description +The default paste mode is an attach mode, which is used when pasting data from the clipboard into the tree. Usually, you +will want new nodes to be added as child nodes to the currently focused node (and this is also the default value), but +you can also specify to add nodes only as siblings. + + + +See Also +TVTNodeAttachMode + +@@TBaseVirtualTree.HintAnimation +Summary +Read or set the current hint animation type. + +Description +With this property you can specify what animation you would like to play when displaying a hint. For some applications it +might not be good to animate hints, hence you can entirely switch them off. Usually however you will leave the system +standard. This way the user can decide whether and which hint animation he or she likes. + +@@TBaseVirtualTree.DragHeight +Summary +Read or set the vertical limit of the internal drag image. + +Description +The DragHeight property (as well as the DragWidth property) are only for compatibility reason in the tree. If a platform +does not support the IDropTargetHelper interface (Windows 9x/Me, Windows NT 4.0) then Virtual Treeview uses its own +implementation of a DragImage. Since displaying a translucent drag image is performance hungry you should limit the image +size shown for the drag operation. + +@@TBaseVirtualTree.DragWidth +Summary +Read or set the horizontal limit of the internal drag image. + +Description +The DragWidth property (as well as the DragHeight property) are only for compatibility reason in the tree. If a platform +does not support the IDropTargetHelper interface (Windows 9x/Me, Windows NT 4.0) then Virtual Treeview uses its own +implementation of a DragImage. Since displaying a translucent drag image is performance hungry you should limit the image +size shown for the drag operation. + +@@TBaseVirtualTree.DragImage +Summary +Holds the instance of the internal drag image. + +Description +For older systems where the IDropTargetHelper interface is not supported Virtual Treeview simulates the translucent drag +image during drag'n drop. The property DragImage makes the internal drag image instance accessible for special handling. +The class itself is always created but is usually not visible when the IDropTargetHelper interface is supported. + +@@TBaseVirtualTree.DragImageKind +Summary +Read or set what should be shown in the drag image. + +Description +DragImageKind allows to switch parts of the drag image off and on. + +@@TBaseVirtualTree.DragManager +Summary +Holds the reference to the internal drag manager. + +Description +The drag manager is the central point for the drag'n drop support in Virtual Treeview. Usually you do not need to access +it but sometimes it might be necessary so the reference is accessible through this property. + + + +See Also +TVTDragManager + +@@TBaseVirtualTree.DragOperations +Summary +Read or set which drag operations may be allowed in the tree. + +Description +Using this property you can determine, which actions may be performed when a drag operation is finished. The default +value includes move, copy and link, where link is rather an esoteric value and only there because it is supported by OLE. +The values used directly determine which image is shown for the drag cursor. The specified drag operations do not tell +which actions will actually be performed but only, which actions are allowed. They still can be modified during drag'n +drop by using a modifier key like the control, shift or alt key or can entirely be ignored by the drop handler. + +@@TBaseVirtualTree.DragSelection +Summary +Keeps a temporary list of nodes during drag'n drop. + +Description +This list is a local copy of the current selection array and is only used during a drag operation. + +@@TBaseVirtualTree.DragType +Summary +Read or set which subsystem should be used for dragging. + +Description +Traditionally, Delphi only supports its own drag mechanism, which is not compatible with the rest of the system. This VCL +dragging also does not support to transport random data nor does it support drag operations between applications. Thus +Virtual Treeview also supports the generally used OLE dragging, which in turn is incompatible with VCL dragging. +Depending on your needs you can enable either VCL or OLE dragging as both together cannot be started. However, Virtual +Treeview is able to act as drop target for both kind of data, independant of what is set in DragType. + +@@TBaseVirtualTree.DrawSelectionMode +Summary +Read or set how multiselection with the mouse is to be visualized. + +Description +Virtuall Treeview allows to display two different selection rectangles when doing multiselection with the mouse. One is +the traditiional dotted focus rectangle and the other one is a translucent color rectangle. The latter is the preferred +\one but the former is set as default (for compatibility reasons). + +@@TBaseVirtualTree.DropTargetNode +Summary +Contains the current drop target node if the tree is currently the target of a drag'n drop operation. + +Description +The drop target node has no meaning except during drag'n drop and only if the tree it belongs to is itself the current +drop target. But even then DropTargetNode might be nil, particularly when the mouse hovers over an area in the tree, +which is not covered by a node. + +@@TBaseVirtualTree.EditDelay +Summary +Read or set the maximum time between two single clicks on the same node, which should start node editing. + +Description +A node edit operation can be started using the keyboard (F2 key), in code using EditNode or by clicking twice on the same +node (but not doing a double click). EditDelay is the maxmimum time distance between both clicks in which the edit +\operation is started. + +See Also + + +@@TBaseVirtualTree.EditLink +Summary +Keeps a reference to the internal edit link during a node edit operation. + +Description +During an edit operation a link is established between the tree and the editor for the current node. By default a simple +TEdit control is used as editor but due to the great customization possibilities there can be any node editor you may +want. In order to communicate with this potentially unknown node editor the edit link is used. The EditLink property +holds this link during the edit operation, so you can manipulate the interface. + +@@TBaseVirtualTree.Expanded +Summary +Read or set the expanded state of a particular node. + +Description +Using this property you can expand or collapse the given node. This method uses the central ToggleNode method. + +@@TBaseVirtualTree.FocusedColumn +Summary +Read or set the currently focused collumn. + +Description +When toExtendedFocus in TVTSelectionOptions is enabled then the user can select node cells in others than the main column +(the column with the tree structure). In order to keep track, which column is currently selected FocusedColumn is used +(similar to FocusedNode). + + + +See Also +FocusedNode, TVTSelectionOptions + +@@TBaseVirtualTree.FocusedNode +Summary +Read or set the currently focused node. + +Description +One node (and only one) in the tree view can have the current input focus, marked as dotted rectangle around the node's +caption. Having the input focus means this node can be edited by pressing F2 or clicking on it and user keyboard input is +interpreted with respect to the focused node (e.g. tree navigation, expansion/collapsing etc.). If extended focus is +enabled then also the FocusedColumn property is taken into account. Read there for more info about column focus. + + + +See Also +FocusedColumn, TVTSelectionOptions + +@@TBaseVirtualTree.Font +Summary +Same as TWinControl.Font. + +Description +See TWinControl.Font. + +@@TBaseVirtualTree.FullyVisible +Summary +Read or set whether a node is fully visible or not. + +Description +Beside the fact that a node can be out of the client area there are two possibilities for it to be hidden. One is the +vsVisible state in TVirtualNodeState, which hides the node regardles of the current state of another node, if not +specified. The other one is that one or more parent nodes might be collapsed, hiding so their entire child nodes +structure. The visibility flag itself can be checked using the IsVisible property, while the expansion state of parents +nodes can be examined via the VisiblePath property. If both are true then the node is said to be fully visible. + + + +See Also +IsVisible, VisiblePath, vsVisible, TVirtualNodeStates + +@@TBaseVirtualTree.HasChildren +Summary +Read or set whether a node has got children. + +Description +A node can be set to have children by assigning true to this property. Internally this will add the vsHasChildren state +to the node but not add any child nodes. This state in turn will cause the node to be drawn with a plus sign in front of +its caption, denoting so it can be expanded and will show child nodes. As long as the child nodes are not touch in any +way (e.g. by expanding the parent node or by navigatin or searching/sorting the tree) there will be no actual child +nodes. They simply do not exist yet. However they will be created as soon as an access is done. + + + +Setting the HasChildren property to false will delete any existing child node. + + + +See Also +vsHasChildren, TVirtualNodeStates + +@@TBaseVirtualTree.Header +Summary +Provides access to the header instance. + +Description +This property is used to allow access to the header instance, which manages all aspects of the tree's header image as +well as the column settings. + + + +See Also +TVTHeader + +@@TBaseVirtualTree.HeaderRect +Summary +\Returns the non-client-area rectangle used for the header. + +Description +Use this property to determine the extents used by the header of Virtual Treeview. + +@@TBaseVirtualTree.HintMode +Summary +Read or set what type of hint you want for the tree view. + +Description +Virtual Treeview supports several hints modes. This includes the normal hint used for any other TControl class as well as +a node specific hint, which is individual for each node or even each cell. + +@@TBaseVirtualTree.HotCursor +Summary +Read or set which cursor should be used for hot nodes. + +Description +When you enable toHotTrack in TreeOptions.PaintOptions then the node, which is currently under the mouse pointer becomes +the hot node. This is a special state, which can be used for certain effects. Hot nodes have by default an underlined +caption and may cause the cursor to change to what ever you like. The HotCursor property is used to specify, which cursor +is to be used. + + + +See Also +HotNode, TVTPaintOptions + +@@TBaseVirtualTree.HotNode +Summary +Read, which node is currently the hot node. + +Description +When you enable toHotTrack in TreeOptions.PaintOptions then the node, which is currently under the mouse pointer becomes +the hot node. The property HotNode can be used to access this node for special handling. + + + +See Also +HotCursor, toHotTrack, TVTPaintOptions + +@@TBaseVirtualTree.Images +Summary +Read or set the tree's normal image list. + +Description +Just like with TListView and TTreeview also Virtual Treeview can take an image list for its normal images. Additionally, +there are image lists for state images and check images. + + + +See Also +StateImages, CheckImages + +@@TBaseVirtualTree.IncrementalSearch +Summary +Read or set the current incremental search mode. + +Description +Virtual Treeview can do an incremental search by calling back the application when comparing node captions. The +IncrementalSearch property determines whether incremental search is enabled and which nodes should be searched through. + + + +See Also +IncrementalSearchDirection, IncrementalSearchStart, IncrementalSearchTimeout + +@@TBaseVirtualTree.IncrementalSearchDirection +Summary +Read or set the direction to be used for incremental search. + +Description +When incremental search is enabled then Virtual Treeview can search forward and backward from the start point given by +IncrementalSearchStart. + + + +See Also +IncrementalSearch, IncrementalSearchStart, IncrementalSearchTime123out + +@@TBaseVirtualTree.IncrementalSearchStart +Summary +Read or set where to start incremental search. + +Description +When incremental search is enabled in the tree view then you can specify here, where to start the next incremental search +\operation from. + + + +See Also +IncrementalSearch, IncrementalSearchDirection, IncrementalSearchTimeout + +@@TBaseVirtualTree.IncrementalSearchTimeout +Summary +Read or set the maximum time, which is allowed between two consecutive key strokes so that incremental search stays +active. + +Description +When incremental search is enabled in Virtual Treeview then you can specify here after what time incremental search +should stop when no keyboard input is encountered any longer. This property so determines also the speed at which users +have to type letters to keep the incremental search rolling. + + + +See Also +IncrementalSearch, IncrementalSearchDirection, IncrementalSearchStart + +@@TBaseVirtualTree.Indent +Summary +Read or set the indentation amount for node levels. + +Description +Each new level in the tree (child nodes of a parent node) are visually shifted to distinguish betwenn them and their +parent node (that's the tree layout after all). The Indent property determines the shift distance in pixels. + +@@TBaseVirtualTree.IsDisabled +Summary +Read or set the enabled state of the given node. + +Description +A node can have many different states. One of them is its enabled state, which can be set via this property. Enabling a +node means it can be focused and selected, so it can take part in clipboard and drag'n drop operations, and can be +edited. + +@@TBaseVirtualTree.IsVisible +Summary +Read or set the visibility state of the given node. + +Description +A node can be made invisible using this property. That means, even if its parent nodes all are expanded the node is not +shown and the visual image is as would the node not exist. However it still can be searched or take part in certain other +\operations. + +@@TBaseVirtualTree.LastDropMode +Summary +Read how the last drop operation finished. + +Description +In the case you don't handle drag'n drop operations directly in OnDragDrop it might be necessary to know how the last +drag operation finshed. Read more in the drag mode enumeration about what is possible. + +@@TBaseVirtualTree.LineMode +Summary +Read or set the mode of the tree lines. + +Description +Apart from the usual lines Virtual Treeview also supports a special draw mode named bands. This allows for neat visual +effects. + +@@TBaseVirtualTree.LineStyle +Summary +Read or set the mode of the tree lines. + +Description +Virtual Treeview allows to customize the lines used to display the node hierarchy. The default style is a dotted pattern, +but you can also make solid lines or specify your own line pattern. + +@@TBaseVirtualTree.Margin +Summary +Read or set the tree's node margin. + +Description +The node margin is the distance between the cell bounds and its content like the lines, images, check box and so on. +However this border is only applied to the left and right side of the node cell. + + + +\Note: there is also a TextMargin property in TVirtualStringTree, which is an additional border for the cell text only. + + + +See Also +TVirtualStringTree.TextMargin + +@@TBaseVirtualTree.MultiLine +Summary +Read or toggle the multiline feature for a given node. + +Description +Since multiline support for nodes requires extra processing this behavior is switchable. When switched on the node is +wrapped into the available space until the node height is exhausted. By including carriage return/line feed pairs you can +explicitely specify where to start new lines. The node's height is not automatically adjusted to the given text. Instead +there is an event (OnMeasureItem), which can be used to compute a node's height before it is displayed the first time. In +addition an application can use the ComputeNodeHeight method to compute the height of the node depending on its caption +text. + +@@TBaseVirtualTree.NodeAlignment +Summary +Read or set the node alignment value. + +Description +Nodes have got an align member, which is used to determine the vertical position of the node's images and tree lines. The +NodeAlignment property specifies how to interpret the value in the align member. + + + +See Also +TVirtualNode + +@@TBaseVirtualTree.NodeDataSize +Summary +Read or set the extra data size for each node. + +Description +A node can have an area for user data, which can be used to store application defined, node specific data in. Use +GetNodeData to get the address of this area. In addition to assigning a value here you can also use the OnGetNodeDataSize +event, which is called when NodeDataSize is -1. + + + +See Also + + +@@TBaseVirtualTree.NodeHeight +Summary +Read or set a node's height. + +Description +Each node can have its individual height, which is stored in the node's record. You could directly assign a value to this +member but I strongly discourage this as it does not update certain other structures in the tree. Instead use the +NodeHeight property here to modify a node's height. + +@@TBaseVirtualTree.NodeParent +Summary +Read or set a node's parent node. + +Description +When reading this property then either the node's real parent node is returned or nil if the parent node is the internal, +hidden root node. When writing to this property you will effectively move a node to a new location. + + + +See Also +MoveTo, CopyTo + +@@TBaseVirtualTree.OffsetX + + +@@TBaseVirtualTree.OffsetXY +Summary +Read or set the tree's current horizontal and vertical scroll offsets. + +Description +Virtual Treeview allows to retrieve or set the internal scroll offset directly, without sending WM_HSCROLL/WM_VSCROLL +message around. This allows also to link two or more trees together. This scroll offset is given in pixels and is always +less or equal 0. + +@@TBaseVirtualTree.OffsetY + + +@@TBaseVirtualTree.OnAdvancedHeaderDraw +Summary +Header paint support event. + +Description +The OnAdvancedHeaderDraw event is used when owner draw is enabled for the header and a column is set to owner draw mode. +It can be used to custom draw only certain parts of the header instead the whole thing. A good example for this event is +customizing the background of the header for only one column. With the standard custom draw method (OnHeaderDraw) you are +in an all-or-nothing situation and have to paint everything in the header including the text, images and sort direction +indicator. OnAdvancedHeaderDraw however uses OnHeaderDrawQueryElements to ask for the elements the application wants to +draw and acts accordingly. + + + +See Also +OnHeaderDrawQueryElements, OnHeaderDraw + +@@TBaseVirtualTree.OnAfterCellPaint +Summary +Paint support event. + +Description +This event is called whenever a cell has been painted. A cell is defined as being one part of a node bound to a certain +column. This event is called several times per node (the amount is determined by visible columns and size of the part to +draw). + + + +See Also + + +@@TBaseVirtualTree.OnAfterItemErase +Summary +Paint support event. + +Description +Called after the background of a node has been erased (erasing can also be filling with a background image). This event +is called once per node in a paint cycle. + +See Also + + +@@TBaseVirtualTree.OnAfterItemPaint +Summary +Paint support event. + +Description +Called after a node has been drawn. This event is called once per node. + +See Also + + +@@TBaseVirtualTree.OnAfterPaint +Summary +Paint support event. + +Description +Called after all nodes which needed an update have been drawn. This event is called once per paint cycle. + +See Also + + +@@TBaseVirtualTree.OnBeforeCellPaint +Summary +Paint support event. + +Description +This event is called immediately before a cell is painted. + +See Also + + +@@TBaseVirtualTree.OnBeforeItemErase +Summary +Paint support event. + +Description +Called when the background of a node is about to be erased. + +See Also + + +@@TBaseVirtualTree.OnBeforeItemPaint +Summary +Paint support event. + +Description +Called after the background of a node has been drawn and just before the node itself is painted. In this event the +application gets the opportunity to decide whether a node should be drawn normally or should be skipped. The application +can draw the node itself if necessary or leave the node area blank. + +See Also + + +@@TBaseVirtualTree.OnBeforePaint +Summary +Paint support event. + +Description +Called as very first event in a paint cycle. In this event has the application the opportunity to do some special +preparation of the canvas onto which the tree is painted, e.g. setting a special viewport and origin or a different +mapping mode. + +See Also + + +@@TBaseVirtualTree.OnChange +Summary +Navigation support event. + +Description +Called when a node's selection state has changed. + +@@TBaseVirtualTree.OnChecked +Summary +Check support event. + +Description +Triggered when a node's check state has changed. + +@@TBaseVirtualTree.OnChecking +Summary +Check support event. + +Description +Triggered when a node's check state is about to change and allows to prevent the change. + +@@TBaseVirtualTree.OnCollapsed +Summary +Miscellaneous event. + +Description +Triggered after a node has been collapsed, that is, its child nodes are no longer displayed. + +@@TBaseVirtualTree.OnCollapsing +Summary +Miscellaneous event. + +Description +Triggered when a node is about to be collapsed and allows to prevent collapsing the node by setting Allowed to +false. + +@@TBaseVirtualTree.OnColumnClick +Summary +Header and column support event. + +Description +Triggered when the user released a mouse button over the same column in the client area on which the button was pressed +previously. + +See Also +OnHeaderClick + +@@TBaseVirtualTree.OnColumnDblClick +Summary +Header and column support event. + +Description +Same as OnColumnClick but for double clicks. + + + +See Also +OnColumnClick, OnHeaderDblClick + +@@TBaseVirtualTree.OnColumnResize +Summary +Header and column support routine. + +Description +Triggered when a column is being resized. During resize OnColumnResize is frequently hence you should make any code in +the associated event handle a short and fast as possible. + +@@TBaseVirtualTree.OnCompareNodes +Summary +Sort and search support event. + +Description +This event is the core event for all comparations between nodes. It is important that you write a handler for this +event if you want to sort nodes! + + + +Result must be set to less than 0 if Node1 is considered as being before Node2, equal to 0 if both a +considered being the same and greater than 0 if the first node is considered as being after node 2. Keep in mind that you +don't need to take sort direction into account. This is automatically handled by the tree. Simply return a comparation +\result as would there be an ascending sort order. + + + +Below is some sample code taken from the Advanced Demo: + + + +procedure TMainForm.VDT1CompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: Integer; + var Result: Integer); + +// used to sort the image draw tree + +var + Data1, + Data2: PImageData; + +begin + Data1 := Sender.GetNodeData(Node1); + Data2 := Sender.GetNodeData(Node2); + // folder are always before files + if Data1.IsFolder \<\> Data2.IsFolder then + begin + // one of both is a folder the other a file + if Data1.IsFolder then + \Result := -1 + else + \Result := 1; + end + else // both are of same type (folder or file) + \Result := CompareText(Data1.FullPath, Data2.FullPath); +end; + + +See Also +SortTree, Sort + +@@TBaseVirtualTree.OnCreateDataObject +Summary +Drag'n drop support event. + +Description +This event is called when the tree's drag manager needs a data object interface to start a drag'n drop operation. +Descentants (which override DoGetDataObject) or the application can return an own IDataObject implementation to support +special formats. + +@@TBaseVirtualTree.OnCreateDragManager +Summary +Drag'n drop support event. + +Description +This event is usually not used but allows power users to create their own drag manager to have different actions and/or +formats than the internal drag manager. + +@@TBaseVirtualTree.OnCreateEditor +Summary +Editing support event. + +Description +Allows to supply a customized node editor without changing the tree. TBaseVirtualTree triggers this event and raises an +exception if there no editor is returned. If you don't want this then disable edit support for nodes in +TreeOptions.MiscOptions. Descentants like TCustomVirtualStringTree supply a generic and simple string editor. + +See Also + + +@@TBaseVirtualTree.OnDragAllowed +Summary +Drag'n drop support event. + +Description +This event is called in the mouse button down handler to determine whether the application allows to start a drag +\operation. Since this check is done in sync with the other code it is much prefered over doing a manual +BeginDrag. + +Note +The OnDragAllowed event is called only if the current DragMode is dmManual. + +@@TBaseVirtualTree.OnDragDrop +Summary +Drag'n drop support event. + +Description +Triggered when either a VCL or a OLE drop action occured. Accepting drag and drop actions is not trivial. In order to +maintain a minimum compatibility with the VCL drag'n drop system Virtual Tree accepts not only OLE drop actions but also +those issued by the Delphi VCL (which is totally different to the OLE way, unfortunately), provided toAcceptOLEDrop is +set in TreeOptions.MiscOptions. The code snippet below is taken from a sample project provided with Virtual Tree. It +shows a general way to deal with dropped data. The following check list can be used as orientation and additional comment +to the code: + + + + 1. Determine what kind of drop data is passed. If DataObject is nil or Formats is empty then the drag + source is a VCL control. The event is not triggered for OLE drag'n drop if there is no OLE format is available (which + should never occur). + 2. If the event is triggered by a VCL control then use Source to access either the control or the drag + \object, depending on the circumstances of the action. + 3. For OLE drag'n drop iterate through the Formats list to find a format you can handle. + 4. If you find CF_VIRTUALTREE then the source of the drag operation is a Virtual Treeview. Since this is the native + tree format you can pass it to the Sender's ProcessDrop method which will take care to retrieve the data and act + depending on Effect and Mode. No further action by the application is usually required in this case. + 5. If you do not find CF_VIRTUALTREE then the operation has been initiated by another application, e.g. the + Explorer (then you will find CF_HDROP or CF_SHELLIDLIST in formats) or Notepad (then you will get CF_TEXT and perhaps + CF_UNICODETEXT) etc., depending on the data which is actually dropped. + 6. Use the provided DataObject to get the drop data via IDataObject.GetData and act depending on the format + you get. + 7. Finally set Effect to either DROPEFFECT_COPY, DROPEFFECT_MOVE or DROPEFFECT_NONE to indicate which + \operation needs to be finished in Sender when the event returns. If you return DROPEFFECT_MOVE then all marked + nodes in the source tree will be deleted, otherwise they stay where they are. + + + +procedure TMainForm.VTDragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject; + const Formats: array of Word; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode); + +var + I: Integer; + AttachMode: TVTNodeAttachMode; + +begin + if Length(Formats) \> 0 then + begin + // OLE drag'n drop + // If the native tree format is listed then use this and accept the drop, otherwise recject (ignore) it. + // It is recommend by Microsoft to order available clipboard formats in decreasing detail richness so + // the first best format which we can accept is usually the best format we can get at all. + for I := 0 to High(Formats) do + if Formats[I] = CF_VIRTUALTREE then + begin + case Mode of + dmAbove: + AttachMode := amInsertBefore; + dmOnNode: + AttachMode := amAddChildLast; + dmBelow: + AttachMode := amInsertAfter; + else + if Assigned(Source) and (Source is TBaseVirtualTree) and (Sender \<\> Source) then + AttachMode := amInsertBefore + else + AttachMode := amNowhere; + end; + // in the case the drop target does an optimized move Effect is set to DROPEFFECT_NONE + // to indicate this also to the drag source (so the source doesn't need to take any further action) + Sender.ProcessDrop(DataObject, Sender.DropTargetNode, Effect, AttachMode); + Break; + end; + end + else + begin + // VCL drag'n drop, Effects contains by default both move and copy effect suggestion, + // as usual the application has to find out what operation is finally to do + Beep; + end; +end; + + + +@@TBaseVirtualTree.OnDragOver +Summary +Drag'n drop support event. + +Description +Triggered when Sender is the potential target of a drag'n drop operation. You can use this event to allow or deny a drop +\operation by setting Allowed to True or False, respectively. For conditions of OLE or VCL drag source see OnDragDrop. + +See Also +OnDragDrop + +@@TBaseVirtualTree.OnEditCancelled +Summary +Editing support event. + +Description +Triggered when an edit action has been cancelled. + +See Also + + +@@TBaseVirtualTree.OnEdited +Summary +Editing support event. + +Description +Triggered when an edit action has successfully been finished. + +See Also + + +@@TBaseVirtualTree.OnEditing +Summary +Editing support event. + +Description +Triggered when a node is about to be edited. Use Allowed to allow or deny this action. + +See Also + + +@@TBaseVirtualTree.OnExpanded +Summary +Misscellaneous event. + +Description +Triggered after a node has been expanded. + +@@TBaseVirtualTree.OnExpanding +Summary +Miscellaneous event. + +Description +Triggered just before a node is expanded. Use Allowed to allow or deny this action. + +@@TBaseVirtualTree.OnFocusChanged +Summary +Navigation support event. + +Description +Triggered after the focused node changed. When examining Node keep in mind that it can be nil, meaning there is no +focused node. + +@@TBaseVirtualTree.OnFocusChanging +Summary +Navigation support event. + +Description +Triggered when the node focus is about to change. You can use Allowed to allow or deny a focus change. Keep in +mind that either the old or the new node can be nil. + +@@TBaseVirtualTree.OnFreeNode +Summary +Data management node. + +Description +Triggered when a node is about to be freed. This is the ideal place to free/disconnect your own data you associated with Node. +Keep in mind, that data which is stored directly in the node does not need to be free by the application. This is part of +the node record and will be freed when the node is freed. You should however finalize the data in such a case if it +contains references to external memory objects (e.g. variants, strings, interfaces). + +@@TBaseVirtualTree.OnGetCursor +Summary +Miscellaneous event. + +Description +This event is triggered from the WM_SETCURSOR message to allow the application use several individual cursors for a tree. +The Cursor property allows to set one cursor for the whole control but not to use separate cursors for different tree +parts. + +@@TBaseVirtualTree.OnGetHeaderCursor +Summary +Header and column support event. + +Description +This event is triggered from the WM_SETCURSOR message to allow the application to define individual cursors for the +header part of the tree control. + +@@TBaseVirtualTree.OnGetHelpContext +Summary +Miscellaneous event. + +Description +This event is usually triggered when the user pressed F1 while the tree has the focus. The tree is iteratively traversed +all the way up to the top level parent of the given node until a valid help context index is returned (via this event). +When the loop reaches the top level without getting a help index then the tree control's help index is used. If the tree +itself does not have a help context index then a further traversal is initiated going up parent by parent of each control +in the current window hierarchy until either a valid index is found or there is no more window parent. + +@@TBaseVirtualTree.OnGetImageIndex +Summary +Display management event. + +Description +This event is triggered whenever the tree needs the index of an image, be it the normal, the selected or the state image. +The event should be as fast as possible because it is at times frequently called when the layout of the node must be +determined, e.g. while doing draw selection with the mouse or painting the tree. Kind determines which image is +needed and Column determines for which column of the node the image is needed. This value can be -1 to indicate +there is no column used. The parameter Ghosted can be set to true to blend the image 50% against the tree +background and can be used for instance in explorer trees to mark hidden file system objects. Additionally nodes are also +drawn with a ghosted icon if the are part of a cut set during a pending cut-to-clipboard operation. In this case changing +the ghosted parameter has no effect. + +Note +Blending nodes can be switched by using toUseBlendImages in TreeOptions.PaintOptions. + +@@TBaseVirtualTree.OnGetLineStyle +Summary +Display management event. + +Description +This event is used to customize the appearance of the tree and grid lines and is only triggered if the LineStyle property +is set to lsCustomStyle. The event must return a pointer to an array containing bits for an 8 x 8 pixel image with word +aligned entries. For more info see PrepareBitmaps and the Windows APIs CreateBitmap and CreatePatternBrush. + +Note +It is important that you do not use dynamically allocated memory in this event (also no local variables on the stack). If +you do so then either the memory is not valid on return of the event (if allocated on stack) or will never be freed (if +allocated with a memory manager). Instead use a constant array and return its address. +See Also +PrepareBitmaps + +@@TBaseVirtualTree.OnGetNodeDataSize +Summary +Data management event. + +Description +Triggered when access to a node's data happens the first time but the actual data size is not yet set. Usually you would +specify the size of the data you want to have added to each node by NodeDataSize, e.g. SizeOf(TMyRecord) is quite usual +there (where TMyRecord is the structure you want to have stored in the node). Sometimes, however it is not possible to +determine the node size in advance, so you can leave NodeDataSize being -1 (the default value) and the OnGetNodeDataSize +event is triggered as soon as the first regular node is created (the hidden root node does not have user data but +\internal data which is determined by other means). + +See Also +NodeDataSize, + +@@TBaseVirtualTree.OnGetPopupMenu +Summary +Miscellaneous event. + +Description +This event allows the application to return a popup menu which is specific to a certain node. The tree does an automatic +traversal all the way up to the top level node which is the parent of a given node to get a popup menu. If Menu is +set then the traversal stops. Otherwise it continues until either a menu is set, AskParent is set to False or the top +level parent has been reached. + +@@TBaseVirtualTree.OnGetUserClipboardFormats +Summary +Drag'n drop and clipboard support event. + +Description +Whenever the tree needs to specify the available clipboard formats for a clipboard or drag'n drop operation it calls this +event too, to allow the application or descentants (which would override DoGetUserClipboardFormats) to specify own +formats which can be rendered. Since the build-in data object does not know how to render formats which are specified +here you have to supply a handler for the OnRenderOLEData event or an own IDataObject implementation to fully support +your own formats. + + + +Use the Formats parameter which is an open array and add the identifiers of your formats (which you got when you +registered the format). + +@@TBaseVirtualTree.OnHeaderClick +Summary +Header & column support event. + +Description +This event is triggered when the user clicks on a header button and is usually a good place to set the current SortColumn +and SortDirection. + +See Also +SortColumn, SortDirection + +@@TBaseVirtualTree.OnHeaderDblClick +Summary +Header & column support event. + +Description +Unlike OnHeaderClick this event is triggered for double clicks on any part of the header and comes with more detailed +information like shift state, which mouse button caused the event and the mouse position. + +See Also +OnHeaderClick + +@@TBaseVirtualTree.OnHeaderDragged +Summary +Header & column support event. + +Description +Triggered after the user has released the left mouse button when a header drag operation was active. Column +contains the index of the column which was dragged. Use this index for the Columns property of the header to find out the +current position. OldPosition is the position which Column occupied before it was dragged around. + +@@TBaseVirtualTree.OnHeaderDraggedOut +Summary +Header & column support event. + +Description +When during a header drag operation the mouse moves out of the header rectangle and the mouse button is released then an +OnHeaderDraggedOut event will be fired with the target mouse position in screen coordinates. + +@@TBaseVirtualTree.OnHeaderDragging +Summary +Header & column support event. + +Description +Triggered just before dragging of a header button starts. Set Allowed to False if you want to prevent the drag +\operation of the given column. + +@@TBaseVirtualTree.OnHeaderDraw +Summary +Header & column support event. + +Description +If you set the hoOwnerDraw style in TVTHeader.Options and a column has been set to vsOwnerDraw (see also +TVirtualTreeColumn.Style) then OnDrawHeader is called whenever a column needs painting. + +@@TBaseVirtualTree.OnHeaderDrawQueryElements +Summary +Header & column support event. + +Description +Used for advanced header painting to query the application for the elements, which are drawn by it and which should be +drawn by the tree. + + + +See Also +OnAdvancedHeaderDraw + +@@TBaseVirtualTree.OnHeaderMouseDown +Summary +Header & column support event. + +Description +This event is similar to OnHeaderClick but comes with more detailed information like shift state, which mouse button +caused the event and the mouse position. + +@@TBaseVirtualTree.OnHeaderMouseMove +Summary +Header & column support event. + +Description +This event is triggered when the mouse pointer is moved over the header area. + +@@TBaseVirtualTree.OnHeaderMouseUp +Summary +Header & column support event. + +Description +This event is very much like OnHeaderMouseDown but is triggered when a mouse button is released. + +@@TBaseVirtualTree.OnHotChange +Summary +Navigation support event. + +Description +This event is triggered if hot tracking is enabled (see also TreeOptions.PaintOptions) and when the mouse pointer moves +from one node caption to another. In full row select mode most parts of a node are considered as being part of the +caption. + +@@TBaseVirtualTree.OnIncrementalSearch +Summary +Miscellaneous event. + +Description +This event is integral part of the incremental search functionality (see also Keyboard, hotkeys and incremental search). +It is triggered during search for a node which matches the given string. Similar to other compare routines return a value +\< 0 if the node's caption is considered as being before the given text, = 0 if it is the same and \> 0 if it is +considered being after the given text. + + + +procedure TfrmProperties.VST3IncrementalSearch(Sender: TBaseVirtualTree; Node: PVirtualNode; const Text: WideString; + var Result: Integer); + +var + S, PropText: string; + + +begin + // Note: This code requires a proper Unicode/WideString comparison routine which I did not want to link here for + // size and clarity reasons. For now strings are (implicitly) converted to ANSI to make the comparison work. + // Search is not case sensitive. + S := Text; + if Node.Parent = Sender.RootNode then + begin + // root nodes + if Node.Index = 0 then + PropText := 'Description' + else + PropText := 'Origin'; + end + else + begin + PropText := PropertyTexts[Node.Parent.Index, Node.Index, ptkText]; + end; + + // By using StrLIComp we can specify a maximum length to compare. This allows us to find also nodes + // which match only partially. + \Result := StrLIComp(PChar(S), PChar(PropText), Min(Length(S), Length(PropText))) +end; + +Note +Usually incremental search allows to match also partially. Hence it is recommended to do comparison only up to the length +\of the shorter string. + +@@TBaseVirtualTree.OnInitChildren +Summary +Node management event. + +Description +In order to allow the tree only to fill content where needed it is possible to set the vsHasChildren style in a node's +initializaton whithout really adding any child nodes. These child nodes must be initialized first when they are about to +be displayed or another access (like search, iteration etc.) occurs. + + + +The application usually prepares data needed to fill child nodes when they are initialized and retrieves the actual +number. Set ChildCount to the number of children you want. + +See Also + + +@@TBaseVirtualTree.OnInitNode +Summary +Node management event. + +Description +This event is important to connect the tree to your internal data. It is the ideal place to put references or whatever +you need into a node's data area. You can set some initial states like selection, expansion state or that a node has +child nodes. + +See Also + + +@@TBaseVirtualTree.OnKeyAction +Summary +Miscellaneous event. + +Description +This event is a convinient way for the application or descentant trees to change the semantic of a certain key stroke. It +is triggered when the user presses a key and allows either to process that key normally (leave DoDefault being +True) or change it to another key instead (set DoDefault to False then). This way a key press can change its +meaning or entirely be ignored (if CharCode is set to 0). + +@@TBaseVirtualTree.OnLoadNode +Summary +Streaming support event. + +Description +This event is typically triggered when serialized tree data must be restored, e.g. when loading the tree from file or +stream or during a clipboard/drag'n drop operation. You should only read in what you wrote out in OnSaveNode. For safety +there is a check in the loader code which tries to keep the internal serialization structure intact in case the +application does not read correctly. + +See Also +OnSaveNode, LoadFromStream, SaveToStream, AddFromStream, VTTreeStreamVersion, TVTHeader.LoadFromStream, +TVTHeader.SaveToStream + +@@TBaseVirtualTree.OnMeasureItem +Summary +Miscellaneous event. + +Description +Virtual Treeview supports individual node heights. However it might sometimes unpractical to set this height in advance +(e.g. during OnInitNode). Another scenario might be that multi line nodes must size themselves to accomodate the entire +node text without clipping. For such and similar cases the event OnMeasureItem is for. It is queried once for each node +and allows to specify the node's future height. If you later want to have a new height applied (e.g. because the node's +text changed) then call InvalidateNode for it and its vsHeightMeasured state is reset causing so the tree to trigger the +OnMeasureItem event again when the node is painted the next time. + + + +See Also +InvalidateNode, vsHeightMeasured + +@@TBaseVirtualTree.OnNodeCopied +Summary +Miscellaneous event. + +Description +This event is triggered during drag'n drop after a node has been copied to a new location. Sender is the target tree +where the copy operation took place. + +@@TBaseVirtualTree.OnNodeCopying +Summary +Miscellaneous event. + +Description +This event is triggered when a node is about to be copied to a new location. Use Allowed to allow or deny the +action. Sender is the target tree where the copy operation will take place. + +@@TBaseVirtualTree.OnNodeMoved +Summary +Miscellaneous event. + +Description +This event is very much like OnNodeCopied but used for moving nodes instead. + +@@TBaseVirtualTree.OnNodeMoving +Summary +Miscellaneous event. + +Description +This event is very much like OnNodeCopying but used for moving nodes instead. + +@@TBaseVirtualTree.OnPaintBackground +Summary +Paint support event. + +Description +This event is triggered when the tree has finished its painting and there is an area which is not covered by nodes. For +nodes there are various events to allow background customizaton. For the free area in the tree window there is this +event. + +@@TBaseVirtualTree.OnRenderOLEData +Summary +Drag'n drop and clipboard support event. + +Description +This event is triggered when the data in a clipboard or drag'n drop operation must be rendered but the built-in data +\object does not know the requested format. This is usually the case when the application (or descentants) have specified +their own formats in OnGetUserClipboardFormats. + +@@TBaseVirtualTree.OnResetNode +Summary +Node management event. + +Description +For large trees or simply because the content changed it is sometimes necessary to discard a certain node and release all +its children. This can be done with ResetNode which will trigger this event. + +See Also +ResetNode + +@@TBaseVirtualTree.OnSaveNode +Summary +Streaming support event. + +Description +This event is triggered whenever a certain node must be serialized into a stream, e.g. for saving to file or for copying +to another tree/node during a clipboard or drag'n drop operation. Make sure you only store non-transient data into the +stream. Pointers (including long/wide string references) are transient and the application cannot assume to find the data +a pointer references on saving at the same place when the node is loaded (see also OnLoadNode). This is even more +essential for nodes which are moved or copied between different trees in different processes (applications). Storing +strings however is easily done by writing the strings as a whole into the stream. + +Note +For exchanging data between different trees and for general stability improvement I strongly recommend that you insert a +kind of identifier as first stream entry when saving a node. This identifier can then be used to determine what data will +follow when loading the node later and does normally not required to be stored in the node data. +See Also +OnLoadNode, LoadFromStream, SaveToStream, AddFromStream, VTTreeStreamVersion, TVTHeader.LoadFromStream, +TVTHeader.SaveToStream + +@@TBaseVirtualTree.OnScroll +Summary +Miscellaneous event. + +Description +This event is triggered when the tree is scrolled horizontally or vertically. You can use it to synchronize scrolling of +several trees or other controls. + +See Also +OffsetXY + +@@TBaseVirtualTree.OnStateChange +Summary +Miscellaneous event. + +Description +For special effects or in order to increase performance it is sometimes useful to know when the tree changes one of its +\internal states like tsIncrementalSearching or tsOLEDragging. The OnStateChange event is triggered each time such a +change occurs letting so the application take measures for it. + +@@TBaseVirtualTree.OnStructureChange +Summary +Miscellaneous event. + +Description +This event is triggered when a change in the tree structure is made. That means whenever a node is created or destroyed +\or a node's child list is change (because a child node was moved, copied etc.) then OnStructureChange is executed. + +@@TBaseVirtualTree.OnUpdating +Summary +Miscellaneous event. + +Description +This event is triggered when the application or the tree call BeginUpdate or EndUpdate and indicate so when a larger +update operation takes place. This can for instance be used to show a hour glass wait cursor. + +@@TBaseVirtualTree.RootNode +Summary +Reference to the internal root node which is the anchor of the entire tree node hierarchy. + +Description +For anchoring the tree hierarchy an internal tree node is maintained which is mostly just like any other tree node but +has sometimes differently handled. The root node is always expanded and initialized. Its parent member points to the +treeview to which the node belongs to and its PreviousSibling and NextSibling members point to the root node itself to +make it possible to actually recognize this node. + +Note +You should not use the root node to iterate through the tree. It is only publicly accessible because it is the parent of +all top level nodes and can be used to test a node whether it is a top level node or not. + +@@TBaseVirtualTree.RootNodeCount +Summary +Read or set the number of nodes on the top level. + +Description +Usually setting RootNodeCount is all what is needed to initially fill the tree. When one of the top level nodes is +initialized you can set its ivsHasChildren style. This will then cause to ask to initialize the child nodes. Recursively +applied, you can use this principle to create tree nodes on demand (e.g. when their parent is expanded). + +@@TBaseVirtualTree.ScrollBarOptions +Summary +Reference to the scroll bar options class. + +Description +Like many other aspects in Virtual Treeview also scrollbars can be customized. See the class itself for further +descriptions. + +@@TBaseVirtualTree.SearchBuffer +Summary +Current input string for incremental search. + +Description +When incremental search is active you can use SearchBuffer to get the input string typed by the user, which created the +last match. + +See Also +IncrementalSearch + +@@TBaseVirtualTree.Selected +Summary +Property to modify or determine the selection state of a node. + +Description +This array property is used to test whether a given node is selected or to switch its selection state. Note that the +selection state has nothing to do with the . Only one node can be +focused while any number of nodes can be selected (read: can be marked with the selection flag to paint their caption +differently). Selection is mainly used to mark nodes for clipboard and drag'n drop operations. + +@@TBaseVirtualTree.SelectedCount +Summary +Contains the number of selected nodes. + +Description +If multiselection is enabled (toMultiSelect) then SelectedCount will contain the actual number of selected nodes. In +\order to change the selection state of a node use Selected or AddToSelection/RemoveFromSelection. + +@@TBaseVirtualTree.SelectionBlendFactor +Summary +Read or set the current blend factor for the multi selection rectangle and the node selection rectangle. + +Description +For a visually appealing tree some operations use alpha blending. One of these operations is multi selection using the +mouse. Another one is the rectangle drawn around the caption of selected nodes. Both rectangles use the +SelectionBlendFactor to determine how much of the underlying tree image and how much of the rectangles should be seen. +The factor can be in the range of [0..255] where 0 means the rectangle is fully transparent and 255 it is fully opaque. + + + +If you don't like to use blended node selection rectangles then switch them off by removing toUseBlendedSelection from +TVTPaintOptions. For selecting a certain multi selection rectangle style use DrawSelectionMode. + + + +Note +Alpha blending is only enabled when the current processor supports MMX instructions. If MMX is not supported then a +dotted draw selection rectangle and an opaque node selection rectangle is used. +See Also +DrawSelectionMode, TVTPaintOptions + +@@TBaseVirtualTree.SelectionCurveRadius +Summary +Read or set the current corner radius for node selection rectangles. + +Description +This is a special property to determine the radius of the corners of the selection rectangle for a node caption. Virtual +Treeview supports not only simple rectangular selection marks but also such with rounded corners. This feature, however, +is only available if blended node selection rectangles are disabled. + +See Also +SelectionBlendFactor, DrawSelectionMode, TVTPaintOptions + +@@TBaseVirtualTree.StateImages +Summary +Reference to the images list which is used for the state images. + +Description +Each node can (in each column) have several images. One is the check image which is supplied by internal image lists or a +special external list (see also CustomCheckImages). Another one is the state image and yet another one the +normal/selected image. + + + +See Also +CheckImages, Images + +@@TBaseVirtualTree.TextMargin +Summary +Read or set the distance of the node caption to its borders. + +Description +TextMargin is used to define a border like area within the content rectangle of a node. This rectangle is the area of the +node less the space used for indentation, images, lines and node margins and usually contains the text of a node. In +\order to support finer adjustment there is another margin, which only applies to the left and right border in the +content rectangle. This is the text margin. + + + +See Also +Margin + +@@TBaseVirtualTree.TopNode +Summary +The top node is the node which is currently at the top border of the client area. + +Description +This property is a reference to the node which is the first node which is at least partially visible in the client area. + +@@TBaseVirtualTree.TotalCount +Summary +\Returns the number of nodes in the tree. + +Description +Use this property to get the overall number of nodes currently in the tree. This will validate all nodes in the control +so that also not yet created child nodes are counted. + + + +Note +This property is quite counter productive as it causes the entire tree to be validated when queried. This means that each +node is initialized, including its children and grandchildren etc. creating so a full blown treeview (if not already +done) which might keep much memory allocated (not counted the time necessary to validate all nodes). Therefore I +discourage the use of the property unless it is really necessary. + +@@TBaseVirtualTree.TotalInternalDataSize +Summary +Keeps the currently accumulated data size for one node. + +Description +Each node in the tree not only supports user data but also an interal area where TVirtualBaseTree descentants can store +their own data per node. This internal data area must be allocated by a tree class, that means it must register its need +for internal data. The internal data size registered by each descendant is accumulated in the TotalInternalDataSize +member and is used to compute the user data offset in the node record. + + + +See Also + + +@@TBaseVirtualTree.TreeOptions +Summary +Reference to the tree's options. + +Description +The tree options are one of the main switchs to modify a treeview's behavior. Virtual Treeview supports customizing tree +\options by descentants. This allows very fine adjustments for derived tree classes, including the decision which +properties should be published. For more information about the base options see TCustomVirtualTreeOptions and its +descentants. + +@@TBaseVirtualTree.TreeStates +Summary +Property which keeps a set of flags which indicate current operation and states of the tree. + +Description +Often it is extremly helpful to know what action is currently happening in the tree. TreeStates gives you this +information, be it that the caches are currently validated, a drag operation is in progress, the tree has delayed data on +the clipboard or a large update operation is under work. You can greatly optimize your code with this knowledge. + + + +See Also +OnStateChange + +@@TBaseVirtualTree.VerticalAlignment +Summary +Used to set a node's vertical button aligment with regard to the entire node rectangle. + +Description +The given value is interpreted differently depending on the value of NodeAlignment. By default the alignment used +relatively with regard to the top bound. In this case a range of 0 through 100 must be used which denotes the relative +pixel amount in percent. The other variants work with absolute pixel values from top or bottom bound. + +@@TBaseVirtualTree.VisibleCount +Summary +Number of currently visible nodes. + +Description +Visible nodes are those nodes which have the vsVisible flag set in their states. + +@@TBaseVirtualTree.VisiblePath +Summary +Property to set or determine a node parent's expand states. + +Description +A node has a visible path when all of its parent nodes are expanded. Setting this property to True will expand all parent +nodes of Node if not yet done. + + + +See Also +Visible + +@@TBaseVirtualTree.WantTabs +Summary +Read or set whether the tree wants to process tabs on its own. + +Description +Usually tab kex strokes advance the input focus from one control to another on a form. For special processing however it +is necessary to let the control decide what to do with the given tabulator character. Virtual Treeview needs this +character mainly for its grid emulation. + +@@TBaseVirtualTree.AbsoluteIndex@PVirtualNode +Summary +Reads the overall index of a node. + +Description +Indicates the index of the tree node relative to the first tree node in a tree. + + + +Note +Similar to TotalCount also with AbsoluteIndex the entire tree will be validated, with all consequences like high memory +usage etc. And since Virtual Treeview is a highly changing environment there is not much sense to use the absolute index. +You cannot use it in any method or property of the control. + +@@TBaseVirtualTree.AddChild@PVirtualNode@Pointer +Summary +Creates and adds a new child node to given node. + +Description +The new node will be created as last child of Parent and is returned as result. + +Note +Using AddChild is not recommended. The method is merely there for easier migration from TTreeview. The reason is that the +method has to validate the node and does some other processing, which prevents the tree from utilizings its virtual +paradigm. Important advantages will so disappear. If possible you should restructure your design and try to use the right +way: via OnInitNode and OnInitChildren. + +See Also +InsertNode, OnInitNode, OnInitChildren + +@@TBaseVirtualTree.AddFromStream@TStream@PVirtualNode +Summary +Adds the content from the given stream to the given node. + +Description +AddFromStream restores the subtree stored in Stream and adds it to TargetNode. The content of the stream must have been +saved previously with SaveToStream. + + + +See Also +SaveToStream + +@@TBaseVirtualTree.AddToSelection@PVirtualNode +Summary +Adds one or more nodes to the current selection. + +Description +AddToSelection either takes a single node or an array of nodes and adds them to the current seletion in the tree. In this +process also the vsSelected state of the node is set. NewLength is the amount of nodes to add (necessary to allow NewItems +to be larger than the actual used entries). ForceInsert is true if nodes must be inserted without consideration of +level select constraint or already set selected flags (e.g. when loading from stream). + +Note +In the case ForceInsert is true the caller is responsible for making sure the new nodes aren't already in the +selection array! + +@@TBaseVirtualTree.AddToSelection@TNodeArray@Integer@Boolean + + +@@TBaseVirtualTree.AdjustPaintCellRect@TVTPaintInfo@TColumnIndex +Summary +Used in descentants to modify the clip rectangle of the current column while painting a certain node. + +Description +The rectangle for the given cell (node, column pair in PaintInfo) can be adjusted by descendants to make room for +special drawings, if necessary. + +@@TBaseVirtualTree.AdjustPanningCursor@Integer@Integer +Summary +Loads the proper cursor which indicates into which direction scrolling is done. + +Description +Wheel mice support a special mode for their wheel, which is used in many applications. By pressing the wheel (which is +also a button) you can start so called wheell panning. In this mode the tree window is smoothly scrolled in the +direction to which the mouse pointer is moved. As soon as you release the wheel button wheel panning is stopped. A second +form of this feature is referred to as wheel scrolling. It is basically the same as wheel panning but is entered +when you release the wheel button before you moved the mouse. In this mode you can move the mouse and do the tree +scrolling without holding the wheel all the time. To stop this mode simple turn the wheel, or click any mouse button. +Also pressing ESC will cause to leave the wheel scrolling mode. + + + +Depending on the direction the tree content is scroll also the mouse cursor must be adjusted to indicate this direction. +AdjustPanningCursor does this. + +@@TBaseVirtualTree.AdviseChangeEvent@Boolean@PVirtualNode@TChangeReason +Summary +Used to register a delayed change event. + +Description +Often there can be many change events in a row and calling the application for each of them might be too time costly. So +they are by default accumulated until a certain time has elapsed (ChangeDelay) or, if BeginUpdate was called, until +EndUpdate is executed. If StructureChange is False then we have a selection change event (without a specific +reason) otherwise it is a structure change. + + + +There are two possibilities to avoid delayed change events. One is the permanent way by setting ChangeDelay to 0, the +\other one is to enter the synchronous mode by calling BeginSynch. + +@@TBaseVirtualTree.AllocateInternalDataArea@Cardinal +Summary +Registration method to allocate tree internal data per node. + +Description +This method is used for descentants to specify their need for internal data. Each node contains some extra reserved bytes +between the node's normal members and the user data area. This internal area can be used to cache additional information, +e.g. the string tree keeps here the width of the node's caption in the main column for quick hit tests when doing draw +selection with the mouse. + + + +A tree implementation must call this method only once and before any node is created (except the hidden root node which +is handled accordingly). The result value is the offset from the start of the node to the internal data area of the node +for this tree class. I recommend to implement an access method called InternalData (as shown in TCustomVirtualStringTree) +which does the pointer mathematic. + + + +See Also +, TotalInternalDataSize + +@@TBaseVirtualTree.Animate@Cardinal@Cardinal@TVTAnimationCallback@Pointer +Summary +Support method for animated actions in the tree view. + +Description +This method is a general purpose helper to do an animation and is used for hint fading, animated node toggling etc. The +method automatically takes care that the animation is done within the specified time interval. For each step in the +animation loop the provided callback is called which gets Data passed as parameter. + +@@TBaseVirtualTree.Assign@TPersistent +Summary +Used to copy properties from another Virtual Treeview. + +Description +Although this method assignes most tree properties it does not assign the header and the nodes to the new tree. There is +an own method (TVTHeader.Assign) for the header assignment. In order to copy the nodes you must save them to a stream and +restore them in the other control- + +@@TBaseVirtualTree.BeginDrag@Boolean@Integer +Summary +Starts an OLE drag'n drop operation. + +Description +This method is called within the mouse down handler when DragMode is set to dmAutomatic. Manual start of a drag operation +is not recommended as it confuses the correct mouse down handling which is quite complex in Virtual Treevew. If you +selectively want to allow to start a drag operation then use the OnDragAllowed event which is called when DragMode is +dmManual. + +@@TBaseVirtualTree.BeginSynch +Summary +Enters the tree into a special synchronized mode. + +Description +Similar to BeginUpdate does BeginSynch provide a mechanism to bring certain events into a common line. That means, +whenever you need to make sure change events are called before a modification in the tree is finished (e.g. when changing +the focus or selection) then use the synchronous mode started with BeginSynch (and stopped with EndSynch). + +@@TBaseVirtualTree.BeginUpdate +Summary +Locks the tree view to perform several update operations. + +Description +Call this method when a long lasting operation begins which might involve manipulation of many nodes. + +@@TBaseVirtualTree.CalculateSelectionRect@Integer@Integer +Summary +Support method for draw selection. + +Description +Recalculates old and new selection rectangle given that X, Y are new mouse coordinates. The function returns true if +there was a change since the last call. + +@@TBaseVirtualTree.CanAutoScroll +Summary +Determines whether the tree can currently auto scroll its window. + +Description +This method was created because the conditions when the tree may automatically scroll its content are quite complex. +Additionally, tree descendants might want to add further limitations. Thus the determination has been put into an own +method which returns true if the tree is allowed to scroll, otherwise False. + +@@TBaseVirtualTree.CancelCutOrCopy +Summary +Canceles any pending cut or copy clipboard operation. + +Description +This method is used to stop any pending clipboard operation. No data is transfered nor are nodes deleted. + +@@TBaseVirtualTree.CancelEditNode +Summary +Cancel the current edit operation, if there is any. + +Description +Used to stop the current edit operation.The node editor will get a CancelEdit call so that the node is not changed. + +@@TBaseVirtualTree.CanEdit@PVirtualNode@TColumnIndex +Summary +Determines whether a node can be edited or not. + +Description +The method is called when the tree is about to start a node edit operation. Returns true if editing is allowed, otherwise +false. + +@@TBaseVirtualTree.CanFocus +Summary +Support method to determine whether the tree window can receive the input focus. + +Description +The method adds a check for the parent form of the control. + +@@TBaseVirtualTree.CanShowDragImage +Summary +Determines whether a drag image should be shown. + +Description +This overridable method is used to determine whether a drag image can be shown or not. + +@@TBaseVirtualTree.Change@PVirtualNode +Summary +Central method called when a node's selection state changes. + +Description +The Change method is called to trigger the change notifcation chain. Depending on the sync and the update states of the +tree as well as the ChangeDelay value either the application is directly notified about the change or a timer is started +to accumulate several change events into one. + + + +See Also +BeginSynch, EndSynch, BeginUpdate, EndUpdate, ChangeDelay + +@@TBaseVirtualTree.ChangeScale@Integer@Integer +Summary +Helper method called by the VCL when control resizing is due. + +Description +ChangeScale is a method introduced by TControl. In Virtual Treeview it is responsible to change the tree's and the +header's fonts as well as to compute the new default node height. + + + +See Also +TVTHeader.ChangeScale, DefaultNodeHeight + +@@TBaseVirtualTree.CheckParentCheckState@PVirtualNode@TCheckState +Summary +Helper method for recursive check state changes. + +Description +Checks all siblings of node to determine which check state Node's parent must get. + +@@TBaseVirtualTree.Clear +Summary +Clears the tree and removes all nodes. + +Description +All pending operations are stopped and the tree is ready to receive new nodes. + +@@TBaseVirtualTree.ClearSelection +Summary +Removes all nodes from the current selection. + +Description +ClearSelection empties the internal selection cache and resets the vsSelected state from all nodes, which were in this +array. + +@@TBaseVirtualTree.ClearTempCache +Summary +Helper method to clear the internal temporary node cache. + +Description +The internal node cache is used when more than one node is involved in certain operations (e.g. including a range of +nodes into the current selection). + +@@TBaseVirtualTree.ColumnIsEmpty@PVirtualNode@TColumnIndex +Summary +Used to determine if a cell is considered as being empty. + +Description +An empty cell might be used for the automatic column spanning feature. Descentants can override this method to modify the +tree's behavior. + + + +See Also +toAutoSpanColumns + +@@TBaseVirtualTree.CopyTo@PVirtualNode@TBaseVirtualTree@TVTNodeAttachMode@Boolean + + +@@TBaseVirtualTree.CopyTo@PVirtualNode@PVirtualNode@TVTNodeAttachMode@Boolean +Summary +Copies Source and all its child nodes to Target. + +Description +Mode is used to specify further where to add the new node actually (as sibling of Target or as child of +Target). Result is the newly created node to which source has been copied if ChildrenOnly is False or just +contains Target in the other case. ChildrenOnly determines whether to copy also the source node or only its +child nodes. + + + +The variant taking a tree reference as target can be used to transfer nodes to a different tree, without determining its +root node first. However one can also pass in any virtual tree node as target, as long as it belongs to a tree. The +\owning tree is automatically determined. + +@@TBaseVirtualTree.MoveTo@PVirtualNode@TBaseVirtualTree@TVTNodeAttachMode@Boolean + + +@@TBaseVirtualTree.MoveTo@PVirtualNode@PVirtualNode@TVTNodeAttachMode@Boolean +Summary +Moves Source and all its child nodes to Target. + +Description +Moves the given node (and all its children) to Target. Source must belong to the tree instance which calls +this MoveTo method. Mode determines how to connect Source to Target. This method might involve a +change of the tree if Target belongs to a different tree than Source. + + + +The variant taking a tree reference as target can be used to transfer nodes to a different tree, without determining its +root node first. However one can also pass in any virtual tree node as target, as long as it belongs to a tree. The +\owning tree is automatically determined and an optimized path is taken if the operation happens within one tree. In this +case simply the source node is disconnected from the old place and reconnected at the new location. + +@@TBaseVirtualTree.CopyToClipBoard +Summary +Copies all currently selected nodes to the clipboard. + +Description +CopyToClipboard causes the tree to copy the currently selected nodes to the clipboard. Actually, Virtual Treeview +maintains socalled delayed rendering. This means the participating nodes are marked as being in the current clipboard set +(see vsCutOrCopy in TVirtualNodeStates) and only an IDataObject interface is placed onto the clipboard but no data yet. +This avoids not only possibly huge memory requirements but it also avoids rendering data in a format which is not +necessary. The application which pastes the clipboard content later will get the IDataObject interface and requests the +format it can handle. The actual data is then rendered when the target application calls IDataObject.GetData, which +results in a call to RenderOLEData. + +@@TBaseVirtualTree.CutToClipBoard +Summary +Copies the currently selected nodes to the clipboard and removes them once a consumer has taken the data. + +Description +Similar to CopyToClipboard only the nodes are deleted after they have been pasted into the target. + +@@TBaseVirtualTree.DeleteChildren@PVirtualNode@Boolean +Summary +Removes all child nodes from the given node. + +Description +The method works recursively: all grandchildren and their children are removed as well. + +@@TBaseVirtualTree.DeleteNode@PVirtualNode@Boolean +Summary +Removes the given node from the tree. + +Description +This method deletes the given node. If the node was initialized or had gotten initial data via the AddChild or InsertNode +then the event OnFreeNode is called to allow the application to free any user data attached to a node. + +@@TBaseVirtualTree.DeleteSelectedNodes +Summary +Removes all currently selected nodes form the tree. + +Description +All nodes in the current selection are affected. + +@@TBaseVirtualTree.DoCancelEdit +Summary +Called when the tree should stop editing without accepting changed values. + +Description +This method calls the edit link's IEditLink.CancelEdit method and stops the edit mode if this call returns True. If +stopping is allowed then the event OnEditCancelled is triggered and a message is sent to release the edit link +asynchronously. + +@@TBaseVirtualTree.DoDragging@TPoint +Summary +\Internal method which handles drag' drop. + +Description +This method starts the OLE drag'n drop operation and returns after this operation is finished. + +@@TBaseVirtualTree.DoEdit +Summary +Initiates editing of the currently set focused column and edit node. + +Description +This method takes care for editor creation and initialization. You can look for tsEditing in TreeStates to know whether +editing is currently active. + + + +See Also +tsEditing, OnCreateEditor, IVTEditLink + +@@TBaseVirtualTree.DoEndEdit +Summary +Stops the current edit operation and takes over the new content. + +Description +The method also sends a message to the tree window to asynchronously release the edit link which communicates to the +actual editor. The edit link is responsible to propagate any changes made in its node editor to the tree. + + + +Note +TVirtualStringTree overrides this method to tell the application about the new caption by calling OnNewText. + +See Also +DoEdit, OnNewText, EditNode + +@@TBaseVirtualTree.DoFocusNode@PVirtualNode@Boolean +Summary +\Internal method to set the focused node. + +Description +This methods is called by the property setter for the focused node as well as from other places to do the actual change. +It takes the parameter Ask to optionally switch off (Ask = False) triggering the OnFocusChanging event. + +@@TBaseVirtualTree.DoGetAnimationType +Summary +Determines the type of animation to be used. + +Description +Windows 98 and Windows 2000 introduced two ways of animating hints when they appear: a sliding window and a fading +window. Virtual Treeview implements both animation types and also supports system dependent animations. This allows to +use the animation type enabled in the particular system on which the tree currently runs. Additonally, there is a check +for MMX to do a fallback if fade animation is specified but no MMX available. In this case sliding is used. Starting with +Windows 2000 and Windows ME the hint animation can even be be switched off entirely. Also this case is handled by this +method. + + + +@@TBaseVirtualTree.DoGetNodeWidth@PVirtualNode@TColumnIndex@TCanvas +Summary +Overridable method which always retuns 0. + +Description +Descentants override this method to return a value which describes the width of a node. This is the inner width of the +node excluding tree lines etc. So TVirtualStringTree returns the width of the node caption (plus text margin). + +@@TBaseVirtualTree.DoGetPopupMenu@PVirtualNode@TColumnIndex@TPoint +Summary +Overridable method which triggers the OnGetPopup event. + +Description +This method does an automatic parent traversal in the tree hierarchy to find a matching popup menu. + +@@TBaseVirtualTree.DoPaintDropMark@TCanvas@PVirtualNode@TRect +Summary +Overridable method which draws the small line on top of a nodes image depending on the current drop state. + +Description +This method draws a simple polyline using Colors.DropMarkColor. Descentant can override this method to customize the +appearance of the drop mark. + +@@TBaseVirtualTree.DoPaintNode@TVTPaintInfo +Summary +Overridable method which does nothing. + +Description +Descentants override this method to paint the content of the node. For instance string trees draw the node's caption. + +@@TBaseVirtualTree.DoPopupMenu@PVirtualNode@TColumnIndex@TPoint +Summary +Overridable method which shows the popup menu for the given node. + +Description +Node and Column describe the cell for which the menu should be shown. Position determines the place +(in client coordinates of the tree window) where to show the menu. + +@@TBaseVirtualTree.DoScroll@Integer@Integer +Summary +Overridable method which triggers the OnScroll event. + +Description +This method is the ideal place if you want to synchronize other controls with the tree. The event is triggered whenever +the tree is scrolled (by the user or programmatically). DeltaX and DeltaY contain the relative values the +position changed about. + +@@TBaseVirtualTree.DoSetOffsetXY@TPoint@TScrollUpdateOptions@PRect +Summary +\Internal core routine to set the tree's scroll position. + +Description +The method takes the Value structure which contains the new absolute scroll positions, both horizontal and +vertical. Options specifies what should happen in the update process. A combination of the following values is +possible: + + + + * suoRepaintHeader, If suoUpdateNCArea is also set then invalidate the header to refresh its + screen image, otherwise it is ignored. + * suoRepaintScrollbars, If suoUpdateNCArea is also set then repaint both scrollbars after + updating them, otherwise it is ignored. + * suoScrollClientArea, Scroll and invalidate the proper part of the client area. + * suoUpdateNCArea, Update non-client area (scrollbars, header). + +@@TBaseVirtualTree.DoTimerScroll +Summary +Callback method which is triggered whenever the scroll timer fires. + +Description +This method is called to do an automatic tree scroll when the user selects nodes with the mouse (multiselection only). + +@@TBaseVirtualTree.DragDrop@IDataObject@Integer@TPoint@Integer +Summary +Helper method, which is used when a drag operation is finished. + +Description +This method is called by the TVTDragManager.Drop and prepares the list of available clipboard formats to be passed to +DoDragDrop. + +@@TBaseVirtualTree.DragFinished +Summary +Called when a drag operation is finished (accepted or cancelled). + +Description +This method is nternally used ito make up for the swallowed mouse-up messages during drag' drop. + +@@TBaseVirtualTree.Dragging +Summary +\Returns true if a drag'n drop operation is in progress. + +Description +The method returns true if currently a drag'n drop operation is in progress, which involves this tree view. + +@@TBaseVirtualTree.EditNode@PVirtualNode@TColumnIndex +Summary +Starts editing the given node if allowed to. + +Description +This method can be used by the application to manually start editiing of a particular node. Column determines hereby in +which column the node should be edited. This parameter determines the target column regardless whether toExtendedFocus is +set in TreeOptions.SelectionOptions or not. The given node must be enabled, otherwise edit start fails. + + + +See Also +DoEdit + +@@TBaseVirtualTree.EndEditNode +Summary +Stops node editing if it was started before. + +Description +EndEditNode stops node editing and accepts the result (which must be set by the edit link). + + + +See Also +, EditNode, DoEdit + +@@TBaseVirtualTree.EndSynch +Summary +Counterpart to BeginSynch. + +Description +Counts down the internal synchronous mode counter and ends synchronous mode when this counter reaches zero. + + + +See Also +BeginSynch, BeginUpdate, EndUpdate + +@@TBaseVirtualTree.EndUpdate +Summary +Resets the update lock set by BeginUpdate. + +Description +This method is the counterpart to BeginUpdate and decreases the internal update count value. If this value reaches 0 then +updates of the tree window will be allowed again. Additionally, some pending operations, which might be started during +the update lock, are finished. This includes tasks like updating the selection list, validating the cache and sorting the +tree if in auto sort mode. + +@@TBaseVirtualTree.FindNodeInSelection@PVirtualNode@Integer@Integer@Integer +Summary +Helper method to find the given node in the current selection. + +Description +This method does a binary search of the given node in the internal selection array which is sorted by memory references. +The search is limited to the area given by LowBound and HighBound. If the node could be found then true is +returned and Index is set to the found node position. + +@@TBaseVirtualTree.FinishCutOrCopy +Summary +Stops any pending cut or copy clipboard operation. + +Description +This method is used by the tree (and can be used by the application too) to stop any pending cut or copy clipboard +\operation. If a cut operation is pending then nodes currently marked with the vsCutOrCopy state are deleted. + +@@TBaseVirtualTree.FlushClipboard +Summary +Renders all pending clipboard data. + +Description +Used to render the data which is currently on the clipboard and finishes so the delayed rendering. This method is useful +if the tree is about to be destroyed but data from this tree is still on the clipboard and should stay there. If this +method is not used then any pending clipboard operation is cancelled on tree destruction (by the tree instance which +currently has data on the clipboard) and the clipboard itself is cleared. + +@@TBaseVirtualTree.FullCollapse@PVirtualNode +Summary +Collapses all nodes in the tree. + +Description +Call this method to bring all nodes in the tree into a collapsed state. This method is used to reset the vsExpanded state +in all nodes in the tree. Nodes which are not yet initialized are also not expanded by definition and therefore do not +need initialization. + + + +See Also +FullExpand + +@@TBaseVirtualTree.FullExpand@PVirtualNode +Summary +Expands all nodes in the tree. + +Description +Call this method to bring all nodes in the tree into an expanded state. This method expands every node in the tree and +initializes nodes which are not yet initialized to expand them too if necessary. Since this will validate every node in +the tree it is counterproductive and against the . + +@@TBaseVirtualTree.GetColumnClass +Summary +\Returns the class to be used to manage columns in the tree. + +Description +GetColumnClass is a special purpose method to return a certain class which is used by the tree for the columns. +TVirtualBaseTree always returns TVirtualTreeColumn but descentants can override this method to return own classes. + +@@TBaseVirtualTree.GetDisplayRect@PVirtualNode@TColumnIndex@Boolean@Boolean +Summary +\Returns the visible region used by the given node in client coordinates. + +Description +If the given node cannot be found (because one of its parents is collapsed or it is invisible) then an empty rectangle is +returned. If TextOnly is true then only the text bounds are returned, that is, the resulting rectangle's left and +right border are updated according to the bidi mode, alignment and text width of the node. If Unclipped is true +(which only makes sense if also TextOnly is true) then the calculated text rectangle is not clipped if the text +does not entirely fit into the text space. This is special handling needed for hints. + + + +If Column is NoColumn then the entire client width is used before determining the node's width otherwise the bounds of +the particular column are used. + +Note +Column must be a valid column and is used independent of whether the header is visible or not. + +@@TBaseVirtualTree.GetFirst +Summary +Group of node navigation functions. + +Description +This group of navigation functions is used to return the first node in the tree or first sub node with various +properties. + +GetFirst First node in the tree with initialization. +GetFirstChild First child node with initialization. +GetFirstCutCopy First node in cut/copy set (no initialization needed). +GetFirstInitialized First initialized node in the tree (no initialization needed). +GetFirstNoInit First node in the tree without initialization. +GetFirstVisible First visible node in the tree with initialization. +GetFirstVisibleChild First visible child of a node with initialization. +GetFirstVisibleChildNoInit First visible child of a node without initialization. +GetFirstVisibleNoInit First visible node in the tree without initialization. +
+ +@@TBaseVirtualTree.GetFirstChild@PVirtualNode + + +@@TBaseVirtualTree.GetFirstCutCopy + + +@@TBaseVirtualTree.GetFirstInitialized + + +@@TBaseVirtualTree.GetFirstNoInit + + +@@TBaseVirtualTree.GetFirstSelected + + +@@TBaseVirtualTree.GetFirstVisible + + +@@TBaseVirtualTree.GetFirstVisibleChild@PVirtualNode + + +@@TBaseVirtualTree.GetFirstVisibleChildNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetFirstVisibleNoInit + + +@@TBaseVirtualTree.GetHeaderClass +Summary +\Returns the header class to be used by the tree. + +Description +As with several other classes in Virtual Treeview (e.g. drag manager, options etc.) also a customized header class is +supported, which allows applications or descendant classes to implement their very own header class with special +behavior. This is a further element to make Virtual Treeview as flexible as possible. + +@@TBaseVirtualTree.GetHitTestInfoAt@Integer@Integer@Boolean@THitInfo +Summary +\Returns information about the node at the given position. + +Description +This method returns information about the given hit position. If the position is not within the client area then the +\result is either of hiAbove, hiBelow, hiToLeft or hiToRight, depending on the side. If the position is within the client +area but no node is hit (e.g. when the tree is empty) then hiNowhere is returned, otherwise the node is examined and HitInfo +is filled with information about which node is hit by this position, which column is involved and where on the node is +the hit (e.g. the caption, the expand/collapse button or the state image). + + + +The parameter Relative is used to tell the method how to interpret the given coordinates. If this property is true +then X and Y are given in client coordinates of the tree window, otherwise they represent absolute +coordinates of the . + + +@@TBaseVirtualTree.GetLast@PVirtualNode +Summary +Group of node navigation functions. + +Description +This group of navigation functions is used to return the last node in the tree or last sub node with various properties. + +GetLast Last node in the tree with initialization. +GetLastChild Last child node with initialization. +GetLastChildNoInit Last child node without initialiization. +GetLastInitialized Last initialized node in the tree (no initialization needed). +GetLastNoInit Last node in the tree without initialization. +GetLastVisible Last visible node in the tree with initialization. +GetLastVisibleChild Last visible child of a node with initialization. +GetLastVisibleChildNoInit Last visible child of a node without initialization. +GetLastVisibleNoInit Last visible node in the tree without initialization. +
+ +@@TBaseVirtualTree.CountLevelDifference@PVirtualNode@PVirtualNode +Summary +Determines the level difference of two nodes. + +Description +This method counts how many indentation levels the given nodes are apart. If both nodes have the same parent then the +difference is 0 otherwise the result is basically GetNodeLevel(Node2) - GetNodeLevel(Node1), but with sign. If the result +is negative then Node2 is less intended than Node1. + +@@TBaseVirtualTree.CountVisibleChildren@PVirtualNode +Summary +Determines the number of visible child nodes of the given node. + +Description +CountVisibleChildren iterates through all child nodes of Node and counts how many of them have the vsVisible state +set. + +@@TBaseVirtualTree.Create@TComponent +Summary +Constructor of the control + +Description +The constructor initializes certain properties to their default values. + +@@TBaseVirtualTree.CreateParams@TCreateParams +Summary +Prepares the creation of the controls window handle. + +Description +CreateParams is overriden to allow to set certain window styles for the control. + +@@TBaseVirtualTree.CreateWnd +Summary +Initializes data, which depends on the window handle. + +Description +Some properties must be preset first after the window handle was created. CreateWnd is the perfect place for this. + +@@TBaseVirtualTree.DefineProperties@TFiler +Summary +Helper method to customize loading and saving persistent tree data. + +Description +There were heavy changes in some properties during development of VT. This method helps to make migration easier by +reading old properties manually and put them into the new properties as appropriate. These old properties are never +written again and silently disappear. + + + +Another task of this method is to work around the problem that TCollection is not streamed correctly when using Visual +Form Inheritance (VFI). + +@@TBaseVirtualTree.Destroy +Summary +Destructor of the control. + +Description +Frees any allocated data in the tree. All pending operations will be stopped and any remaining node is freed. + +@@TBaseVirtualTree.DetermineHiddenChildrenFlag@PVirtualNode +Summary +Determines whether all children of a given node are hidden. + +Description +Virtual Treeview supports a feature, which is called node button auto hide. What happens is that when +all children of a node are hidden then the expand button for this node is automatically removed. In order to know about +the visibility state of the child nodes an internal flag is maintained, which allows to quickly decide about the button +display. DetermineHidenChildren is the update method for cases where more than one child node changed. + +See Also +vsVisible, toAutoHideButtons + +@@TBaseVirtualTree.DetermineHiddenChildrenFlagAllNodes +Summary +Determines whether all children of all nodes are hidden. + +Description +As extension to DeterminHiddenChildren this method iteratively determines the hidden children flag for all existing nodes +in the tree. This is only used for large updates. No node will be initialized in this process. + +@@TBaseVirtualTree.DetermineHitPositionLTR@THitInfo@Integer@Integer@TAlignment +Summary +Determines the hit position within a node with left-to-right and right-to-left orientation. + +Description +This method, together with its counter part DetermineHitPositionRTL, is used in the process of figuring out where the a +given position is located in relation to a node. + +@@TBaseVirtualTree.DetermineHitPositionRTL@THitInfo@Integer@Integer@TAlignment + + +@@TBaseVirtualTree.DoAutoScroll@Integer@Integer +Summary +Enables or disables the auto scroll timer. + +Description +This method determines whether the tree needs to be scrolled (the mouse is near the borders) and enables or disables the +\internal scroll timer which triggers the DoTimerScroll method. + +@@TBaseVirtualTree.DragCanceled +Summary +Called by the VCL when a drag'n drop operation was canceled by the user. + +Description +DragCanceled is used to do some housekeeping in the tree. + +@@TBaseVirtualTree.GetLastChild@PVirtualNode + + +@@TBaseVirtualTree.GetLastChildNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetLastInitialized@PVirtualNode + + +@@TBaseVirtualTree.GetLastNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetLastVisible@PVirtualNode + + +@@TBaseVirtualTree.GetLastVisibleChild@PVirtualNode + + +@@TBaseVirtualTree.GetLastVisibleChildNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetLastVisibleNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetMaxColumnWidth@TColumnIndex +Summary +\Returns the width of the largest node in the given column. + +Description +This method is mainly used to determine a minimal width of the given column without having to shorten a node caption. +Since the method has to go through all visible nodes and initialize them to learn about their width it might be time +consuming to call this method and circumvents also the virtual approach of the tree. + +@@TBaseVirtualTree.GetMaxRightExtend +Summary +Determines the maximum with of the currently visible part of the tree. + +Description +This method is similar to GetMaxColumnWidth, but determines the width of the tree if no columns are used. This method is +used for determining the horizontal scroll range for the columnless case. + +@@TBaseVirtualTree.GetNativeClipboardFormats@TFormatEtcArray +Summary +Used to let descendants and the application add their own supported clipboard formats. + +Description +GetNativeClipboardFormats returns the supported clipboard formats of the tree in the native CF_* form as used in +IDataObject. This includes all formats which are listed in the ClipboardFormats property as well as any changes made by +the OnGetUserClipboardFormats event if a handler for it is attached. + +@@TBaseVirtualTree.GetNext@PVirtualNode +Summary +Group of node navigation functions. + +Description +This group of navigation functions is used to return the next node relative to a given node in the tree with various +properties. + +GetNext Next node in the tree with initialization. +GetNextCutCopy Next node in the cut/copy set (no initialization needed). +GetNextInitialized Next initialized node in the tree (no initialization needed). +GetNextNoInit Next node in the tree without initialization. +GetNextSelected Next selected node (no initialization needed). +GetNextSibling Next sibling node with initialization. +GetNextVisible Next visible node in the tree with initialization. +GetNextVisibleNoInit Next visible node in the tree without initialization. +GetNextVisibleSibling Next visible sibling node with initialization. +GetNextVisibleSiblingNoInit Next visible sibling node without initialization. +
+ +@@TBaseVirtualTree.GetNextCutCopy@PVirtualNode + + +@@TBaseVirtualTree.GetNextInitialized@PVirtualNode + + +@@TBaseVirtualTree.GetNextNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetNextSelected@PVirtualNode + + +@@TBaseVirtualTree.GetNextSibling@PVirtualNode + + +@@TBaseVirtualTree.GetNextVisible@PVirtualNode + + +@@TBaseVirtualTree.GetNextVisibleNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetNextVisibleSibling@PVirtualNode + + +@@TBaseVirtualTree.GetNextVisibleSiblingNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetNodeData@PVirtualNode +Summary +\Returns the address of the user data area of the given node. + +Description +GetNodeData returns the address of the user data area for Node. It is strongly recommended to use this method +instead directly accessing @Node.Data. Some trees require internal data for their own use which is also stored after +Node.Data and the actual user data (application data) follows then this internal data. GetNodeData takes care of this +situation. + +@@TBaseVirtualTree.GetNodeLevel@PVirtualNode +Summary +\Returns the indentation level of the given node. + +Description +GetNodeLevel returns the level of Node. This level is determined by the number of parent nodes (excluding the +hidden root node). Top level nodes have the level 0, their direct child nodes have level 1 etc. + +@@TBaseVirtualTree.GetOptionsClass +Summary +Customization helper to determine which options class the tree should use. + +Description +GetOptionsClass is a special purpose method to return a certain class which is used by the tree for its options. +TVirtualBaseTree always returns TCustomVirtualTreeOptions but descendants can override this method to return own classes. + + + +For ease of use it makes much sense to always use the same name for the tree's options (which is TreeOptions). By using a +customized options class, however, the wrong type is returned by this property. Hence it is meaningful to override +TreeOptions and return the derived options class. To make this work the tree descendant must additionally provide new +access methods for this property. An example can be seen in TVirtualStringTree: + + + + TVirtualStringTree = class(TCustomVirtualStringTree) + private + function GetOptions: TStringTreeOptions; + procedure SetOptions(const Value: TStringTreeOptions); + protected + function GetOptionsClass: TTreeOptionsClass; override; + public + property Canvas; + published + ... + property TreeOptions: TStringTreeOptions read GetOptions write SetOptions; + ... + end; + + ... + +//----------------- TVirtualStringTree --------------------------------------------------------------------------------- + +function TVirtualStringTree.GetOptions: TStringTreeOptions; + +begin + \Result := FOptions as TStringTreeOptions; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualStringTree.SetOptions(const Value: TStringTreeOptions); + +begin + FOptions.Assign(Value); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualStringTree.GetOptionsClass: TTreeOptionsClass; + +begin + \Result := TStringTreeOptions; +end; + + + +@@TBaseVirtualTree.GetPrevious@PVirtualNode +Summary +Group of node navigation functions. + +Description +This group of navigation functions is used to return the previous node relative to a given node in the tree with various +properties. + +GetPrevious Previous node in the tree with initialization. +GetPreviousInitialized Previous initialized node in the tree (no initialization needed). +GetPreviousNoInit Previous node in the tree without initialization. +GetPreviousSibling Previous sibling node with initialization. +GetPreviousVisible Previous visible node in the tree with initialization. +GetPreviousVisibleNoInit Previous visible node in the tree without initialization. +GetPreviousVisibleSibling Previous visible sibling node with initialization. +GetPreviousVisibleSiblingNoInit Previous visible sibling node without initialization. +
+ +@@TBaseVirtualTree.GetPreviousInitialized@PVirtualNode + + +@@TBaseVirtualTree.GetPreviousNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetPreviousSibling@PVirtualNode + + +@@TBaseVirtualTree.GetPreviousVisible@PVirtualNode + + +@@TBaseVirtualTree.GetPreviousVisibleNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetPreviousVisibleSibling@PVirtualNode + + +@@TBaseVirtualTree.GetPreviousVisibleSiblingNoInit@PVirtualNode + + +@@TBaseVirtualTree.GetSortedCutCopySet@Boolean +Summary +\Returns a sorted list of nodes, which are marked for s cut or copy clipboard operation. + +Description +\Returns a list of nodes which are flagged with vsCutOrCopy, sorted in logical order, that is, as they appear in the +tree. If Resolve is true then nodes which are children of other cut/copy nodes are not put into the new array. +This feature is particularly important when doing drag'n drop as in this case all selected node plus their children need +to be considered. A selected node, which is a child (grand child etc.) of another selected node is then automatically +included and doesn't need to be explicitly mentioned in the returned selection array. + +Note +The caller is responsible for freeing the array. Allocation is done here. Usually, though, freeing the array doesn't need +additional attention as it is automatically freed by Delphi when it gets out of scope. + +@@TBaseVirtualTree.GetSortedSelection@Boolean +Summary +\Returns a sorted list of all currently selected nodes. + +Description +\Returns a list of selected nodes sorted in logical order, that is, as they appear in the tree. If Resolve is true +then nodes which are children of other selected nodes are not put into the new array. This feature is in particuar +important when doing drag'n drop as in this case all selected node plus their children need to be considered. A selected +node which is child (grand child etc.) of another selected node is then automatically included and doesn't need to be +explicitely mentioned in the returned selection array. + +Note +The caller is responsible for freeing the array. Allocation is done here. Usually, though, freeing the array doesn't need +additional attention as it is automatically freed by Delphi when it gets out of scope. + +@@TBaseVirtualTree.GetTextInfo@PVirtualNode@TColumnIndex@TFont@TRect@WideString +Summary +Helper method for node editors, hints etc. + +Description +GetTextInfo is used to define a base access method for node data and the associated font from node editors and for hints. + +@@TBaseVirtualTree.GetTreeFromDataObject@IDataObject +Summary +OLE drag'n drop and clipboard support method. + +Description +\Returns the owner/sender of the given data object by means of a special clipboard format or nil if the sender is in +another process or no virtual tree at all. + +@@TBaseVirtualTree.GetTreeRect +Summary +\Returns the size of the virtual tree image. + +Description +GetTreeRect can be used to determine the full size of the as used for painting etc. + +@@TBaseVirtualTree.GetVisibleParent@PVirtualNode +Summary +\Returns the first (nearest) parent node, which is visible. + +Description +GetVisibleParent returns the first (nearest) parent node of Node which is visible. This method is one of the +seldom cases (if not the only one) where the hidden root node could be returned. + +@@TBaseVirtualTree.HasAsParent@PVirtualNode@PVirtualNode +Summary +Determines if the given node has got another node as one of its parents. + +Description +Determines whether Node has got PotentialParent as one of its parents. + +@@TBaseVirtualTree.HasPopupMenu@PVirtualNode@TColumnIndex@TPoint +Summary +Determines whether there is a pop up menu assigned to the tree. + +Description +This overridable method is used to determine whether there is a pop up menu assigned to the tree or can be retrieve via +the OnGetPopupMenu event for a particular node. This is necessary for the tree to know how to deal with various condition +in an mouse button down event. + +@@TBaseVirtualTree.InsertNode@PVirtualNode@TVTNodeAttachMode@Pointer +Summary +Inserts a new node and returns it to the caller. + +Description +Adds a new node relative to Node. The final position is determined by Mode. UserData can be used to +set the first 4 bytes of the user data area to an initial value, which can be used in OnInitNode and will also cause to +trigger the OnFreeNode event (if \<\> nil) even if the node is not yet "officially" initialized. + +InsertNode is a compatibility method and will implicitly validates the given node if the new node is to be added as child +node. This is however against the virtual paradigm and hence I dissuade from its usage. + +@@TBaseVirtualTree.InternalData@PVirtualNode +Summary +\Returns the address of the internal data for a tree class. + +Description +In TBaseVirtualTreeview this method returns nil but should be overridden in descendants to allow proper access to the +\internal data of Node if the descendant tree has allocated internal data. + + + +See Also + + +@@TBaseVirtualTree.InvalidateCache +Summary +Empties the internal node cache and marks it as invalid. + +Description +Marks the internal node cache as being invalid. This will cause a cache validation run next time ValidateCache is called. + + + +The internal node cache is used to speed up display in Virtual Treeview. It contains node references with a distance of +CacheThreshold nodes along with their vertical absolute position, which makes it possible to quickly find the position of +a node for display, hit tests and so on. + +@@TBaseVirtualTree.InvalidateChildren@PVirtualNode@Boolean +Summary +Invalidates all children of the given node. + +Description +Invalidates Node and its immediate children. If Recursive is true then all grandchildren are invalidated as +well. The node itself is initialized if necessary and its child nodes are recreated (and initialized too if Recursive +is true). + +@@TBaseVirtualTree.InvalidateColumn@TColumnIndex +Summary +Invalidates the client area part of a column. + +Description +Invalidates the client area part of a column. + +@@TBaseVirtualTree.InvalidateNode@PVirtualNode +Summary +Invalidates the given node. + +Description +InvalidateNode initiates repaint of the given node by calling InvalidateRect with the node's display rectangel and +\returns this rectangle. + +@@TBaseVirtualTree.InvalidateToBottom@PVirtualNode +Summary +Invalidates the client area starting with the top position of the given node. + +Description +InvalidateToBottom initiates repaint of client area starting at given node. If this node is not visible or not yet +initialized then nothing happens. + +@@TBaseVirtualTree.InvertSelection@Boolean +Summary +Inverts the current selection. + +Description +InvertSelection inverts the current selection, so nodes, which are selected become unselected and vice versa. If VisibleOnly +is true then only visible nodes are considered. + +@@TBaseVirtualTree.IsEditing +Summary +Tells the caller whether the tree is currently in edit mode. + +Description +Just a simple shortcut to test the tsEditing state. + +@@TBaseVirtualTree.IsMouseSelecting +Summary +Tell the caller whether the tree is currently in draw selection mode. + +Description +IsMouseSelecting returns true if draw selection by the user is active or pending. + +@@TBaseVirtualTree.IterateSubtree@PVirtualNode@TVTGetNodeProc@Pointer@TVirtualNodeStates@Boolean@Boolean +Summary +Iterator method to go through all nodes of a given sub tree. + +Description +IterateSubtree iterates through all children and grandchildren etc. of Node (or the entire tree if Node = +nil) and calls for each node the provided callback method (which must not be empty). Filter determines which nodes +are to be considered (an empty set denotes all nodes). If DoInit is true then nodes which aren't initialized yet +will be initialized. + + + +During execution of the callback the application can set Abort to true. In this case the iteration is stopped and +the last accessed node (the one on which the callback set Abort to true) is returned to the caller. Otherwise (no +abort) nil is returned. + +Note +An application should not modify the content of the tree (e.g. delete nodes) during the iteration, otherwise the +\outcome is unpredictable and may result in an access violation. + +@@TBaseVirtualTree.LoadFromFile@TFileName +Summary +Loads previously streamed out tree data back in again. + +Description +LoadFromFile clears the current content of the tree and loads a new structure from the given file. + + + +See Also +AddFromStream + +@@TBaseVirtualTree.LoadFromStream@TStream + + +@@TBaseVirtualTree.Paint +Summary +TControl's Paint method used here to display the tree. + +Description +Overriden method to paint the tree image. The actual work is however done in PaintTree. + +@@TBaseVirtualTree.PaintTree@TCanvas@TRect@TPoint@TVTInternalPaintOptions@TPixelFormat +Summary +Main paint routine for the tree image. + +Description +PaintTree is the core paint routine used to draw any part of the tree image to any canvas. It is responsible for +maintaining the paint cycles per node as well as coordinating drawing of the various parts of the tree image. TargetCanvas +is the canvas to which to draw the tree image. This is usually the tree window itself but could well be a bitmap or +printer canvas. Window determines which part of the entire tree image to draw. The full size of the virtual image +is determined by GetTreeRect. Target is the position in TargetCanvas where to draw the tree part specified +by Window. PaintOptions determines what of the tree to draw. For different tasks usually different parts +need to be drawn, with a full image in the window, selected only nodes for a drag image etc. + +See Also + + +@@TBaseVirtualTree.PasteFromClipboard +Summary +Inserts the content of the clipboard into the tree. + +Description +PasteFromClipboar reads what is currently on the clipboard into the tree (if the format is supported). If the application +wants to have text or special formats to be inserted then it must implement its own code (OLE). Here only the native tree +format is accepted. + +@@TBaseVirtualTree.ProcessDrop@IDataObject@PVirtualNode@Integer@TVTNodeAttachMode +Summary +Helper method to ease OLE drag'n drop operations. + +Description +ProcessDrop can be used in a OnDragDrop handler to let the tree view handle a drop operation of native tree data. The +method only prepares some variables and calls then the more universal ProcessOLEData method. + +@@TBaseVirtualTree.ProcessOLEData@TBaseVirtualTree@IDataObject@PVirtualNode@TVTNodeAttachMode@Boolean +Summary +Takes serialized OLE tree data and reconstructs the former structure. + +Description +PrcessOLEData recreates the (sub) tree structure serialized into memory and provided by DataObject. The new nodes are +attached to the passed node or the hidden root node if TargetNode is nil, according to Mode. Optimized +can be set to true if the entire operation happens within the same process (i.e. sender and receiver of the OLE operation +are located in the same process). Optimized = true makes only sense if the operation to carry out is a move hence +it is also the indication of the operation to be done here. Source is the source of the OLE data and only of use +(and usually assigned) when an OLE operation takes place in the same application. + + + +The function returns true on success, i.e. the CF_VIRTUALTREE format is supported by the data object and the structure +could be recreated, otherwise false. + +@@TBaseVirtualTree.ReinitChildren@PVirtualNode@Boolean +Summary +Forces all child nodes of Node to be reinitialized. + +Description +ReinitChildren forces all child nodes of Node to be reinitialized. If Recursive is true then also the +grandchildren are reinitialized. + +@@TBaseVirtualTree.ReinitNode@PVirtualNode@Boolean +Summary +Forces a reinitialization of the given node. + +Description +ReinitNode forces Node and all its children (if Recursive is true) to be initialized again without +modifying any data in the nodes nor deleting children (unless the application requests a different amount). + +@@TBaseVirtualTree.RemoveFromSelection@PVirtualNode +Summary +Removes the given node from the current selection. + +Description +Removes the vsSelected style from Node's states and also removes Node from the internal selection array. + +@@TBaseVirtualTree.RenderOLEData@TFormatEtc@TStgMedium@Boolean +Summary +Renders pending OLE data. + +Description +RenderOLData is called by TVTDataObject.GetData when a consumer of clipboard data actually requests the data. The base +tree view only renders the native tree format, which is a chunk based stream of node data. The format to be rendered is +specified in FormatEtcIn.cfFormat and is one of the formats which are returned from GetNativeClipboardFormats. + + + +Descendants may override RenderOLEData in order to render other formats like HTML text. In TBaseVirtualTreeview this +method calls the OnRenderOLEData event for all formats, except CF_VIRTUALTREE. + +@@TBaseVirtualTree.RepaintNode@PVirtualNode +Summary +Causes the treeview to repaint the given node. + +Description +RepaintNode causes an immediate repaint of Node and returns once repainting has finished. + +@@TBaseVirtualTree.ResetNode@PVirtualNode +Summary +Resets the given node to uninitialized. + +Description +ResetNode deletes all children of Node and marks it as being uninitialized. + +@@TBaseVirtualTree.SaveToFile@TFileName +Summary +Saves the entire content of the tree into a file or stream. + +Description +Saves the entire content of the tree into a file or stream. + +See Also +LoadFromStream, AddFromStream + +@@TBaseVirtualTree.SaveToStream@TStream@PVirtualNode + + +@@TBaseVirtualTree.ScrollIntoView@PVirtualNode@Boolean@Boolean +Summary +Scrolls the tree so that the given node comes in the client area. + +Description +ScrollIntoView scrolls the tree so that the given node is in the client area and returns true if the tree really has been +scrolled (e.g. to avoid further updates) else it returns false. If extened focus is enabled then the tree will also +horizontally scrolled if needed. All collapsed parents of the node are expanded, forming so a visible path to Node. + +@@TBaseVirtualTree.SelectAll@Boolean +Summary +Selects all nodes in the tree. + +Description +SelectAll select all existing nodes in the tree. If VisibleOnly is true then only visible nodes are selected. + +@@TBaseVirtualTree.SelectNodes@PVirtualNode@PVirtualNode@Boolean +Summary +Selects a range of nodes. + +Description +SelectNodes selects a range of nodes and unselects all other possibly selected nodes which are not in this range if AddOnly +is false. EndNode must be visible while StartNode does not necessarily, as in the case where the last +focused node is the start node but it is a child of a node which has been collapsed previously. In this case the first +visible parent node is used as start node. StartNode can be nil in which case the very first node in the tree is +used. + +@@TBaseVirtualTree.Sort@PVirtualNode@TColumnIndex@TSortDirection@Boolean +Summary +Sorts the given node. + +Description +Sort sorts the child nodes of Node. The application is queried about how to sort via the OnCompareNodes event. Column +is simply passed to the the compare function so the application can also sort in a particular column. In order to free +the application from taking care about the sort direction the parameter Direction is used. This way the +application can always compare as would the node be sorted in increasing direction , while Sort reorders nodes according +to this flag. + +@@TBaseVirtualTree.SortTree@TColumnIndex@TSortDirection@Boolean +Summary +Sorts the entire tree view. + +Description +SortTree sorts the entire tree by applying Sort to every node which has got children. + +Note +This method initializes all nodes in the tree which may not only take quite a while but is also against the +and therefore usually not recommended. + +@@TBaseVirtualTree.ToggleNode@PVirtualNode +Summary +Changes a node's expand state to the opposite state. + +Description +Toggle node expands Node if it is collapsed currently and vice versa. + +@@TBaseVirtualTree.ToggleSelection@PVirtualNode@PVirtualNode +Summary +Toggles the selection state of a range of nodes. + +Description +ToggleSelection switchs the selection state of a range of nodes, so selected nodes become unselected and vice versa. This +method is specifically designed to help selecting ranges with the keyboard and considers therefore the range anchor. + +@@TBaseVirtualTree.UnselectNodes@PVirtualNode@PVirtualNode +Summary +Deselects a range of nodes. + +Description +UnselectNodes deselects a given range of nodes. EndNode must be visible while StartNode is not required to +be so as in the case where the last focused node is the start node but it is a child of a node which has been collapsed +previously. In this case the first visible parent node is used as start node. StartNode can be nil in which case +the very first node in the tree is used. + +@@TBaseVirtualTree.UpdateHorizontalScrollBar@Boolean + + +@@TBaseVirtualTree.UpdateScrollBars@Boolean +Summary +Applies changes to the horizontal and vertical scrollbars. + +Description +UpdateScrollbars (and its counterparts for vertical and horizontal scrollbars) is the core method to set the scrollbar's +properties like range, page size etc. + +@@TBaseVirtualTree.UpdateVerticalScrollBar@Boolean + + +@@TBaseVirtualTree.UseRightToLeftReading +Summary +Helper method for right-to-left layout. + +Description +UseRightToLeftReading had to be overriden in order to overcome a limitation introduced by the VCL. The VCL only allows a +window to be in right-to-left reading order if the operating system is prepared to handle this (e.g. an arabic Windows +98). Virtual Treeview however does most of the RTL stuff handle itself, also on non-RTL system. + +@@TBaseVirtualTree.ValidateCache +Summary +Initiates the validation of the internal node cache. + +Description +If the node cache is marked as being invalid then this method puts the tree into the worker thread's list and awakes then +the thread so that the validation is performed in the background. + +See Also +InvalidateCache + +@@TBaseVirtualTree.ValidateChildren@PVirtualNode@Boolean +Summary +Validates all children of a given node. + +Description +ValidateChildren ensures that the children of the given node (and all their children, if Recursive is true) are +initialized. Node must already be initialized. If nil is passed to the method the hidden root node is used +(which makes only sense if Recursive is true, in which case the entire tree is validated). + +@@TBaseVirtualTree.ValidateNode@PVirtualNode@Boolean +Summary +Validates a given node. + +Description +ValidateNode ensures that the given node (and all its children, if Recursive is true) are initialized. If Node +is nil then the hidden root node is used (which makes only sense if Recursive is true, in which case the +entire tree is validated). + +@@TBaseVirtualTree.ValidateNodeDataSize@Integer +Summary +Helper method for node data size initalization. + +Description +ValidateNodeDataSize is called from MakeNewNode if the currently set node data size is -1, which indicates it has not yet +been determined. The method calls the event OnGetNodeDataSize allowing so the application to compute now its data +requirement. + +@@TBaseVirtualTree.WndProc@TMessage +Summary +Redirected window procedure to do some special processing. + +Description +WndProc has been overriden to allow the header to handle certain messages (which are forwarded by the tree) as well as to +do some other special handling internal to the tree. + +@@TBaseVirtualTree.WriteChunks@TStream@PVirtualNode +Summary +Writes the core chunks for the given node to the given stream. + +Description +WriteChunks is part of the streaming system in Virtual Treeview and writes the core chunks for Node into Stream. +Descentants can optionally override this method to add other node specific chunks. This streaming is used when the +tree must be saved to disk or a stream used e.g. for clipboard operations. + +Note +Keep in mind that this method is also called for the hidden root node. Using this fact in descendants you can create a +kind of "global" chunk set not directly bound to a specific node. + +See Also +WriteNode, SaveToStream + +@@TBaseVirtualTree.WriteNode@TStream@PVirtualNode +Summary +Writes the cover (envelop) chunk for the given node to the given stream. + +Description +WriteNode writes the cover chunk for Node to Stream and initiates writing child nodes and chunks. This +method is part of the streaming system used in Virtual Treeview. + + + +See Also +WriteChunks, WriteToStream + +@@TClipboardFormats.Create@TBaseVirtualTree +Summary +Constructor of the class. + +Description +Create initializes the class. + +@@TCustomStringTreeOptions +Summary +Enhanced options class for string trees. + +Description +This class enhances the base class TCustomVirtualTreeOptions by options related to a string tree. + +@@TCustomStringTreeOptions.AssignTo@TPersistent +Summary +Used to copy the options class. + +Description +You can either call this method directly or use the Assign method of the target class to do the assignment. Implementing +AssignTo instead of Assign allows for future enhancements. TPersistent will automatically call AssignTo if there was no +Assign method. + +@@TCustomStringTreeOptions.Create@TBaseVirtualTree +Summary +The constructor of the class. + +Description +The constructor initializes the class. + +@@TCustomStringTreeOptions.StringOptions +Summary +The new options introduced by the class. + +Description +StringOptions provides access to the newly introduced options by which the base class is extended. + +@@TCustomVirtualDrawTree +Summary +Simple owner draw descendant of the base tree. + +Description +TCustomVirtualDrawTree is a simple TBaseVirtualTree descendant, which publishes the paint method through an event. This +allows an application for self drawn tree views without overriding the base class. + +@@TCustomVirtualDrawTree.OnDrawHint +Summary +Triggered when a node hint or tooltip must be drawn. + +Description +Use an event handler for OnDrawHint to draw the hint or tooltip for the given node. You must implement this event and +OnGetHintSize to get a hint at all. + +@@TCustomVirtualDrawTree.DoDrawHint@TCanvas@PVirtualNode@TRect@TColumnIndex +Summary +Overridable method which triggers OnDrawHint. + +Description +You can override DoDrawHint to customize the behavior for this request. + +@@TCustomVirtualDrawTree.DoGetHintSize@PVirtualNode@TColumnIndex@TRect +Summary +Overridable method which triggers OnGetHintSize. + +Description +You can override OnGetHintSize to customize the behavior for this request. + +@@TCustomVirtualDrawTree.DoGetNodeWidth@PVirtualNode@TColumnIndex@TCanvas +Summary +Overridable method which triggers OnGetNodeWidth. + +Description +You can override OnGetNodeWidth to customize the behavior for this request. + +@@TCustomVirtualDrawTree.DoPaintNode@TVTPaintInfo +Summary +Overridable method which triggers OnPaintNode. + +Description +You can override OnPaintNode to customize the behavior for this request. + +@@TCustomVirtualDrawTree.OnDrawNode +Summary +Triggered when a node must be drawn. + +Description +Use an event handler for OnDrawNode to draw the actual content for the given node. + +@@TCustomVirtualDrawTree.OnGetHintSize +Summary +Triggered when a node hint or tooltip is about to show. + +Description +Use an event handler for OnGetHintSize to return the size of the tooltip/hint window for the given node. You must +implement this event and OnDrawHint to get a hint at all. + +@@TCustomVirtualDrawTree.OnGetNodeWidth +Summary +Triggered when a node is about to be drawn. + +Description +Use an event handler for OnGetNodeWidth to return your calculated width for the given node. Since the draw does not know +the width of a node you have to tell it yourself. + +@@TCustomVirtualStringTree +Summary +Descendant of TBaseVirtualTree, which is able to manage node captions on its own + +Description +TCustomVirtualStringTree enhances the base tree to display and edit node captions. It implements a generic node editor +which can be used as reference to build your own one. + +@@TCustomVirtualStringTree.OnGetHint +Summary +Virtual string tree event to query for a custom hint text. + +Description +Write an event handler for this event to specify a custom hint for the passed node and column. The TextType will always +be ttNormal. This event will only be fired if HintMode is not hmTooltip. The delay for hints can be set as usual: adjust +the properties HintPause and HintShortPause of the global Application object. + +@@TCustomVirtualStringTree.OnGetText +Summary +Virtual string tree event to query for a node's normal or static text. + +Description +This is one of the fundamental string tree events which must always be handled. The string tree will fire this event +every time when it needs to know about the text of a specific node and column. This is mainly the case when the node +appears in the visible area of the tree view (in other words it is not scrolled out of view) but also on some other +\occasions, including streaming, drag and drop and calculating the width of the node. + + + +The node text is distinguished between two text types: + + + + * Normal text: If TextType is ttNormal return the main node caption for the specified column. + * Static text: All text that you return when TextType is ttStatic will be displayed right beside the normal text (or + left to it if the column's BidiMode is not bdLeftToRight, i.e. the column has right-to-left layout). Static text is used + \only for informational purposes; it cannot be selected or dragged and if the column is not wide enough to show all text + it will not be shortened with an ellipsis (...) as normal text. The string tree will only query for static text if the + StringOptions (see TreeOptions) include toShowStaticText. This is off by default. + + + +When this event is fired the text parameter will always be initialized with the value of property DefaultText. To handle +the event get your node data and then extract the string for the appropriate column and TextType. + +See Also +OnPaintText + +Note +Be sure that your event handler only contains absolutely necessary code. This event will be fired very often - easily a +few hundred times for medium sized trees with some columns defined when the tree is repainted completely. +For example it is far too slow to use Locate() on some Dataset, a database query result or table, and then get the text +from some TField. This may only work with in-memory tables or a client dataset. When you initialize your node data do +some caching and use these cached values to display the data. + +@@TCustomVirtualStringTree.OnNewText +Summary +Virtual string tree event to pass edited text. + +Description +A string tree will fire this event after a node has been edited successfully (not canceled with Escape). The event +handler must store the new text in the node data. + + + +This event will only be used for the default node caption editor. Other custom node editors may or may not use this event +to pass their edited data to the application. Editing for the whole tree is only possible if the MiscOptions (see +TreeOptions) include toEditable. If only certain columns or nodes should be editable write an event handler for +OnEditing. + + + +See Also +OnCreateEditor, OnEdited + +@@TCustomVirtualStringTree.OnPaintText +Summary +Event to change text formatting for particular nodes. + +Description +Write an event handler for this event to render nodes with different fonts, font sizes, styles or colors. According to +the parameters each column of each node and even normal and static text can be painted in different ways. + +Note +The string tree view manages an internal width for each node's main column. This is done because computing this width is +quite costly and the width is needed on several occasions. If you change the font which is used to paint a node's text, +for example to bold face style, its width changes but the tree view does not know this - it still relies on its cached +node width. This may result in cut off selection rectangles among others. +Hence if the width of a node changes after its initialization because it is now formatted differently than before force a +recalculation of the node width by calling InvalidateNode (when the conditions for the changed formatting are met - not +in the event handler for OnPaintText). +See Also + + +@@TCustomVirtualStringTree.OnShortenString +Summary +String tree event for custom handling of string abbreviations. + +Description +If the text of a node does not fit into its cell (in grid mode) or is too wide for the width of the tree view it is being +abbreviated with an ellipsis (...). By default the ellipsis is added to the end of the node text. + +Occasionally you may want to shorten the node text at a different position, for example if the node text is a path string +and not the last folder or filename should be cut off but rather some mid level folders if possible. + +In the handler S must be processed (shortened) and returned in Result. If Done is set to true (default value is false) +the tree view takes over the shortening. This is useful if not all nodes or columns need + +@@TCustomVirtualStringTree.AdjustPaintCellRect@TVTPaintInfo@TColumnIndex +Summary +Method which can be used by descentants to adjust the given rectangle during a paint cycle. + +Description +For some special behaviour, like the auto span column feature, it is necessary to tell the base treeview which rectangle +is to be considered as the current paint cell when drawing the tree. ClipRect is set to a rectangle which +corresponds to the current node and the current column in the paint cycle. + +@@TCustomVirtualStringTree.ContentToHTML@TVSTTextSourceType@WideString + + +@@TCustomVirtualStringTree.ContentToRTF@TVSTTextSourceType + + +@@TCustomVirtualStringTree.ContentToText@TVSTTextSourceType@Char + + +@@TCustomVirtualStringTree.ContentToUnicode@TVSTTextSourceType@WideChar + + +@@TCustomVirtualTreeOptions +Summary +Organizes all tree options into subproperties for easier managment. + +Description +There are a lot of options available which control certain aspects of Virtual Treeview. Because there might only be at +most 32 members in a published set and also for better overview these options have been splitted into several subsets, +each related to a particular feature group like painting or node selection. With this implementation you can even derive +an own option class and modify which options should be shown in Delphi's object inspector for your class. + +@@TCustomVirtualTreeOptions.AnimationOptions +Summary +Options related to animations. + +Description +These options can be used to switch certain animation effects in a tree. + +@@TCustomVirtualTreeOptions.AutoOptions +Summary +Options related to automatic actions. + +Description +These options can be used to switch certain actions in a tree which happen automatically under certain circumstances. + +@@TCustomVirtualTreeOptions.MiscOptions +Summary +Options not related to any other category. + +Description +These options can be used to switch miscellanous aspects in a tree. + +@@TCustomVirtualTreeOptions.Owner +Summary +Owner tree to which the property class belongs. + +Description +Owner tree to which the property class belongs. + +@@TCustomVirtualTreeOptions.PaintOptions +Summary +Options related to painting. + +Description +These options can be used to switch visual aspects of a tree. + +@@TCustomVirtualTreeOptions.SelectionOptions +Summary +Options related to the way nodes can be selected. + +Description +These options can be used to switch the way how nodes can be selected in a tree. + +@@TCustomVirtualTreeOptions.AssignTo@TPersistent +Summary +Used to copy this option class to another option collection. + +Description +This is the usual method to support streaming or simply copying of this class. To stay open for future enhancements in +form of new descentants not Assign but AssignTo has been used. AssignTo is called by TPersistent if there is no Assign +method. + +@@TCustomVirtualTreeOptions.Create@TBaseVirtualTree +Summary +Constructor of the class. + +Description +Used to assign default values to all sub lists. + +@@TStringEditLink.Create +Summary +Constructor of the class. + +Description +The constructor of the edit link also creates an instance of a simple node editor control. It is by default hidden and +first displayed if the tree directs the link to do so. + +@@TStringEditLink.Destroy +Summary +Destructor of the class. + +Description +Frees the internal editor control. + +@@TVirtualTreeColumn +Summary +Represents a column in a Virtual Treeview. + +Description +This enhanced collection item, which is organized within the TCollection descentant TVirtualTreeColumns, manages all +aspects of a single column. + +@@TVirtualTreeColumns +Summary +Collection class, which holds the columns for the tree. + +Description +This class is an enhanced collection which manages general aspects of columns like ordering, traversion, streaming, +painting, dragging etc. + +@@TVirtualStringTree +Summary +Descentant of TBaseVirtualTree which is able to manage node captions on its own. + +Description +TVirtualStringTree adds no new functionality to TCustomVirtualStringTree but is publicly available version and appears in +the component palette. + +@@TBaseVirtualTree.LastClickPos +Summary +Used for retained drag start and wheel mouse scrolling. + +Description +This internal positions is made public to allow descendants to modify mainly the right click behavior of the tree +control. + +@@TBaseVirtualTree.OnGetCellIsEmpty +Summary +Triggered when the tree control needs to know whether a given column is empty. + +Description +Virtual Treeview supports the concept of column spanning where one cell with too much text to fit into its own space can +expand to the right cell neighbors if they are empty. To make this work it is necessary to know if a cell is considered +as being empty, whatever this means to an application. The string tree descendant simply checks the text for the given +cell and calls back its ancestor if there is no text to further refine if the cell must stay as if it contained +something. The ancestor (TBaseVirtualTree) now triggers OnGetCellIsEmpty to let the application decide. + + +@@TVTColors.DropMarkColor +Summary +Color of the drop mark. + +Description +Since the drop metapher has been extended to include dropping on node, above a node or below a node +(e.g. to determine adding as child, previous sibling or next sibling) there must be an indication where the node would +actually be placed when it would be dropped. This indication is the drop mark, whose color can be set via the +DropMarkColor property. + +@@TVTDataObject +Summary +Implementation of an IDataObject interface. + +Description +This class is used for OLE drag'n drop and clipboard operations. It allows not only to transfer various kinds of data +between trees but also to transfer this data between different processes. Additionally, every OLE aware application (like +Word) can take part in the data transfer. This makes it easy to copy some of the tree's content for documentation +purposes. + +@@TVTDataObject.CanonicalIUnknown@IUnknown +Summary +Helper method for setting data in the IDataObject. + +Description +In SetData the class can get a circular reference if the client calls GetData then calls SetData with the same StgMedium. +Because the unkForRelease for the IDataObject can be marshalled it is necessary to get pointers that can be correctly +compared. CanonicalIUknown uses COM object identity for this task. An explicit call to the IUnknown::QueryInterface +method, requesting the IUnknown interface, will always return the same pointer. See the IDragSourceHelper article by +Raymond Chen at MSDN. + +@@TVTDataObject.Create@TBaseVirtualTree@Boolean +Summary +Constructor of the class. + +Description +Create is used only for initialization. + +@@TVTDataObject.DAdvise@TFormatEtc@Integer@IAdviseSink@Integer +Summary +Implementation of the IDataObject.DAdvise method. + +Description +Advise sinks are used to have an opportunity for clients to get notified if something changes in the data object. +TVTDataObject uses the data advise holder APIs to provide the advise sink service. + +@@TVTDataObject.Destroy +Summary +Destructor of the class. + +Description +Cleans up the object. + +@@TVTDataObject.DUnadvise@Integer +Summary +Implementation of the IDataObject.DUnAdvise method. + +Description +DUnadvice reverses the call to DAdvise. + +@@TVTDataObject.EnumDAdvise@IEnumStatData +Summary +Implementation of the IDataObject.EnumDAdvise method. + +Description +EnumDAdvice does nothing but forwards the call to the internal advise holder class, which the responds accordingly. +That's why we use data advise holders after all. + +@@TVTDataObject.EnumFormatEtc@Integer@IEnumFormatEtc +Summary +Implementation of the IDataObject.EnumFormatEtc method. + +Description +This method creates a FormatEtc enumerator class which is used to enumerate all data formats supported by the owner tree. + +@@TVTDataObject.EqualFormatEtc@TFormatEtc@TFormatEtc +Summary +Compares two TFormatEtc structures. + +Description +\Returns true if both records are considered the same. That means if they have at least one common storage format and all +\other entries have the same values. + +@@TVTDataObject.FindFormatEtc@TFormatEtc@TFormatEtcArray +Summary +Searchs the given array for a the given format. + +Description +\Returns true if the given format is part of the array. + +@@TVTDataObject.FindInternalStgMedium@TClipFormat +Summary +\Returns a storage medium for a given clipboard format. + +Description +The class keeps an internal list of clipboard format/storage medium relations. For some operations data is set in certain +formats which is later retrieve by locating it using this method. + +@@TVTDataObject.GetCanonicalFormatEtc@TFormatEtc@TFormatEtc +Summary +Implementation of the IDataObject.GetCanonicalFormatEtc method. + +Description +The implementation of this method simply consists of a result value telling the caller to use the EnumFormatEtc method. + +@@TVTDataObject.GetData@TFormatEtc@TStgMedium +Summary +Implementation of the IDataObject.GetData method. + +Description +Whenever drag'n drop or clipboard data actually needs to be rendered then this method is called by the OLE subsystem. The +class automatically returns the CF_VTREFERENCE format and any data previously set by the SetData method (e.g. by the +Shell). For any other format the owner tree is asked to render the OLE data. + +See Also +RenderOLEData + +@@TVTDataObject.GetDataHere@TFormatEtc@TStgMedium +Summary +Implementation of the IDataObject.GetDataHere method. + +Description +GetDataHere is an alternative data retrival method to GetData, but the caller provides the storage place where to store +the actual data. Since Virtual Treeview has a very limited spectrum of what it can use this method is not fully +implmented. + +@@TVTDataObject.HGlobalClone@THandle +Summary +Helper method for SetData. + +Description +This method copies a HGlobal memory block. + +@@TVTDataObject.QueryGetData@TFormatEtc +Summary +Implementation of the IDataObject.QueryGetData method. + +Description +This method is called by OLE subsystem to determine which data formats are offered by the owner tree. It uses the +\internal clipboard format list to get a list of available and allowed formats. Currently following formats are +supported: + + + +TBaseVirtualTree + + * Virtual Treeview reference and process identifier + * native serialized tree data + + + +TCustomVirtualStringTree + + * generic Unicode text + * generic ANSI text + * HTML formatted text (UTF-8 format) + * RTF text (UTF-16 format) + * CSV (comma separated values) but with customizable separators + +@@TVTDataObject.RenderInternalOLEData@TFormatEtc@TStgMedium@HResult +Summary +Helper method to return data previously stored by SetData. + +Description +For some operations (e.g. shell transfers with IDropTargetHelper interface) data is stored in the class. +RenderInternalOLEData returns this data when queried later. + +@@TVTDataObject.SetData@TFormatEtc@TStgMedium@BOOL +Summary +Implementation of the IDataObject.SetData method. + +Description +This method is used to add or replace data in the data object. + +@@TVTDataObject.StgMediumIncRef@TStgMedium@TStgMedium@Boolean@IDataObject +Summary +Central managing method to copy OLE data. + +Description +This method is called when data must be copied from or to the data object. For each supported storage medium a different +(and appropriate) action is taken. + + +@@AlphaBlend@HDC@HDC@TRect@TPoint@TBlendMode@Integer@Integer +Summary +General purpose procedure to blend one bitmap to another. + +Description +This is an optimized alpha blend procedure using MMX instructions to perform as quick as possible. For this procedure to +work properly it is important that both source and target bitmap use the 32 bit color format (pf32Bit for TBitmap). R +describes the source rectangle to work on, while Target is the place (upper left corner) in the target bitmap +where to blend to. Note that source width + X offset must be less or equal to the target width. Similar for the height. + + + +If Mode is bmConstantAlpha then the blend operation uses the given ConstantAlpha value for all pixels. + +If Mode is bmPerPixelAlpha then each pixel is blended using its individual alpha value (the alpha value of the +source). + +If Mode is bmMasterAlpha then each pixel is blended using its individual alpha value multiplied by ConstantAlpha. + +If Mode is bmConstantAlphaAndColor then each destination pixel is blended using ConstantAlpha but also a constant +color which will be obtained from Bias. In this case no offset value is added, otherwise Bias is used as offset. + + + +Blending of a color into target only (bmConstantAlphaAndColor) ignores Source (the DC) and Target (the +position). + + + +Note +This procedure does not check whether MMX instructions are actually available! Call it only if MMX is really usable, +\otherwise a process exception for unknown op codes is thrown. + +@@DrawTextW@HDC@PWideChar@Integer@TRect@Cardinal@Boolean +Summary +Paint support procedure. + +Description +This procedure implements a subset of Window's DrawText API for Unicode which is not available for Windows 95, 98 and ME. +For a description of the parameters see DrawText in the online help. + + + +Supported flags are currently: + + * DT_LEFT + * DT_TOP + * DT_CALCRECT + * DT_NOCLIP + * DT_RTLREADING + * DT_SINGLELINE + * DT_VCENTER + +Differences to the DrawTextW Windows API: + +The additional parameter AdjustRight determines whether to adjust the right border of the given rectangle to +accomodate the largest line in the text. It has only a meaning if also DT_CALCRECT is specified. + +Note +When running on any NT windows version (Windows NT 4.0, Windows 2000., Windows XP and up) the native windows API is used +instead of this method as it also supports word wrapping properly. + +@@TreeFromNode@PVirtualNode +Summary +General purpose routine to get the tree to which a node belongs. + +Description +For obvious reasons it makes no sense to store the reference to a tree in each node record, but sometimes there might +arise the need to know to which tree a node belongs. This is not often the case but is necessary e.g. for optimized moves +in drag'n drop or cut'n paste operations. + + + +Each node contains a reference to its parent to allow fast traversal. The hidden root node, however, does not need this +reference because it does not have a node parent. Instead it contains the reference of the tree to which it belongs. To +determine which node is the root node (when you don't know its tree) a special case of sibling reference is used. Since +the root node does neither have a previous nor a next sibling the corresponding pointers are set to the root node, making +the root so pointing to itself. This case will never happen in "normal" nodes, so it is a reliable way to detect the root +node. + +@@TVTButtonFillMode +Summary +Determines how the interior of nodes buttons should be drawn. + +Description +Usually the little plus and minus buttons have just the color of the treeview but sometimes it looks better to use +another kind of painting. This is particularly important when simulating Windows XP buttons on non-XP systems. The image +below shows how the various modes look like: + + + + + +======================== + +
+ +@@CFSTR_CSV +Summary +Contains the registration string for certain clipboard formats. + +Description +Some of the clipboard formats in the system, like CF_HDROP, are registered by Windows itself. For rich text, html, csv +and other data first the formats must be registered with the clipboard. The identifier returned by the registration code +is used to unregister the format later and to identify the format when transferring data or enumerating the clipboard +formats. The following formats are registered by Virtual Treeview: + + * CVS: comma separated values, a tabular data format. + * HTML: text data with text formatting and structured like a big table. Unicode is supported as well (UTF-8). + * RTF: rich text format, similar to HTML, but more complex and also a bit older. + * RTFNOOBJS: like RTF but without embedded objects (not used by Virtual Treeview). + * VIRTUALTREEVIEW: serialized treeview data. This is the native tree format and the only one directly accepted by the + control. + * VTREFERENCE: a special format to pass on a reference of the sender treeview. If both, sender and receiver, live in + the same process this reference can be used to directly access the sender treeview, without COM interecption. + +@@CFSTR_HTML + + +@@TVTDragImage.DragTo@TPoint@Boolean +Summary +Moves the drag image to a new position, which is determined from the passed point P and the previous mouse +position. + +Description +ForceRepaint is true if something on the screen changed and the back image must be refreshed. + +@@TVTDragImage.GetDragImageRect +Summary +\Returns the current size and position of the drag image (screen coordinates). + +Description +\Returns the current size and position of the drag image (screen coordinates). + +@@TVTDragImage.InternalShowDragImage@HDC +Summary +Frequently called helper routine to actually do the blend and put it onto + +Description +Frequently called helper routine to actually do the blend and put it onto the screen. Only used if the system does not +support drag images. + + +@@TVTDragImage.PrepareDrag@TBitmap@TPoint@TPoint@IDataObject +Summary +Creates all necessary structures to do alpha blended dragging using the given image. + +Description +ImagePostion and Hotspot are given in screen coordinates. The first determines where to place the drag +image while the second is the initial mouse position. This method also determines whether the system supports drag images +natively. If so then only minimal structures are created. + +@@TVTDragImage.WillMove@TPoint +Summary +Add a summary here... + +Description +This method determines whether the drag image would "physically" move when DragTo would be called with the same target +point. Always returns false if the system drag image support is available. + +@@TVTDragManager.ForceDragLeave +Summary +This method calls the drop target helper's DragLeave method to ensure it removes the drag image from screen. + +Description +This method calls the drop target helper's DragLeave method to ensure it removes the drag image from screen. + +@@TVTHeader.DragTo@TPoint +Summary +Moves the drag image to a new position, which is determined from the passed point P and the previous mouse +position. + +Description +Moves the drag image to a new position, which is determined from the passed point P and the previous mouse +position. + +@@TVTHeader.GetColumnsClass +Summary +\Returns the class to be used for the actual column implementation. + +Description +Descentants may optionally override this and return their own class. + +@@TVTHeader.HandleMessage@TMessage +Summary +General message handler for the header. + +Description +The header gets here the opportunity to handle certain messages before they reach the tree. This is important because the +tree needs to handle various non-client area messages for the header as well as some dragging/tracking events. By +returning True the message will not be handled further, otherwise the message is then dispatched to the proper message +handlers. + +@@TVTHeader.InHeader@TPoint +Summary +Determines whether the given point (client coordinates!) is within the header rectangle (non-client coordinates). + +Description +Determines whether the given point (client coordinates!) is within the header rectangle (non-client coordinates). + +@@TVTHeader.Invalidate@TVirtualTreeColumn@Boolean +Summary +Invalidates the entire header or parts of it so they are repainted. + +Description +Because the header is in the non-client area of the tree it needs some special handling in order to initiate its +repainting. If ExpandToRight is true then not only the given column but everything to its right will be +invalidated (useful for resizing). This makes only sense when a column is given. + +@@TVTHeader.LoadFromStream@TStream +Summary +Restores the state of the header from the given stream. + +Description +Restores the state of the header from the given stream. + +@@TVTHeader.PrepareDrag@TPoint@TPoint +Summary +Initializes dragging of the header, P is the current mouse postion and Start the initial mouse position. + +Description +Initializes dragging of the header, P is the current mouse postion and Start the initial mouse position. + +@@TVTHeader.RecalculateHeader +Summary +Initiate a recalculation of the non-client area of the owner tree. + +Description +Initiate a recalculation of the non-client area of the owner tree. + +@@TVTHeader.RestoreColumns +Summary +Restores all columns to their width which they had before they have been auto fitted. + +Description +Restores all columns to their width which they had before they have been auto fitted. + +@@TVTHeader.SaveToStream@TStream +Summary +Saves the complete state of the header into the provided stream. + +Description +Saves the complete state of the header into the provided stream. + +@@TVTHeader.UpdateMainColumn +Summary +Called once the load process of the owner tree is done. + +Description +Called once the load process of the owner tree is done. + + +@@RegisterVTClipboardFormat@Word@TVirtualTreeClass@Cardinal + + +@@RegisterVTClipboardFormat@string@TVirtualTreeClass@Cardinal@Integer@PDVTargetDevice@Integer@Integer +Summary +Methods to register a certain clipboard format for a given tree class. + +Description +Registration with the clipboard is done here too and the assigned ID returned by the function. tymed may contain or'ed +TYMED constants which allows to register several storage formats for one clipboard format. + + +@@CacheThreshold +Summary +Number of nodes a tree must at least have to start caching and at the same time the maximum number of nodes between two +cache entries. + +Description +Number of nodes a tree must at least have to start caching and at the same time the maximum number of nodes between two +cache entries. + +@@CFSTR_RTF + + +@@CFSTR_RTFNOOBJS + + +@@CFSTR_VIRTUALTREE + + +@@CFSTR_VTREFERENCE + + +@@ShadowSize +Summary +Size in pixels of the hint shadow. + +Description +This value has no influence on Win2K and XP systems as those OSes have native shadow support. Set it to 0 if you don't +want shadows on the other systems. + + +@@IVTEditLink +Summary +Interface which is used for communication between the treeview and a node editor. + +Description +Due to the virtual nature of the tree it is necessary to supply a kind of plug in interface for application defined node +editors. TCustomVirtualStringTree is the first class which implements a node editor. This is just a generic editor to +edit a node's caption just like TTreeview does it. Because of the lack of support under Win9x system this editor only can +edit ANSI text. You have to create an own editor to make also Unicode string editing available for node captions. + + + +All node editors must implement this interface to allow the treeview to communicate with the node editor. Node editors +are small components or forms. If a node shall be edited (for instance when the user presses F2) the treeview will fire +the event OnCreateEditor. The application must determine which node editor must be used for the data in the given node +and column. Then it creates and returns an instance of the appropriate node editor. + +The life cycle of the node editor object is handled via reference counting. This means that the application must not +destroy the node editor explicitly - this will happen automatically when the node editor is not used anymore. + +@@IVTEditLink.BeginEdit +Summary +This function will be called by the virtual tree when the editing starts. + +Description +Write code to actually display the node editor here. This might be something like Visible := True or Show. The return +value should be true if editing can start or false otherwise. Before this function is called PrepareEdit and SetBounds +are executed. + +@@IVTEditLink.CancelEdit +Summary +This function will be called by the virtual tree when the current editing is about to be cancelled. + +Description +Hide the node editor here. This might be something like Visible := False or Hide. The return value should be True if the +editing can be cancelled. Return false if the node editor is in an internal state which does not allow to cancel the +editing right now. + + + +Do not destroy the node editor instance because this will be done implicitly via reference counting. + +Note +If the edited tree is changed during this function, i.e. focus change, node deletion and so on, CancelEdit might be +called again by the tree which can lead to access violations. It is therefore advisable to block reentrancy with a +boolean variable. Example: + +function TStringEditLink.CancelEdit: Boolean; +begin + \Result := not FStopping; + if Result then + begin + FStopping := True; + FEdit.Hide; + FTree.CancelEditNode; + end; +end; + + + +@@IVTEditLink.EndEdit +Summary +This function will be called by the virtual tree when the current editing is being finished. + +Description +Hide the node editor here. This might be something like Visible := False or Hide. The return value should be true if the +editing can be finished. Return false if the node editor is in an internal state which does not allow to finish the +editing right now - possibly because there is no valid value available at the moment. If the editing can be finished +transmit the edited value to the tree or to the data structure which is displayed in the tree. + + + +Do not destroy the node editor instance because this will be done implicitly via reference counting. + +Note +If the edited tree is changed during this function, i.e. focus change, node deletion and so on, EndEdit might be called +again by the tree which can lead to access violations. It is therefore advisable to block reentrancy with a boolean +variable. Example: + + +function TStringEditLink.EndEdit: Boolean; +begin + \Result := not FStopping; + if Result then + try + FStopping := True; + if FEdit.Modified then + FTree.DoNewText(FNode, FColumn, FEdit.Caption); + FEdit.Hide; + except + FStopping := False; + raise; + end; +end; + + + + +@@IVTEditLink.GetBounds +Summary +The virtual tree can use this function to get the current bounding rect of the node editor. + +Description +The bounding rect of the node editor may change during the editing to reflect the changed edit contents. The tree uses +this function to query the current bounding rect of the editor. VCL components derived from TControl have a BoundsRect +property which can be used as a return value here. + +@@IVTEditLink.PrepareEdit@TBaseVirtualTree@PVirtualNode@TColumnIndex +Summary +This function is called by a virtual tree to initialize the node editor. + +Description +Use PrepareEdit to initialize the node editor. This includes getting the node content in the specified column which will +be needed later when the editor is shown. BeginEdit may be called anytime after this function returns. If the +initialization fails simply return false (exceptions should be trapped). + +@@IVTEditLink.ProcessMessage@TMessage +Summary +This function is used to forward messages being directed to the virtual tree. + +Description +Some node editors might need to trap some messages which are directed to the treeview window. This function remedies the +need to subclass the virtual tree via its WindowProc property. If these messages are not needed leave the function body +empty. + +@@IVTEditLink.SetBounds@TRect +Summary +The virtual tree calls this function to initialize the bounding rectangle of the node editor. + +Description +This function is usually called after PrepareEdit and before BeginEdit in order to place the node editor exactly over the +node which is about to be edited. Use the R parameter to set the bounding rect of the editor. If the treeview is in grid +mode R will be equal to the cell rectangle of the to be edited cell. Otherwise R is the bounding rectangle of the actual +node text. + +Note +SetBounds is also a method of TControl. Hence if your node editor is implemented by a descendant of TControl you must use +a method resolution clause to avoid a name clash. The clause can look similar to this: + +procedure EditLinkSetBounds(R: TRect); stdcall; +procedure IVTEditLink.SetBounds = EditLinkSetBounds; + + +@@TCheckImageKind.ckSystemDefault +System defined check images. + +@@THeaderState.hsResizing +multi column resizing in progress + +@@THeaderState.hsColumnWidthTrackPending +left button is down, user might want to start resize a column + +@@THeaderState.hsColumnWidthTracking +column resizing is in progress From 1ed06c52af3280df650288c12f8363bd68cf7a51 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 26 Aug 2019 14:06:16 +0200 Subject: [PATCH 015/681] Fix for issue #915: Possible race condition when setting TVirtualTreeStates.tsUseCache flag, plus assertion. --- Source/VirtualTrees.pas | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index a641a058b..85da5e42a 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18271,7 +18271,13 @@ procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirt begin //TODO: If this works reliable, move to TWorkerThread if (Self.HandleAllocated) then - TThread.Synchronize(nil, procedure begin DoStateChange(EnterStates, LeaveStates) end); + TThread.Synchronize(nil, procedure + begin + // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) + if not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then + DoStateChange(EnterStates, LeaveStates) + end); + end; //---------------------------------------------------------------------------------------------------------------------- @@ -20636,6 +20642,7 @@ procedure TBaseVirtualTree.DoStateChange(Enter: TVirtualTreeStates; Leave: TVirt FOnStateChange(Self, Enter, Leave); end; FStates := FStates + Enter - Leave; + Assert(FStates * [tsUseCache, tsValidationNeeded] <> [tsUseCache, tsValidationNeeded], 'Invalid state. tsUseCache and tsValidationNeeded are mutually exclusive and must not be set at the same time'); end; //---------------------------------------------------------------------------------------------------------------------- From 59d44c6bb07b6765bafb9356f7175c91d72bee00 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 26 Aug 2019 14:28:32 +0200 Subject: [PATCH 016/681] Fixed issue #919: TVirtualTreeColumn.SetColor() should remove coStyleColor flag --- Source/VirtualTrees.pas | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 85da5e42a..57520dee2 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -6589,6 +6589,7 @@ procedure TVirtualTreeColumn.SetColor(const Value: TColor); begin FColor := Value; Exclude(FOptions, coParentColor); + Exclude(FOptions, coStyleColor); // Issue #919 Changed(False); Owner.Header.TreeView.Invalidate; end; @@ -6713,7 +6714,7 @@ procedure TVirtualTreeColumn.SetOptions(Value: TVTColumnOptions); ToBeSet, ToBeCleared: TVTColumnOptions; VisibleChanged, - ColorChanged: Boolean; + lParentColorSet: Boolean; lTreeView: TBaseVirtualTree; begin if FOptions <> Value then @@ -6724,12 +6725,14 @@ procedure TVirtualTreeColumn.SetOptions(Value: TVTColumnOptions); FOptions := Value; VisibleChanged := coVisible in (ToBeSet + ToBeCleared); - ColorChanged := coParentColor in ToBeSet; + lParentColorSet := coParentColor in ToBeSet; if coParentBidiMode in ToBeSet then ParentBiDiModeChanged; - if ColorChanged then - ParentColorChanged; + if lParentColorSet then begin + Include(FOptions, coStyleColor);// Issue #919 + ParentColorChanged(); + end; if coAutoSpring in ToBeSet then FSpringRest := 0; @@ -6740,8 +6743,7 @@ procedure TVirtualTreeColumn.SetOptions(Value: TVTColumnOptions); Changed(False); // Need to repaint and adjust the owner tree too. lTreeView := Owner.Header.Treeview; - if not (csLoading in lTreeview.ComponentState) and (VisibleChanged or ColorChanged) and (Owner.UpdateCount = 0) and - lTreeView.HandleAllocated then + if not (csLoading in lTreeview.ComponentState) and (VisibleChanged or lParentColorSet) and (Owner.UpdateCount = 0) and lTreeView.HandleAllocated then begin lTreeview.Invalidate(); if VisibleChanged then begin From e0c6ab0c32cb06a023fb3b917d9ffd506fd8eaa2 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 26 Aug 2019 14:45:21 +0200 Subject: [PATCH 017/681] Fix for issue #918: Missing sort indicator with toUseExpolorerTheme when VCL styles are used --- Source/VirtualTrees.pas | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 57520dee2..4370c24cb 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -9167,7 +9167,8 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const else Glyph := thHeaderSortArrowSortedDown; Details := StyleServices.GetElementDetails(Glyph); - StyleServices.DrawElement(TargetCanvas.Handle, Details, Pos, @Pos); + if not StyleServices.DrawElement(TargetCanvas.Handle, Details, Pos, @Pos) then + PaintInfo.DrawSortArrow(FHeader.FSortDirection); end else begin From 15ccc9de15316c744137ff0197b40038f5be5f12 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 26 Aug 2019 17:14:17 +0200 Subject: [PATCH 018/681] Update VirtualTrees.pas TBaseVirtualTree.HandleHotTrack(): * We always need to invalidate the new hot node in case it differs from the old one. Fixes #920 * Removed duplicate invalidation of oldHotNode --- Source/VirtualTrees.pas | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 4370c24cb..52c41119a 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -21826,7 +21826,7 @@ procedure TBaseVirtualTree.HandleHotTrack(X, Y: Integer); FCurrentHotNode := HitInfo.HitNode; if (FCurrentHotNode <> oldHotNode) or (HitInfo.HitColumn <> FCurrentHotColumn) then begin - DoInvalidate := (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.FMiscOptions); + DoInvalidate := (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.FMiscOptions) or (oldHotNode <> FCurrentHotNode); DoHotChange(oldHotNode, HitInfo.HitNode); if Assigned(oldHotNode) and DoInvalidate then InvalidateNode(oldHotNode); @@ -21834,7 +21834,7 @@ procedure TBaseVirtualTree.HandleHotTrack(X, Y: Integer); end; ButtonIsHit := (hiOnItemButtonExact in HitInfo.HitPositions); - if Assigned(HitInfo.HitNode) and ((FHotNodeButtonHit <> ButtonIsHit) or DoInvalidate) then + if Assigned(HitInfo.HitNode) and ((FHotNodeButtonHit <> ButtonIsHit) or (FCurrentHotNode <> oldHotNode) or DoInvalidate) then begin FHotNodeButtonHit := ButtonIsHit; InvalidateNode(HitInfo.HitNode); @@ -21842,10 +21842,6 @@ procedure TBaseVirtualTree.HandleHotTrack(X, Y: Integer); else if not Assigned(HitInfo.HitNode) then FHotNodeButtonHit := False; - - if (oldHotNode <> FCurrentHotNode) and (oldHotNode <> nil) then - InvalidateNode(oldHotNode); - end; //---------------------------------------------------------------------------------------------------------------------- From 05b57c413b881701c055341e4722872342f20cc3 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 29 Aug 2019 08:28:02 +0200 Subject: [PATCH 019/681] Fix issue #921: Code Issues 41 Pull requests 2 Projects 0 Wiki Security Insights Settings OnGetNodeHint ignores the supplied TVTTooltipLineBreakStyle --- Source/VirtualTrees.pas | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 52c41119a..ce6bc94f9 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -5599,7 +5599,6 @@ function TVirtualTreeHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: str GetTextMetrics(Canvas.Handle, TM); FTextHeight := TM.tmHeight; - LineBreakStyle := hlbDefault; if Length(HintText) = 0 then Result := Rect(0, 0, 0, 0) @@ -15678,7 +15677,6 @@ procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow); IsFocusedOrEditing: Boolean; ParentForm: TCustomForm; BottomRightCellContentMargin: TPoint; - DummyLineBreakStyle: TVTTooltipLineBreakStyle; HintKind: TVTHintKind; begin with Message do @@ -15732,9 +15730,9 @@ procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow); if Assigned(HitInfo.HitNode) and (HitInfo.HitColumn > InvalidColumn) then begin if HintMode = hmToolTip then - HintStr := DoGetNodeToolTip(HitInfo.HitNode, HitInfo.HitColumn, DummyLineBreakStyle) + HintStr := DoGetNodeToolTip(HitInfo.HitNode, HitInfo.HitColumn, fHintData.LineBreakStyle) else - HintStr := DoGetNodeHint(HitInfo.HitNode, HitInfo.HitColumn, DummyLineBreakStyle); + HintStr := DoGetNodeHint(HitInfo.HitNode, HitInfo.HitColumn, fHintData.LineBreakStyle); end; // First check whether there is a header hint to show. From d674e988a24150d2209b2ba47422c2662c07af7c Mon Sep 17 00:00:00 2001 From: hubalazs <10714856+hubalazs@users.noreply.github.com> Date: Fri, 20 Sep 2019 16:11:35 +0200 Subject: [PATCH 020/681] Fixed small typos --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5bcf4f293..fa89f83db 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ I don't use C++ Builder and my experience with it is very limited. This makes it ### Downloads **V7.2** official release for **RAD Studio XE3 to 10.3 Rio**: [JAM Software](https://www.jam-software.com/virtual-treeview/VirtualTreeView.zip) ([Changes](https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/12?closed=1)) -An experimetal **FireMonkey** port can be found here: [livius2/Virtual-TreeView](https://github.com/livius2/Virtual-TreeView) +An experimental **FireMonkey** port can be found here: [livius2/Virtual-TreeView](https://github.com/livius2/Virtual-TreeView) A port to **Lazarus / FPC** can be found here: [blikblum/VirtualTreeView-Lazarus](https://github.com/blikblum/VirtualTreeView-Lazarus) @@ -22,7 +22,7 @@ For a **Delphi XE** compatible fork see: [sglienke/Virtual-TreeView](https://git For installation instruction see the "INSTALL.TXT" file in the ZIP. [Delphinus](http://memnarch.bplaced.net/blog/2015/08/delphinus-packagemanager-for-delphi-xe-and-newer/)-Support was added. ### Technical Support -Please do not contact developers or JAM Software for technical support. Please try to get support from the community e.g. at [Stack Overflow](http://stackoverflow.com/search?q=%22virtual+treeview%22) , [Delphi Pages](http://www.delphipages.com/), [Delphi Praxis](http://www.delphipraxis.net/141465-virtual-treeview-tutorials-mit-beispielen.html) or [Embarcadero forums](https://forums.embarcadero.com/). Please do not use the issue tracker for getting support, only for reporting true bugs (see below). +Please do not contact developers or JAM Software for technical support. Please try to get support from the community e.g. at [Stack Overflow](http://stackoverflow.com/search?q=%22virtual+treeview%22), [Delphi Pages](http://www.delphipages.com/), [Delphi Praxis](http://www.delphipraxis.net/141465-virtual-treeview-tutorials-mit-beispielen.html) or [Embarcadero forums](https://forums.embarcadero.com/). Please do not use the issue tracker for getting support, only for reporting true bugs (see below). ### Reporting Bugs First of all, please make sure you are using the **latest official version**. When **[reporting a bug](https://github.com/Virtual-TreeView/Virtual-TreeView/issues)** please include a **sample** project that allows us to quickly reproduce the bug. This can also be one of the demo projects that come with Virtual Treeview, modified to show the bug. If only small changes are required, a description is sufficient how a demo projects needs to be changed in order to replicate the bug. If you already have a solution, please supply a patch file or make a pull request. If you used a previous version that did not have the bug, please include this version number in your report. @@ -34,7 +34,7 @@ We currently focus on reducing the number of reported bugs and getting Virtual T If you want to contribute, you are welcome. We always look for help, not only for the development of the Virtual Treeview control itself, but also for maintaining the sample projects, the help or the wiki. Please send an email to: joachim(dot)marder(a)gmail.com ### License -Virtual Treeview is published under a double license: MPL 1.1 and LGPL 2.1 with static linking excpetion as described here: http://wiki.freepascal.org/modified_LGPL +Virtual Treeview is published under a double license: MPL 1.1 and LGPL 2.1 with static linking exception as described here: http://wiki.freepascal.org/modified_LGPL ### New project owner JAM Software took Virtual Treeview under its wing in 2014, but not much will change besides the homepage and download location. From 260dcb4b0bfe8b8454b86db5dde3bb90bf63276c Mon Sep 17 00:00:00 2001 From: hubalazs <10714856+hubalazs@users.noreply.github.com> Date: Fri, 20 Sep 2019 16:17:47 +0200 Subject: [PATCH 021/681] Fixed small typos --- INSTALL.txt | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/INSTALL.txt b/INSTALL.txt index e1fad1c5b..aaee4dbb3 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -11,10 +11,10 @@ Delphi / RAD Studio 10.3 and higher Installation Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" Do this for both Win32 and Win64 platform, which you can choose in the dropdown box. 4. C++ Builder users only: - In the Options dialog go to "Environment Options > C++ Options > Paths and Directories" - a) Click "Library Path > [...]" + In the Options dialog go to "Environment Options > C++ Options > Paths and Directories" + a) Click "Library Path > [...]" Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" - b) Click "System Include path > [...]" + b) Click "System Include path > [...]" Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" 5. Close the RAD Studio Options dialog by clicking "Save". @@ -27,10 +27,10 @@ Delphi / RAD Studio XE3 - 10.2 Installation Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" Do this for both Win32 and Win64 platform, which you can choose in the dropdown box. 4. C++ Builder users only: - In the Options dialog go to "Environment Options > C++ Options > Paths and Directories" - a) Click "Library Path > [...]" + In the Options dialog go to "Environment Options > C++ Options > Paths and Directories" + a) Click "Library Path > [...]" Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" - b) Click "System Include path > [...]" + b) Click "System Include path > [...]" Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" 5. Close the RAD Studio Options dialog by clicking "OK". @@ -39,28 +39,28 @@ Troubleshooting ~~~~~~~~~~~~~~~ In case you experience any problems, try to delete all these files from your disk and then start over: - Virtualtrees.* - - VTAccessibility.* + - VTAccessibility.* - VTHeaderPopup.* - - VirtualTreesD.* + - VirtualTreesD.* - VirtualTreesR.* -I recommand using UltraSearch for this task: http://www.jam-software.de/ultrasearch/ - - +I recommend using UltraSearch for this task: http://www.jam-software.de/ultrasearch/ + + Please send comments and suggestions regarding the packages and the install instructions to joachim.marder@gmail.com or open an issue. C++ Builder XE3 and higher Installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. Open the project group "Packages\CBullder XE*\VirtualTreeView.groupproj" +1. Open the project group "Packages\CBuilder XE*\VirtualTreeView.groupproj" that is closest to your version. 2. Right click on "VirtualTreesR*.bpl" and click "Build" 3. Right click on "VirtualTreesD*.bpl" and click "Install" 4. Go to "Tools > Options > Environment Options > Delphi Options > Library > Library Path > [...]" Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK", "OK" -5. Go to "Tools > Options > Environment Options > C++ Options > Paths and Directories" - a) Click "Library Path > [...]" +5. Go to "Tools > Options > Environment Options > C++ Options > Paths and Directories" + a) Click "Library Path > [...]" Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK" - b) Click "System Include path > [...]" - Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK", "OK"" + b) Click "System Include path > [...]" + Browse to the "Source" folder of VirtualTreeView, press "OK", "Add", "OK", "OK" 6. If you target Win64 you need to build VirtualTreesR*.bpl also for the platform "Win64" From b6e5e549e1c884a198f77cea1423b166facb2d18 Mon Sep 17 00:00:00 2001 From: lyynxx <46624206+lyynxx@users.noreply.github.com> Date: Tue, 24 Sep 2019 12:59:05 +0200 Subject: [PATCH 022/681] Added Header Option to include caption size for autoresizing - Added hoAutoResizeInclCaption - Added TVirtualTreeColumn.GetCaptionWidth - Adjusted TBaseVirtualTree.GetMaxColumnWidth - Adjusted Header Popup Menu to show the Auto Resize item when the option hoAutoResizeInclCaption was added to the header. --- Source/VirtualTrees.pas | 111 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ce6bc94f9..5c0990cfa 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1013,6 +1013,7 @@ TVirtualTreeColumn = class(TCollectionItem) FHasImage: Boolean; FDefaultSortDirection: TSortDirection; function GetCaptionAlignment: TAlignment; + function GetCaptionWidth: TDimension; function GetLeft: TDimension; function IsBiDiModeStored: Boolean; function IsCaptionAlignmentStored: Boolean; @@ -1070,6 +1071,7 @@ TVirtualTreeColumn = class(TCollectionItem) property BiDiMode: TBiDiMode read FBiDiMode write SetBiDiMode stored IsBiDiModeStored; property CaptionAlignment: TAlignment read GetCaptionAlignment write SetCaptionAlignment stored IsCaptionAlignmentStored default taLeftJustify; + property CaptionWidth: TDimension read GetCaptionWidth; property CheckType: TCheckType read FCheckType write SetCheckType default ctCheckBox; property CheckState: TCheckState read FCheckState write SetCheckState default csUncheckedNormal; property CheckBox: Boolean read FCheckBox write SetCheckBox default False; @@ -1244,7 +1246,8 @@ TVTFixedAreaConstraints = class(TPersistent) hoHeightDblClickResize, // Allow the header to resize itself to its default height. hoHeaderClickAutoSort, // Clicks on the header will make the clicked column the SortColumn or toggle sort direction if // it already was the sort column - hoAutoColumnPopupMenu // Show a context menu for activating and deactivating columns on right click + hoAutoColumnPopupMenu, // Show a context menu for activating and deactivating columns on right click + hoAutoResizeInclCaption // Includes the header caption for the auto resizing ); TVTHeaderOptions = set of TVTHeaderOption; @@ -6504,6 +6507,107 @@ function TVirtualTreeColumn.GetCaptionAlignment: TAlignment; //---------------------------------------------------------------------------------------------------------------------- +function TVirtualTreeColumn.GetCaptionWidth: TDimension; +var + Theme: HTHEME; + AdvancedOwnerDraw: Boolean; + PaintInfo: THeaderPaintInfo; + RequestedElements: THeaderPaintElements; + + TextSize: TSize; + HeaderGlyphSize: TPoint; + UseText: Boolean; + R: TRect; +begin + AdvancedOwnerDraw := (hoOwnerDraw in Owner.FHeader.FOptions) and Assigned(Owner.FHeader.Treeview.FOnAdvancedHeaderDraw) and Assigned(Owner.FHeader.Treeview.FOnHeaderDrawQueryElements) and + not(csDesigning in Owner.FHeader.Treeview.ComponentState); + + PaintInfo.Column := Self; + PaintInfo.TargetCanvas := Owner.FHeaderBitmap.Canvas; + + with PaintInfo, Column do + begin + ShowHeaderGlyph := (hoShowImages in Owner.FHeader.FOptions) and ((Assigned(Owner.FHeader.FImages) and (FImageIndex > -1)) or FCheckBox); + ShowSortGlyph := ((Owner.FHeader.FSortColumn > -1) and (Self = Owner.Items[Owner.FHeader.FSortColumn])) and (hoShowSortGlyphs in Owner.FHeader.FOptions); + + // This path for text columns or advanced owner draw. + // See if the application wants to draw part of the header itself. + RequestedElements := []; + if AdvancedOwnerDraw then + begin + PaintInfo.Column := Self; + Owner.FHeader.Treeview.DoHeaderDrawQueryElements(PaintInfo, RequestedElements); + end; + end; + + UseText := Length(FText) > 0; + // If nothing is to show then don't waste time with useless preparation. + if not(UseText or PaintInfo.ShowHeaderGlyph or PaintInfo.ShowSortGlyph) then + Exit(0); + + // Calculate sizes of the involved items. + with Owner, Header do + begin + if PaintInfo.ShowHeaderGlyph then + if not FCheckBox then + begin + if Assigned(FImages) then + HeaderGlyphSize := Point(FImages.Width, FImages.Height); + end + else + with Self.Owner.Header.Treeview do + begin + if Assigned(FCheckImages) then + HeaderGlyphSize := Point(FCheckImages.Width, FCheckImages.Height); + end + else + HeaderGlyphSize := Point(0, 0); + if PaintInfo.ShowSortGlyph then + begin + if tsUseExplorerTheme in FHeader.Treeview.FStates then + begin + R := Rect(0, 0, 100, 100); + Theme := OpenThemeData(FHeader.Treeview.Handle, 'HEADER'); + GetThemePartSize(Theme, PaintInfo.TargetCanvas.Handle, HP_HEADERSORTARROW, HSAS_SORTEDUP, @R, TS_TRUE, PaintInfo.SortGlyphSize); + CloseThemeData(Theme); + end + else + begin + PaintInfo.SortGlyphSize.cx := Header.Treeview.ScaledPixels(16); + PaintInfo.SortGlyphSize.cy := Header.Treeview.ScaledPixels(4); + end; + end + else + begin + PaintInfo.SortGlyphSize.cx := 0; + PaintInfo.SortGlyphSize.cy := 0; + end; + end; + + if UseText then + begin + GetTextExtentPoint32W(PaintInfo.TargetCanvas.Handle, PWideChar(FText), Length(FText), TextSize); + Inc(TextSize.cx, 2); + end + else + begin + TextSize.cx := 0; + TextSize.cy := 0; + end; + + // if CalculateTextRect then + Result := TextSize.cx; + if PaintInfo.ShowHeaderGlyph then + if Layout in [blGlyphLeft, blGlyphRight] then + Inc(Result, HeaderGlyphSize.X + FSpacing) + else // if Layout in [ blGlyphTop, blGlyphBottom] then + Result := Max(Result, HeaderGlyphSize.X); + if PaintInfo.ShowSortGlyph then + Inc(Result, PaintInfo.SortGlyphSize.cx + FSpacing + 2); // without this +2, there is a slight movement of the sort glyph when expanding the column + +end; +//---------------------------------------------------------------------------------------------------------------------- + function TVirtualTreeColumn.GetLeft: Integer; begin @@ -8040,7 +8144,7 @@ function TVirtualTreeColumns.HandleClick(P: TPoint; Button: TMouseButton; Force, TVTHeaderPopupMenu(fColumnPopupMenu).OnAddHeaderPopupItem := HeaderPopupMenuAddHeaderPopupItem; TVTHeaderPopupMenu(fColumnPopupMenu).OnColumnChange := HeaderPopupMenuColumnChange; fColumnPopupMenu.PopupComponent := Header.Treeview; - if (hoDblClickResize in Header.Options) and (Header.Treeview.ChildCount[nil] > 0) then + if (hoDblClickResize in Header.Options) and ((Header.Treeview.ChildCount[nil] > 0) or (hoAutoResizeInclCaption in Header.Options)) then TVTHeaderPopupMenu(fColumnPopupMenu).Options := TVTHeaderPopupMenu(fColumnPopupMenu).Options + [poResizeToFitItem] else TVTHeaderPopupMenu(fColumnPopupMenu).Options := TVTHeaderPopupMenu(fColumnPopupMenu).Options - [poResizeToFitItem]; @@ -27723,6 +27827,9 @@ function TBaseVirtualTree.GetMaxColumnWidth(Column: TColumnIndex; UseSmartColumn else LastNode := nil; + if hoAutoResizeInclCaption in FHeader.Options then + Result := Result + (2 * Header.Columns[Column].FMargin + Header.Columns[Column].CaptionWidth + 2); // not sure where this +2 is coming from + while Assigned(Run) and not OperationCanceled do begin GetOffsets(Run, lOffsets, TVTElement.ofsLabel, Column); From aea617905ce34a09021c8be73c7bce7f12b2e985 Mon Sep 17 00:00:00 2001 From: lyynxx <46624206+lyynxx@users.noreply.github.com> Date: Tue, 24 Sep 2019 13:08:08 +0200 Subject: [PATCH 023/681] Removed comment. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 5c0990cfa..0b838e338 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -27828,7 +27828,7 @@ function TBaseVirtualTree.GetMaxColumnWidth(Column: TColumnIndex; UseSmartColumn LastNode := nil; if hoAutoResizeInclCaption in FHeader.Options then - Result := Result + (2 * Header.Columns[Column].FMargin + Header.Columns[Column].CaptionWidth + 2); // not sure where this +2 is coming from + Result := Result + (2 * Header.Columns[Column].FMargin + Header.Columns[Column].CaptionWidth + 2); while Assigned(Run) and not OperationCanceled do begin From 7ee6e806ba9d3f9d63b041a365ff549ae1fcabfc Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 2 Oct 2019 20:53:34 +0100 Subject: [PATCH 024/681] * Fixed issue #926: KEY_UP/KEY_DOWN clears entire selection when reversing direction * Introduced const for default column spacing --- Source/VirtualTrees.pas | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 0b838e338..ef85abc31 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -982,6 +982,8 @@ THeaderPaintInfo = record TVirtualTreeColumn = class(TCollectionItem) + private + const cDefaultColumnSpacing = 3; private FText, FHint: string; @@ -1087,7 +1089,7 @@ TVirtualTreeColumn = class(TCollectionItem) property EditOptions: TVTEditOptions read FEditOptions write FEditOptions default toDefaultEdit; property EditNextColumn: TDimension read FEditNextColumn write FEditNextColumn default -1; property Position: TColumnPosition read FPosition write SetPosition; - property Spacing: TDimension read FSpacing write SetSpacing default 3; + property Spacing: TDimension read FSpacing write SetSpacing default cDefaultColumnSpacing; property Style: TVirtualTreeColumnStyle read FStyle write SetStyle default vsText; property Tag: NativeInt read FTag write FTag default 0; property Text: string read GetText write SetText; @@ -6409,7 +6411,7 @@ constructor TVirtualTreeColumn.Create(Collection: TCollection); FMaxWidth := 10000; FImageIndex := -1; FMargin := 4; - FSpacing := 3; + FSpacing := cDefaultColumnSpacing; FText := ''; FOptions := DefaultColumnOptions; FAlignment := taLeftJustify; @@ -16803,9 +16805,8 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); begin if not EndEditNode then exit; - if (not PerformMultiSelect or (CompareNodePositions(LastFocused, FRangeAnchor) > 0)) and - Assigned(FFocusedNode) then - ClearSelection(False); + if (not PerformMultiSelect or (CompareNodePositions(LastFocused, Node) < -1)) and Assigned(FFocusedNode) then + ClearSelection(False); // Clear selection only if more than one node was skipped. See issue #926 if FFocusedColumn <= NoColumn then FFocusedColumn := FHeader.MainColumn; FocusedNode := Node; @@ -16831,8 +16832,8 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); begin if not EndEditNode then exit; - if (not PerformMultiSelect or (CompareNodePositions(LastFocused, FRangeAnchor) < 0)) and Assigned(FFocusedNode) then - ClearSelection(False); + if (not PerformMultiSelect or (CompareNodePositions(LastFocused, Node) > 1)) and Assigned(FFocusedNode) then + ClearSelection(False); // Clear selection only if more than one node was skipped. See issue #926 if FFocusedColumn <= NoColumn then FFocusedColumn := FHeader.MainColumn; FocusedNode := Node; @@ -17005,11 +17006,14 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); begin // Finally change the selection for a specific range of nodes. if DoRangeSelect then - ToggleSelection(LastFocused, FFocusedNode); - + ToggleSelection(LastFocused, FFocusedNode) // Make sure the new focused node is also selected. - if (LastFocused <> FFocusedNode) or ForceSelection then - AddToSelection(FFocusedNode, False); + else if (LastFocused <> FFocusedNode) then begin + if ForceSelection then + AddToSelection(FFocusedNode, False) + else + ToggleSelection(LastFocused, FFocusedNode); // See issue #926 + end; end; // If a repaint is needed then paint the entire tree because of the ClearSelection call, From 211a05982f4170c00cf8fc6e74abee244a70e7dd Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 2 Oct 2019 21:14:31 +0100 Subject: [PATCH 025/681] Fixed issue #927: GetSelectedData() may return wrong array --- Source/VirtualTrees.pas | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ef85abc31..77d00c401 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -13363,12 +13363,15 @@ function TBaseVirtualTree.GetSelectedData: TArray; i: Integer; begin SetLEngth(Result, Self.SelectedCount); + i := 0; lItem := Self.GetFirstSelected; - for i := 0 to SelectedCount - 1 do + while Assigned(lItem) do begin Result[i] := Self.GetNodeData(lItem); lItem := Self.GetNextSelected(lItem); + Inc(i); end; + SetLength(Result, i); // See issue #927, SelectedCount may not yet be updated. end; //---------------------------------------------------------------------------------------------------------------------- From b62976a99c7deb626f24a7dc24f76d86ae2b707b Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 2 Oct 2019 22:03:39 +0100 Subject: [PATCH 026/681] Fixed issue #885: Two fast clicks do not change the check state of all selected nodes --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 77d00c401..bead7c6d9 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -22270,7 +22270,7 @@ procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitI NewCheckState := DetermineNextCheckState(HitInfo.HitNode.CheckType, HitInfo.HitNode.CheckState); if (ssLeft in KeysToShiftState(Message.Keys)) and DoChecking(HitInfo.HitNode, NewCheckState) then begin - SetCheckState(HitInfo.HitNode, NewCheckState); + SetCheckStateForAll(NewCheckState, True); MayEdit := False; end; end// if hiOnItemCheckBox From d9ca7e7f061284cb63190dbd96a8743ef79dab81 Mon Sep 17 00:00:00 2001 From: lyynxx <46624206+lyynxx@users.noreply.github.com> Date: Sat, 5 Oct 2019 16:01:12 +0200 Subject: [PATCH 027/681] Fix for issue #872: TVirtualTreeColumns: Show column header text as hint in case column is to narrow --- Source/VirtualTrees.pas | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index bead7c6d9..d81374d08 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -15860,6 +15860,12 @@ procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow); if (HitInfo.HitColumn > NoColumn) and not (csLButtonDown in ControlState) then begin HintStr := FHeader.FColumns[HitInfo.HitColumn].FHint; + if HintStr = '' then + with FHeader.FColumns[HitInfo.HitColumn] do + begin + if (2 * FMargin + CaptionWidth + 1) >= Width then + HintStr := FCaptionText; + end; if HintStr <> '' then ShowOwnHint := True else From 4d7e137f083340e03ff48578614c69204505e5af Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Oct 2019 13:54:43 +0200 Subject: [PATCH 028/681] Correct wrongly assigned CustomCheckImages, we want to use the system checkimages. --- .gitignore | 1 + Demos/Advanced/GeneralAbilitiesDemo.dfm | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 10ae397ca..6bf745683 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ VirtualTreeView.zip *.#00 *.pch +*.skincfg diff --git a/Demos/Advanced/GeneralAbilitiesDemo.dfm b/Demos/Advanced/GeneralAbilitiesDemo.dfm index 29498e88d..9cb5d04f3 100644 --- a/Demos/Advanced/GeneralAbilitiesDemo.dfm +++ b/Demos/Advanced/GeneralAbilitiesDemo.dfm @@ -34,7 +34,6 @@ object GeneralForm: TGeneralForm Colors.HotColor = clBlack Colors.UnfocusedSelectionBorderColor = clBtnShadow Ctl3D = True - CustomCheckImages = TreeImages DefaultNodeHeight = 20 DragCursor = crHelp DragMode = dmAutomatic @@ -273,7 +272,6 @@ object GeneralForm: TGeneralForm Margins.Bottom = 6 Align = alLeft Caption = 'Switch main column:' - ExplicitHeight = 13 end object MainColumnUpDown: TUpDown Left = 112 @@ -297,7 +295,7 @@ object GeneralForm: TGeneralForm Left = 22 Top = 148 Bitmap = { - 494C010112001300100010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 + 494C010112001300040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 0000000000003600000028000000400000005000000001002000000000000050 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 @@ -1006,7 +1004,7 @@ object GeneralForm: TGeneralForm Left = 32 Top = 200 Bitmap = { - 494C010151005400100018001800FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 + 494C010151005400040018001800FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 000000000000360000002800000060000000F8010000010020000000000000F4 0200000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 From 86038d4a6e81075ed86b8758b6aaa357e91865bc Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Oct 2019 13:59:34 +0200 Subject: [PATCH 029/681] Update VirtualTrees.pas * For ikState use Images by default if not StateImages are assigned * Added fallback code when getting checkbox size from system failed. --- Source/VirtualTrees.pas | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index bead7c6d9..5f9a875bd 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -4324,6 +4324,8 @@ function CreateSystemImageSet(pControl: TWinControl): TImageList; //--------------- end local functions --------------------------------------- +const + cDefaultCheckboxSize = 13;// Used when no other value is available var I: Integer; lSize: TSize; @@ -4345,8 +4347,13 @@ function CreateSystemImageSet(pControl: TWinControl): TImageList; end else Res := StyleServices.GetElementSize(BM.Canvas.Handle, StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal), TElementSize.esActual, lSize); - if not Res then + if not Res then begin lSize := TSize.Create(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK)); + if lSize.cx = 0 then begin + lSize.cx := ScaledPixels(cDefaultCheckboxSize); + lSize.cy := lSize.cx; + end;// if + end;//if Result := TImageList.CreateSize(lSize.cx, lSize.cy); with Result do @@ -20069,7 +20076,7 @@ function TBaseVirtualTree.DoGetImageIndex(Node: PVirtualNode; Kind: TVTImageKind const cTVTImageKind2String: Array [TVTImageKind] of string = ('ikNormal', 'ikSelected', 'ikState', 'ikOverlay'); begin - if Kind = ikState then + if (Kind = ikState) and Assigned(StateImages) then Result := Self.StateImages else Result := Self.Images; From 19b92b2e28c42f35ad69b69e1b4b7464f8893b7f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Oct 2019 14:05:19 +0200 Subject: [PATCH 030/681] Fixed previous commit, ScaledPixels() is only availbale within a tree control. --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 5f9a875bd..19681ad50 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -4349,8 +4349,8 @@ function CreateSystemImageSet(pControl: TWinControl): TImageList; Res := StyleServices.GetElementSize(BM.Canvas.Handle, StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal), TElementSize.esActual, lSize); if not Res then begin lSize := TSize.Create(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK)); - if lSize.cx = 0 then begin - lSize.cx := ScaledPixels(cDefaultCheckboxSize); + if lSize.cx = 0 then begin // error? (Should happen rarely only) + lSize.cx := MulDiv(cDefaultCheckboxSize, Screen.PixelsPerInch, USER_DEFAULT_SCREEN_DPI); lSize.cy := lSize.cx; end;// if end;//if From 2bdeaba3de032f24e67d306140f1b5617545333f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 13 Oct 2019 21:48:23 +0200 Subject: [PATCH 031/681] Fixed issue #930: Removing Canvas.Refresh from TBaseVirtualTree.PaintCheckImage breaks painting of grid lines. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index a5e853230..64330156f 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -23876,7 +23876,7 @@ procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas; const ImageInfo: TVT end;//if R := Rect(XPos, YPos, XPos + lSize.cx, YPos + lSize.cy); StyleServices.DrawElement(Canvas.Handle, Details, R); - //Canvas.Refresh; // Why is this needed? + Canvas.Refresh; // Every time you give a Canvas.Handle away to some other code you can't control you have to call Canvas.Refresh afterwards because the Canvas object and the HDC can be out of sync. end; if (Index in [ckButtonNormal..ckButtonDisabled]) then begin Canvas.Pen.Color := clGray; From 0bebc37bbd85c7c8029c4ccf054d3ba9d8e62a3c Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 4 Nov 2019 20:52:27 +0100 Subject: [PATCH 032/681] V7.3 --- CHANGES.txt | 5 ++++- Source/VirtualTrees.pas | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 84eb140cd..47d4f7702 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,7 @@ -V7.2.1: (07 Mar 2019) +V7.3: (04 Nov 2019) +See: https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/13 + +V7.2.1: (07 Mar 2019) See: https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/14?closed=1 V7.2: (18 Feb 2019) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 64330156f..c437c7916 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -86,7 +86,7 @@ interface {$ENDIF} const - VTVersion = '7.2.1'; + VTVersion = '7.3.0'; const VTTreeStreamVersion = 3; From d4b5b0fae00a9fea882c0350e8e238ee8200e960 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 15 Nov 2019 13:02:52 +0100 Subject: [PATCH 033/681] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa89f83db..9f122cb4d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Virtual Treeview is a Delphi treeview control built from ground up. Many years o I don't use C++ Builder and my experience with it is very limited. This makes it difficult to take care about bugs that are reported in C++ Builder and to maintain the C++ Builder packages. I would be great if someone would volunteer to do this. Please contact me at joachim.marder+CPP@gmail.com. ### Downloads -**V7.2** official release for **RAD Studio XE3 to 10.3 Rio**: [JAM Software](https://www.jam-software.com/virtual-treeview/VirtualTreeView.zip) ([Changes](https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/12?closed=1)) +**V7.3** official release for **RAD Studio XE3 to 10.3 Rio**: [JAM Software](https://www.jam-software.com/virtual-treeview/VirtualTreeView.zip) ([Changes](https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/12?closed=1)) An experimental **FireMonkey** port can be found here: [livius2/Virtual-TreeView](https://github.com/livius2/Virtual-TreeView) From 72c3209eb992732de2b072219a981ac89949c3c5 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 25 Nov 2019 21:23:33 +0100 Subject: [PATCH 034/681] Fix for issue #933 (PrepareBitmaps is called before FCurrentPPI is set): Prevent TBaseVirtualTree.UpdateHeaderRect() from creating the window handle --- Source/VirtualTrees.pas | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index c437c7916..014527493 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -25320,8 +25320,10 @@ procedure TBaseVirtualTree.UpdateHeaderRect; FHeaderRect := Rect(0, 0, Width, Height); // Consider borders... - Size := GetBorderDimensions; - InflateRect(FHeaderRect, Size.cx, Size.cy); + if HandleAllocated then begin // Prevent preliminary creation of window handle, see issue #933 + Size := GetBorderDimensions(); + InflateRect(FHeaderRect, Size.cx, Size.cy); + end; // ... and bevels. OffsetX := BorderWidth; From ba0bfb1deb4aec2d6bc47689c491ccd95e6f6438 Mon Sep 17 00:00:00 2001 From: Sorien Date: Tue, 26 Nov 2019 13:07:33 +0100 Subject: [PATCH 035/681] Allow to override DoColumnVisibilityChanged --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 014527493..6f37418bd 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -2576,7 +2576,7 @@ TBaseVirtualTree = class(TCustomControl) procedure DoColumnClick(Column: TColumnIndex; Shift: TShiftState); virtual; procedure DoColumnDblClick(Column: TColumnIndex; Shift: TShiftState); virtual; procedure DoColumnResize(Column: TColumnIndex); virtual; - procedure DoColumnVisibilityChanged(const Column: TColumnIndex; Visible: Boolean); + procedure DoColumnVisibilityChanged(const Column: TColumnIndex; Visible: Boolean); virtual; function DoCompare(Node1, Node2: PVirtualNode; Column: TColumnIndex): Integer; virtual; function DoCreateDataObject: IDataObject; virtual; function DoCreateDragManager: IVTDragManager; virtual; From 4c8f5ccea8ee23d1cebd461854b80f3a3c2931cb Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 30 Nov 2019 12:20:02 +0100 Subject: [PATCH 036/681] Issue #936: TVTHeader.DragTo() is now virtual. --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 6f37418bd..341fdd912 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1355,7 +1355,7 @@ TVTHeader = class(TPersistent) function DoHeightTracking(var P: TPoint; Shift: TShiftState): Boolean; virtual; function DoHeightDblClickResize(var P: TPoint; Shift: TShiftState): Boolean; virtual; procedure DoSetSortColumn(Value: TColumnIndex; pSortDirection: TSortDirection); virtual; - procedure DragTo(P: TPoint); + procedure DragTo(P: TPoint); virtual; procedure FixedAreaConstraintsChanged(Sender: TObject); function GetColumnsClass: TVirtualTreeColumnsClass; virtual; function GetOwner: TPersistent; override; @@ -25305,7 +25305,7 @@ procedure TBaseVirtualTree.UpdateDesigner; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.UpdateHeaderRect; +procedure TBaseVirtualTree.UpdateHeaderRect(); // Calculates the rectangle the header occupies in non-client area. // These coordinates are in window rectangle. From 26b2bede2720ac235a5aaa5e736ae814d037bd14 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 10 Dec 2019 10:25:40 +0100 Subject: [PATCH 037/681] Fixed issue #939: Column's hot text color implementation is missing --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 19681ad50..91808d0f3 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -11837,7 +11837,7 @@ function TVTColors.GetColor(const Index: TVTColorEnum): TColor; if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemHot), ecTextColor, Result) then Result := StyleServices.GetSystemColor(FColors[Index]); cHeaderHotColor: - if not StyleServices.GetElementColor(StyleServices.GetElementDetails(thHeaderItemNormal), ecTextColor, Result) then + if not StyleServices.GetElementColor(StyleServices.GetElementDetails(thHeaderItemHot), ecTextColor, Result) then Result := StyleServices.GetSystemColor(FColors[Index]); cSelectionTextColor: if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemSelected), ecTextColor, Result) then @@ -20981,7 +20981,7 @@ function TBaseVirtualTree.DoValidateCache: Boolean; // New cache entry to set up. with FPositionCache[Index] do begin - Node := CurrentNode; // 2 EAccessViolation seen here in TreeSize V4.3.1 (Write of address 00000000) + Node := CurrentNode; // 2 EAccessViolation seen here in TreeSize V4.3.1, 1 in V4.4.0 (Write of address 00000000) AbsoluteTop := CurrentTop; end; Inc(Index); From 4fe7a55d0bdcfb82391306c6c50661124f0df5da Mon Sep 17 00:00:00 2001 From: Sorien Date: Wed, 11 Dec 2019 08:58:51 +0100 Subject: [PATCH 038/681] use proper background for toggle animation --- Source/VirtualTrees.pas | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 6f37418bd..f8f36537c 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -32015,7 +32015,15 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); begin Window := Handle; DC := GetDC(Handle); - Self.Brush.Color := FColors.BackGroundColor; + + if (toShowBackground in FOptions.FPaintOptions) and Assigned(FBackground.Graphic) then + Self.Brush.Style := bsClear + else + begin + Self.Brush.Style := bsSolid; + Self.Brush.Color := FColors.BackGroundColor; + end; + Brush := Self.Brush.Handle; if (Mode1 <> tamNoScroll) and (Mode2 <> tamNoScroll) then From a53449fbc030c600328457b7e108c089e4fef643 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 11 Dec 2019 21:26:03 +0100 Subject: [PATCH 039/681] Fixed issue #943: Add assign check to TVirtualTreeColumn.Create(). Call ParentBiDiModeChanged() and ParentColorChanged() whenever a new owner is assigned. --- Source/VirtualTrees.pas | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 64227fe19..9f5d7693a 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1048,6 +1048,7 @@ TVirtualTreeColumn = class(TCollectionItem) function GetOwner: TVirtualTreeColumns; reintroduce; procedure ReadHint(Reader: TReader); procedure ReadText(Reader: TReader); + procedure SetCollection(Value: TCollection); override; property HasImage: Boolean read FHasImage; property ImageRect: TRect read FImageRect; public @@ -6436,9 +6437,16 @@ constructor TVirtualTreeColumn.Create(Collection: TCollection); inherited Create(Collection); - FWidth := Owner.FDefaultWidth; - FLastWidth := Owner.FDefaultWidth; - FPosition := Owner.Count - 1; + if Assigned(Owner) then begin + FWidth := Owner.FDefaultWidth; + FLastWidth := Owner.FDefaultWidth; + FPosition := Owner.Count - 1; + end; +end; + +procedure TVirtualTreeColumn.SetCollection(Value: TCollection); +begin + inherited; // Read parent bidi mode and color values as default values. ParentBiDiModeChanged; ParentColorChanged; @@ -10320,7 +10328,7 @@ function TVTHeader.HandleHeaderMouseMove(var Message: TWMMouseMove): Boolean; begin NewWidth := FTrackPoint.X - XPos; NextColumn := FColumns.GetPreviousVisibleColumn(FColumns.FTrackIndex); - end + end else begin NewWidth := XPos - FTrackPoint.X; From 224c1a6383f434124a07a3a356fc0b17feebe1b2 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 13 Dec 2019 11:59:43 +0100 Subject: [PATCH 040/681] Fixed wrong comparison in TBaseVirtualTree.DoValidateCache() that can lead to access violations. --- Source/VirtualTrees.pas | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ae5f36b93..b24f5e107 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -20923,7 +20923,7 @@ procedure TBaseVirtualTree.DoUpdating(State: TVTUpdateState); //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoValidateCache: Boolean; +function TBaseVirtualTree.DoValidateCache(): Boolean; // This method fills the cache, which is used to speed up searching for nodes. // The strategy is simple: Take the current number of visible nodes and distribute evenly a number of marks @@ -20980,8 +20980,8 @@ function TBaseVirtualTree.DoValidateCache: Boolean; while not (tsStopValidation in FStates) do begin // If the cache is full then stop the loop. - if (Integer(Index) > Length(FPositionCache)) then // ADDED: 17.09.2013 - Veit Zimmermann - Break; // ADDED: 17.09.2013 - Veit Zimmermann + if (Integer(Index) >= Length(FPositionCache)) then + Break; if (EntryCount mod CacheThreshold) = 0 then begin // New cache entry to set up. From 6c6def24a5a8b93b0f2fd4442ffe64abcd50515e Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 25 Jan 2020 14:10:41 +0100 Subject: [PATCH 041/681] Fixed for #947: Make sure every code path in TVTColors.GetColor() returns a value. --- Source/VirtualTrees.pas | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index f49e4a630..984e95151 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -11840,7 +11840,9 @@ function TVTColors.GetColor(const Index: TVTColorEnum): TColor; Result := StyleServices.GetSystemColor(FColors[Index]); cBorderColor: if (seBorder in FOwner.StyleElements) then - Result := StyleServices.GetSystemColor(FColors[Index]); + Result := StyleServices.GetSystemColor(FColors[Index]) + else + Result := FColors[Index]; cHotColor: if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemHot), ecTextColor, Result) then Result := StyleServices.GetSystemColor(FColors[Index]); From 6b7ddd01c67fa88e74ce9f4f3612443e5f999b95 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 5 Feb 2020 22:28:55 +0100 Subject: [PATCH 042/681] Implemented issue #949: Allow deletion of a node without immediate reindexing --- Source/VirtualTrees.pas | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 984e95151..934e91eb4 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -3006,7 +3006,7 @@ TBaseVirtualTree = class(TCustomControl) procedure CopyToClipboard; virtual; procedure CutToClipboard; virtual; procedure DeleteChildren(Node: PVirtualNode; ResetHasChildren: Boolean = False); - procedure DeleteNode(Node: PVirtualNode); overload; inline; + procedure DeleteNode(Node: PVirtualNode; pReIndex: Boolean = True); overload; inline; procedure DeleteNodes(const pNodes: TNodeArray); procedure DeleteSelectedNodes; virtual; function Dragging: Boolean; @@ -26531,9 +26531,9 @@ procedure TBaseVirtualTree.DeleteNode(Node: PVirtualNode; Reindex: Boolean; Pare end; end; -procedure TBaseVirtualTree.DeleteNode(Node: PVirtualNode); +procedure TBaseVirtualTree.DeleteNode(Node: PVirtualNode; pReIndex: Boolean = True); begin - DeleteNode(Node, True, False); + DeleteNode(Node, pReIndex, False); end; //---------------------------------------------------------------------------------------------------------------------- From 4c7e6711f4913f3bdf49657d089c1badc9808e22 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 10 Feb 2020 23:19:55 +0100 Subject: [PATCH 043/681] Issue #951; * Extracted code in initialization section into new class procedure TVirtualTreeAccessibility.RegisterDefaultAccessibleProviders() * Removed finalization section: TVTAccessibilityFactory is destroyed anyway, so unregistering is unnecessary. --- Source/VirtualTrees.Accessibility.pas | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Source/VirtualTrees.Accessibility.pas b/Source/VirtualTrees.Accessibility.pas index 568815a5c..81a86ab1b 100644 --- a/Source/VirtualTrees.Accessibility.pas +++ b/Source/VirtualTrees.Accessibility.pas @@ -17,6 +17,8 @@ TVirtualTreeAccessibility = class(TInterfacedObject, IDispatch, IAccessible) FVirtualTree: TVirtualStringTree; public constructor Create(AVirtualTree: TVirtualStringTree); + /// Register the default accessible provider of Virtual TreeView + class procedure RegisterDefaultAccessibleProviders(); { IAccessibility } function Get_accParent(out ppdispParent: IDispatch): HResult; stdcall; @@ -766,7 +768,8 @@ function TVTMultiColumnAccessibleItemProvider.CreateIAccessible(ATree: TBaseVirt DefaultAccessibleItemProvider: TVTDefaultAccessibleItemProvider; MultiColumnAccessibleProvider: TVTMultiColumnAccessibleItemProvider; -initialization +class procedure TVirtualTreeAccessibility.RegisterDefaultAccessibleProviders(); +begin if DefaultAccessibleProvider = nil then begin DefaultAccessibleProvider := TVTDefaultAccessibleProvider.Create; @@ -782,13 +785,11 @@ initialization MultiColumnAccessibleProvider := TVTMultiColumnAccessibleItemProvider.Create; TVTAccessibilityFactory.GetAccessibilityFactory.RegisterAccessibleProvider(MultiColumnAccessibleProvider); end; -finalization - TVTAccessibilityFactory.GetAccessibilityFactory.UnRegisterAccessibleProvider(MultiColumnAccessibleProvider); - MultiColumnAccessibleProvider := nil; - TVTAccessibilityFactory.GetAccessibilityFactory.UnRegisterAccessibleProvider(DefaultAccessibleItemProvider); - DefaultAccessibleItemProvider := nil; - TVTAccessibilityFactory.GetAccessibilityFactory.UnRegisterAccessibleProvider(DefaultAccessibleProvider); - DefaultAccessibleProvider := nil; +end; + + +initialization + TVirtualTreeAccessibility.RegisterDefaultAccessibleProviders(); end. From aaf9515930591d52537b89b7cb2e433945552cee Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 10 Feb 2020 23:34:55 +0100 Subject: [PATCH 044/681] Added unit "VirtualTrees.Accessibility" to uses for better test coverage. Issue #951 --- Demos/Advanced/Main.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Demos/Advanced/Main.pas b/Demos/Advanced/Main.pas index 04be4fd0a..0499f900b 100644 --- a/Demos/Advanced/Main.pas +++ b/Demos/Advanced/Main.pas @@ -56,7 +56,7 @@ procedure SetStatusbarText(const S: string); implementation uses - CommCtrl, + CommCtrl, VirtualTrees.Accessibility, SpeedDemo, GeneralAbilitiesDemo, DrawTreeDemo, PropertiesDemo, GridDemo, VisibilityDemo, AlignDemo, WindowsXPStyleDemo, MultilineDemo, HeaderCustomDrawDemo, States; From 2d1958e8d8be00a2eadb9a866e5dd020b2dd756f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 10 Feb 2020 23:38:55 +0100 Subject: [PATCH 045/681] Do not restrict hint to column 1. Issue #952 --- Demos/Advanced/DrawTreeDemo.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Demos/Advanced/DrawTreeDemo.pas b/Demos/Advanced/DrawTreeDemo.pas index 878cdea30..90e37972c 100644 --- a/Demos/Advanced/DrawTreeDemo.pas +++ b/Demos/Advanced/DrawTreeDemo.pas @@ -604,7 +604,7 @@ procedure TDrawTreeForm.VDT1GetHintSize(Sender: TBaseVirtualTree; Node: PVirtual begin Data := Sender.GetNodeData(Node); - if Assigned(Data) and Assigned(Data.Image) and (Column = 1) then + if Assigned(Data) and Assigned(Data.Image) then R := Rect(0, 0, 2 * Data.Image.Width, 2 * Data.Image.Height) else R := Rect(0, 0, 0, 0); @@ -622,7 +622,7 @@ procedure TDrawTreeForm.VDT1DrawHint(Sender: TBaseVirtualTree; Canvas: TCanvas; begin Data := Sender.GetNodeData(Node); - if Assigned(Data) and Assigned(Data.Image) and (Column = 1) then + if Assigned(Data) and Assigned(Data.Image) then begin SetStretchBltMode(Canvas.Handle, HALFTONE); StretchBlt(Canvas.Handle, 0, 0, 2 * Data.Image.Width, 2 * Data.Image.Height, Data.Image.Canvas.Handle, 0, 0, From 2ecff56ba3462bf6f3cfcdaf01c90c631b47cb21 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 12 Feb 2020 18:23:27 +0100 Subject: [PATCH 046/681] Fixed Issue #953: horizontal synchronous scrolling does not work --- Source/VirtualTrees.pas | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 934e91eb4..461ebe823 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -20699,10 +20699,11 @@ function TBaseVirtualTree.DoSetOffsetXY(Value: TPoint; Options: TScrollUpdateOpt begin if DeltaX <> 0 then begin + UpdateHorizontalScrollBar(suoRepaintScrollBars in Options); if (suoRepaintHeader in Options) and (hoVisible in FHeader.FOptions) then FHeader.Invalidate(nil); if not (tsSizing in FStates) and (FScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssHorizontal, System.UITypes.TScrollStyle.ssBoth]) then - UpdateHorizontalScrollBar(suoRepaintScrollBars in Options); + UpdateVerticalScrollBar(suoRepaintScrollBars in Options); end; if (DeltaY <> 0) and ([tsThumbTracking, tsSizing] * FStates = []) then From aa1e75a5e2f6f1ac655b04c8fba04c95cb2991ff Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 4 Mar 2020 23:06:55 +0100 Subject: [PATCH 047/681] Issue #951: Registration of DefaultAccessibleProvider not done for C++ Builder --- Source/VirtualTrees.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 461ebe823..597900ab2 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -72,6 +72,7 @@ interface {$HPPEMIT '#pragma comment(lib, "VirtualTreesR")'} {$endif} {$HPPEMIT '#pragma comment(lib, "Shell32")'} +{$HPPEMIT '#pragma link "VirtualTrees.Accessibility"'} uses Winapi.Windows, Winapi.oleacc, Winapi.Messages, System.SysUtils, Vcl.Graphics, From faf9fe8a04f5d5186e9659d61dcd4f72039377f0 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 15 Mar 2020 15:14:46 +0100 Subject: [PATCH 048/681] TBaseVirtualTree.InternalClearSelection(): Now caching node in new local variable lNNode. This is more efficient and prevents exceptptions seen with the toSyncCheckboxesWithSelection flag set. --- Source/VirtualTrees.pas | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 597900ab2..eaf404485 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -23129,11 +23129,11 @@ procedure TBaseVirtualTree.InternalCacheNode(Node: PVirtualNode); //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InternalClearSelection; +procedure TBaseVirtualTree.InternalClearSelection(); var Count: Integer; - + lNode: PVirtualNode; begin // It is possible that there are invalid node references in the selection array // if the tree update is locked and changes in the structure were made. @@ -23151,11 +23151,12 @@ procedure TBaseVirtualTree.InternalClearSelection; while FSelectionCount > 0 do begin Dec(FSelectionCount); + lNode := FSelection[FSelectionCount]; //sync path note: deselect when click on another or on outside area - Exclude(FSelection[FSelectionCount].States, vsSelected); - if SyncCheckstateWithSelection[FSelection[FSelectionCount]] then - checkstate[FSelection[FSelectionCount]] := csUncheckedNormal; - DoRemoveFromSelection(FSelection[FSelectionCount]); + Exclude(lNode.States, vsSelected); + if SyncCheckstateWithSelection[lNode] then + CheckState[lNode] := csUncheckedNormal; + DoRemoveFromSelection(lNode); end; ResetRangeAnchor; FSelection := nil; From f7be8aae27f174f7520a0d4ba304b0a1418723f6 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 17 Mar 2020 21:21:12 +0100 Subject: [PATCH 049/681] Fixed issue #954: toAlwaysSelectNode can cause unintended scrolling --- Source/VirtualTrees.pas | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index eaf404485..2af67133a 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -21516,10 +21516,14 @@ procedure TBaseVirtualTree.EnsureNodeSelected(); begin if (SelectedCount = 0) and not SelectionLocked then begin - if Assigned(FNextNodeToSelect) then - Selected[FNextNodeToSelect] := True - else - Selected[GetFirstVisible] := True; + if not Assigned(FNextNodeToSelect) then + begin + FNextNodeToSelect := GetFirstVisible; + // Avoid selecting a disabled node, see #954 + while Assigned(FNextNodeToSelect) and IsDisabled[FNextNodeToSelect] do + FNextNodeToSelect := GetNextVisible(FNextNodeToSelect); + end; + Selected[FNextNodeToSelect] := True; Self.ScrollIntoView(Self.GetFirstSelected, False); end;// if nothing selected EnsureNodeFocused(); From 776ec5e3e37f5def4e21c040cbd49f0ca9f96312 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 24 Mar 2020 18:36:13 +0100 Subject: [PATCH 050/681] Implemented issue #956: Option toSyncCheckboxesWithSelection should automatically set CheckType to ctCheckBox --- Source/VirtualTrees.pas | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 2af67133a..bbe660a12 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1,4 +1,3 @@ - unit VirtualTrees; // The contents of this file are subject to the Mozilla Public License @@ -22857,7 +22856,9 @@ procedure TBaseVirtualTree.InitNode(Node: PVirtualNode); and (Parent <> FRoot) then SetCheckState(Node, Node.Parent.CheckState); - end; + end + else if (toSyncCheckboxesWithSelection in TreeOptions.SelectionOptions) then + Node.CheckType := TCheckType.ctCheckBox; if ivsDisabled in InitStates then Include(States, vsDisabled); From e95fddcc8e47de78385f5826bef4130c17e44f33 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 24 Mar 2020 18:44:13 +0100 Subject: [PATCH 051/681] Fixed issue #957: toSyncCheckboxesWithSelection: Unchecking one node deselects all --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index bbe660a12..dad916686 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -22539,7 +22539,7 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T NewCheckState := DetermineNextCheckState(HitInfo.HitNode.CheckType, HitInfo.HitNode.CheckState); if (ssLeft in KeysToShiftState(Message.Keys)) and DoChecking(HitInfo.HitNode, NewCheckState) then begin - if (Self.SelectedCount > 1) and (Selected[HitInfo.HitNode]) then + if (Self.SelectedCount > 1) and (Selected[HitInfo.HitNode]) and not (toSyncCheckboxesWithSelection in TreeOptions.SelectionOptions) then SetCheckStateForAll(NewCheckState, True) else DoCheckClick(HitInfo.HitNode, NewCheckState); From dcc972a0187df31cb7adcb72090015e15bf6f64f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 25 Mar 2020 18:28:50 +0100 Subject: [PATCH 052/681] Update VirtualTrees.pas Fixed timing problem with selection rectangle and option toSyncCheckboxesWithSelection. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index dad916686..26669f599 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -23417,7 +23417,7 @@ procedure TBaseVirtualTree.InternalRemoveFromSelection(Node: PVirtualNode); //sync path note: deselect when overlapping drawselection is made Exclude(Node.States, vsSelected); if SyncCheckstateWithSelection[Node] then - checkstate[Node] := csUncheckedNormal; + Node.CheckState := csUncheckedNormal; // Avoid using SetCheckState() as it handles toSyncCheckboxesWithSelection as well. Inc(PAnsiChar(FSelection[Index])); DoRemoveFromSelection(Node); AdviseChangeEvent(False, Node, crIgnore); From 4f5f513b7794ad27728a5c9951cc1ea94a78a72b Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 26 Mar 2020 17:34:07 +0100 Subject: [PATCH 053/681] toSyncCheckboxesWithSelection: Avoid using SetCheckState() in TBaseVirtualTree.RemoveFromSelection() as it handles toSyncCheckboxesWithSelection as well. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 26669f599..4f4d8f1df 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -24643,7 +24643,7 @@ procedure TBaseVirtualTree.RemoveFromSelection(Node: PVirtualNode); //sync path note: deselect when a ctrl click removes a selection Exclude(Node.States, vsSelected); if SyncCheckstateWithSelection[Node] then - checkstate[Node] := csUncheckedNormal; + Node.CheckState := csUncheckedNormal; // Avoid using SetCheckState() as it handles toSyncCheckboxesWithSelection as well. if FindNodeInSelection(Node, Index, -1, -1) and (Index < FSelectionCount - 1) then Move(FSelection[Index + 1], FSelection[Index], (FSelectionCount - Index - 1) * SizeOf(Pointer)); From d09b59b0c6bd91da5d2ee71a858e0b1771f89e33 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 28 Mar 2020 14:12:06 +0100 Subject: [PATCH 054/681] Added hints to the first two columns. Issue #958 --- Demos/Interfaces/modelviewform.dfm | 4337 ++++++++++++++-------------- Demos/Interfaces/myevents.pas | 2 + 2 files changed, 2170 insertions(+), 2169 deletions(-) diff --git a/Demos/Interfaces/modelviewform.dfm b/Demos/Interfaces/modelviewform.dfm index fbdf7fdff..ebf924390 100644 --- a/Demos/Interfaces/modelviewform.dfm +++ b/Demos/Interfaces/modelviewform.dfm @@ -1,2169 +1,2168 @@ -object FormModelView: TFormModelView - Left = 0 - Top = 0 - Caption = 'Charity Events' - ClientHeight = 532 - ClientWidth = 872 - Color = clBtnFace - Font.Charset = ANSI_CHARSET - Font.Color = clWindowText - Font.Height = -11 - Font.Name = 'Segoe UI' - Font.Style = [] - OldCreateOrder = False - Position = poScreenCenter - OnCreate = FormCreate - PixelsPerInch = 96 - TextHeight = 13 - object Label1: TLabel - Left = 520 - Top = 456 - Width = 313 - Height = 57 - AutoSize = False - Caption = - 'Click on a column header to sort. See design notes at the top of' + - ' source files.' - Font.Charset = ANSI_CHARSET - Font.Color = clWindowText - Font.Height = -13 - Font.Name = 'Segoe UI' - Font.Style = [] - ParentFont = False - WordWrap = True - end - object btDisplayStars: TButton - Left = 48 - Top = 480 - Width = 201 - Height = 25 - Caption = 'Display Star Events Only' - TabOrder = 0 - OnClick = btDisplayStarsClick - end - object btDisplayAll: TButton - Left = 288 - Top = 480 - Width = 201 - Height = 25 - Caption = 'Display All' - TabOrder = 1 - OnClick = btDisplayAllClick - end - object VST: TVirtualStringTree - Left = 0 - Top = 0 - Width = 872 - Height = 441 - Align = alTop - DefaultNodeHeight = 21 - Font.Charset = ANSI_CHARSET - Font.Color = clWindowText - Font.Height = -16 - Font.Name = 'Segoe UI' - Font.Style = [] - Header.AutoSizeIndex = 0 - Header.Font.Charset = DEFAULT_CHARSET - Header.Font.Color = clWindowText - Header.Font.Height = -11 - Header.Font.Name = 'Tahoma' - Header.Font.Style = [] - Header.MainColumn = -1 - Images = ImageList1 - ParentFont = False - TabOrder = 2 - Columns = <> - end - object ImageList1: TImageList - Height = 24 - Width = 24 - Left = 16 - Top = 376 - Bitmap = { - 494C01011B003000480018001800FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 - 000000000000360000002800000060000000A8000000010020000000000000FC - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000C4C4C4009996970099969700D1D1 - D100F9F6F500FBFBFC00DBC9C600B5B3B300CFCFCF00DAD9D900000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000CCCACA00B9B9B900DAD9D900DFDFDF00AFAFAF00B5B3 - B30099969700BA8E8500A18C8200AFAFAF00DAD9D900C7C7C700BFBFBF00D1D1 - D10000000000000000000000000000000000000000000000000000000000A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 74008C5D5C00000000000000000000000000000000000000000000000000A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 74008C5D5C000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000ECEAEA00B9B9B900CFCFCF00EFEFEF00EFEFEF00DAD9D900AFAFAF00B5B3 - B3008F8F8F004F4C4C0060606000606060008C89880099969700D1D1D100D1D1 - D100B9B9B900CCCACA000000000000000000000000000000000000000000A478 - 7400FED6C900FED6C900FED6C900FED6C900FED6C900FED6C900FED6C900FED6 - C900FECFC200FECFC200FECFC200FED0B700FED0B700FED0B700FED0B700FED0 - B7008C5D5C000000000000000000000000000000000000000000B5B3B300897D - 8B00CC9A9900CABCB700FEE4CA00FEE4CA00FEE4CA00FED6C900FEE4CA00FED6 - C900FEDDBB00FEDDBB00FEDDBB00FED0B700FED0B700FED0B700FED0B700FED0 - B7008C5D5C000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000BFBF - BF00C0C0C000ECEAEA00F3F3F300EFEFEF00ECEAEA00D1D1D100A8A8A800AFAF - AF008F8F8F004F4C4C00434242006060600060606000868585008F8F8F00AFAF - AF00C7C7C700DAD9D900BFBFBF00DAD9D900000000000000000000000000A478 - 7400F3E3D100FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD - BB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FECC9A00FED0 - B7008C5D5C000000000000000000000000000000000000000000458EB6005D71 - AB00AB8FA300C8ABAB00FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4 - CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FECC9A00FED0 - B7008C5D5C000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000B9B9B900DFDF - DF00FAFAFA00F9F6F500EFEFEF00ECEAEA00E1E0E000D1D1D100A8A8A800A8A8 - A800AFAFAF00AFAFAF00A8A8A8008C8988007070700060606000868585008F8F - 8F00B5B3B3009F9F9F00CFCFCF00F3F3F300000000000000000000000000A478 - 7400EFE4D800FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD - BB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FED0 - B7008C5D5C00000000000000000000000000000000000000000061C6FE003A80 - E0005D71AB00AB8FA300D5B3AF00FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4 - CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED0 - B7008C5D5C000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000B9B9B900FEFEFE00FBFB - FC00F9F6F500EFEFEF00ECEAEA00DAD9D900B5B3B3008F8F8F00808080008F8F - 8F00A8A8A800AFAFAF00AFAFAF00B5B3B300B9B9B900AFAFAF009F9F9F006060 - 600051515100CCCACA00E1E0E00000000000000000000000000000000000A478 - 7400F5EADF00FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4 - CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED0 - B7008C5D5C000000000000000000000000000000000000000000000000005DB2 - FC003A80E0005D71AB00AB8FA300D5B3AF00FEEBD700FEEBD700FEE4CA00FEE4 - CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED0 - B7008C5D5C000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000AFAFAF00FBFBFC00F9F6 - F500EFEFEF00DAD9D9009F9F9F009F9F9F00B9B9B900C7C7C7009F9F9F008F8E - 8D0080808000808080008F8F8F009F9F9F00B5B3B300B9B9B900BFBFBF00C3C2 - C200B9B9B900C4C4C400E1E0E00000000000000000000000000000000000B481 - 7600F5EADF00FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4 - CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED0 - B7008C5D5C000000000000000000000000000000000000000000000000008F8F - 8F005DB2FC003A80E0005D71AB00AB8FA300E2BDB300FEEBD700FEEBD700FEE4 - CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED0 - B7008C5D5C000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000A8A8A800F3F3F300CFCF - CF00999697009F9F9F00D1D1D100DAD9D900D1D1D100D1D1D100CCCACA00BFBF - BF00A8A8A800A8A8A8009F9F9F008F8F8F00868585008C8988009F9F9F00B9B9 - B9008DBC8B008DBC8B00E1E0E00000000000000000000000000000000000B481 - 7600F6EFE700FEF0E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4 - CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED0 - B7008C5D5C00000000000000000000000000000000000000000000000000B481 - 7600CABCB7005DB2FC003A80E0005D71AB00B9AAA600F3E3D100E2BDB300BF98 - 9500CDA6A000C8ABAB00E2BDB300FEDDBB00FEDDBB00FEDDBB00FED7AB00FECF - C2008C5D5C000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000009F9F9F008C8988009996 - 9700DAD9D900DFDFDF00DAD9D900D1D1D100CCCACA00ECEAEA00E6E5E400E6E5 - E400ECEAEA00DAD9D900CCCACA00B9B9B900AFAFAF00AFAFAF00999697008685 - 85008080800099969700DFDFDF0000000000000000000000000000000000B481 - 7600F6EFE700FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4 - CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FECF - C2008C5D5C00000000000000000000000000000000000000000000000000B481 - 7600F8F3EC00FEF4E9005DB2FC0085AFCB008F8F8F00BF989500DAB4A000FDE8 - B700FEFED300FDF6C600D8C5B500CC9A9900E5CDBE00FEDDBB00FEDDBB00FED0 - B7008C5D5C000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000008F8F8F00ECEAEA00E1E0 - E000DFDFDF00DAD9D900D1D1D100CFCFCF00E1E0E000DAD9D900B5B3B300B9B9 - B900B9B9B900BFBFBF00CFCFCF00E6E5E400E6E5E400D1D1D100C7C7C700BFBF - BF00B9B9B900A8A8A800DAD9D90000000000000000000000000000000000BA8E - 8500F6EFE700FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEEB - D700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FECF - C200986B6600000000000000000000000000000000000000000000000000B481 - 7600F6EFE700FEF4E900F8F3EC00DED7D300BF989500FED7AB00FDF6C600FEFE - D300FEFFE100FEFFE100FFFDF800F6EFE700BF989500EDD9B700FEDDBB00FECF - C200986B66000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000008C898800E1E0E000DFDF - DF00DAD9D900D1D1D100CFCFCF00DAD9D900E6E5E400C0C0C000EFEFEF00EFEF - EF00ECEAEA00CFCFCF00BFBFBF00B9B9B900B9B9B900BFBFBF00D1D1D100E1E0 - E000DFDFDF00D1D1D100E6E5E40000000000000000000000000000000000BA8E - 8500F8F3EC00FFF8EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700FEEB - D700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FECF - C200986B6600000000000000000000000000000000000000000000000000BA8E - 8500F8F3EC00FFF8EE00FFF8EE00D5B3AF00EAC8A100FDF6C600FDE8B700FEFE - D300FEFFE100FEF9F300FEFFFC00FFFFFF00DED2CA00CDA6A000FEDDBB00FED6 - C900986B66000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000D1D1D100DAD9 - D900D1D1D100CFCFCF00DFDFDF00CFCFCF00C3C2C200F3F3F300F3F3F300EFEF - EF00EFEFEF00EFEFEF00ECEAEA00ECEAEA00DAD9D900C7C7C700B9B9B900B9B9 - B900B9B9B900CCCACA000000000000000000000000000000000000000000BA8E - 8500F8F3EC00FEF9F300FFF8EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEB - D700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FECF - C200986B6600000000000000000000000000000000000000000000000000BA8E - 8500FAF3F200FFF8EE00FEF9F300C8ABAB00FDE8B700FDE8B700FDF6C600FEFE - D300FEFFE100FFFDF800FEFFFC00FFF8EE00FDF6C600BF989500FEDDBB00FED6 - C900986B66000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000E6E5 - E400AFAFAF00BFBFBF00C3C2C200D1D1D100C4C4C400CCCACA00E6E5E400F3F3 - F300EFEFEF00EFEFEF00EFEFEF00ECEAEA00ECEAEA00E6E5E400E6E5E400D1D1 - D100AFAFAF00E6E5E4000000000000000000000000000000000000000000CB9A - 8200F9F6F500FEFAF700FFF8EE00FFF8EE00FEF4E900FEF4E900FEF0E200FEF0 - E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FED6 - C900986B6600000000000000000000000000000000000000000000000000CB9A - 8200F9F6F500FEF9F300FFFDF800CDA6A000FDF6C600FED7AB00FDE8B700FEFE - D300FEFED300FEFFE100FEFFE100FEFFE100FEFED300BF989500FEE4CA00FED6 - C900986B66000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000A8A8A800E6E5E400E6E5E400AFAFAF00B5B3B300B9B9B900B9B9 - B900C3C2C200CCCACA00DAD9D900E6E5E400E6E5E400ECEAEA00CCCACA00B5B3 - B300DFDFDF00000000000000000000000000000000000000000000000000CB9A - 8200F8F8F800FFFDF800FEF9F300FFF8EE00FFF8EE00FEF4E900FEF4E900FEF0 - E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FED6 - C900A4787400000000000000000000000000000000000000000000000000CB9A - 8200F8F8F800FFFCFB00FEFFFC00CDA6A000FDF6C600FDE8B700FDE8B700FEFE - D300FEFED300FEFED300FEFFE100FEFED300FDF6C600BF989500FEE4CA00FED6 - C900A47874000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000A8A8A800FEF9F300FEF0E200ECDACC00D1D1D100C4C4C400C4C4 - C400B9B9B900C0C0C000BFBFBF00B9AAA600B9AAA600B5B3B300DAD9D9000000 - 000000000000000000000000000000000000000000000000000000000000CB9A - 8200FBFBFC00FFFEFE00FFFDF800FEF9F300FFF8EE00FFF8EE00FEF4E900FEF4 - E900FEF0E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FED6 - C900A4787400000000000000000000000000000000000000000000000000CB9A - 8200FCFEFB00FFFEFE00FFFFFF00D5B3AF00EDD9B700FEF0E200FDE8B700FED7 - AB00FDF6C600FDF6C600FDF6C600FDF6C600EDD9B700BF989500FEE4CA00FED6 - C900A47874000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000CB9A8200FED7AB00FED7AB00FED7AB00FEDDBB00FEDD - BB00FEEBD700F3E3D100EFE4D800C8ABAB00DAD9D90000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000DCA8 - 8700F8FBFB00FFFFFF00FFFEFE00FFFCFB00FEFAF700FFF8EE00FFF8EE00FEF4 - E900FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FECF - C200A4787400000000000000000000000000000000000000000000000000DCA8 - 8700FBFBFC00FFFEFF00FFFEFF00DED7D300E2BDB300F9F6F500F9FFFE00FDE8 - B700FDE8B700FED7AB00FDE8B700FDE8B700D6A89400D8C5B500FEE4CA00FED0 - B700A47874000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000D6A89400FEDDBB00FED7AB00FEDDBB00FED7AB00FEDD - BB00FED7AB00FEDDBB00F3D1A300BF9895000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000DCA8 - 8700F8FBFB00FFFFFF00FFFFFE00FFFEFF00FEFAF700FEFAF700FEF9F300FFF8 - EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700FED6C900FECFC200F3B9 - B500A4787400000000000000000000000000000000000000000000000000DCA8 - 8700FBFBFC00FFFFFF00FFFFFF00FFFEFF00D5B3AF00D8C5B500EFE4D800F5EA - DF00FDE8B700FDE8B700EDD9B700D6A89400CDA6A000FED6C900FECFC200F3B9 - B500A47874000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000E6E5E400FED0B700FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDD - BB00FEDDBB00FEDDBB00D5B3AF00BF9895000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000DCA8 - 8700FFFDF800FFFFFF00FFFFFE00FFFFFE00FFFEFE00FEFAF700FEFAF700FFF8 - EE00FFF8EE00FEF4E900FEF4E900FEF0E200FED6C900F3B9B500FCA3A200FCA3 - A200B4817600000000000000000000000000000000000000000000000000DCA8 - 8700FCFEFB00FFFFFE00FEFFFC00FFFEFF00FFFFFF00D6BCBB00CDA6A000DAB4 - A000C8ABAB00DAB4A000CDA6A000E2BDB300FED6C900F3B9B500FCA3A200FCA3 - A200B48176000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000D8C5B500FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4 - CA00FEE4CA00FEE4CA00BF989500EADBD4000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000E3B1 - 8E00FCFEFE00FFFFFF00FFFEFF00FFFFFF00FFFFFF00FEFFFC00FEFAF700FEFA - F700FFF8EE00FFF8EE00FEF4E900B4817600B4817600B4817600A4787400B481 - 7600B4817600000000000000000000000000000000000000000000000000E3B1 - 8E00FCFEFE00FFFFFF00FCFEFE00FFFFFF00FFFFFF00FFFFFF00FFFFFF00EADB - D400EFE4D800F1E3E200FFF8EE00B4817600B4817600B4817600A4787400B481 - 7600B48176000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000BF989500FEEBD700FEEBD700FEEBD700FEEBD700FEEBD700FEEB - D700FEEBD700ECDACC00BF989500000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000E3B1 - 8E00FCFEFE00FFFEFE00FCFEFE00FFFFFF00FFFFFE00FEFEFE00FFFFFF00FDFA - FA00FEFAF700FFF8EE00FFF8EE00B4817600E3B18E00FCB04C00FB9B0F00D393 - 6400D6A89400000000000000000000000000000000000000000000000000E3B1 - 8E00FCFEFE00FFFFFF00FFFEFF00FFFFFF00FFFFFE00FFFFFF00FFFFFE00FFFE - FF00FFFEFE00FEFAF700FEF9F300B4817600E3B18E00FCB04C00FB9B0F00D393 - 6400D6A894000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000ECEAEA00DBC9C600FEF0E200FEF0E200FEF0E200FEF0E200FEF0E200FEF0 - E200FEF0E200D5B3AF00BF989500000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200FFFEFE00FFFEFF00FEFEFE00FFFFFF00FFFFFE00FFFEFF00FCFEFE00FFFF - FF00FEFAF700FEFAF700FFF8EE00B4817600EAC8A100FCC47C00DCA88700D6A8 - 940000000000000000000000000000000000000000000000000000000000EDBD - 9200FEFEFE00FFFFFF00FFFFFE00FFFFFF00FFFFFF00FFFFFF00FFFEFF00FCFE - FE00FEFAF700FEF9F300FEF9F300B4817600EAC8A100FCC47C00DCA88700D6A8 - 9400000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000D6BCBB00FEF4E900FFF8EE00FFF8EE00FFF8EE00FFF8EE00FFF8EE00FFF8 - EE00FEF4E900BF989500E4D1CD00000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200FCFEFE00FFFFFE00FFFFFE00FFFFFF00FFFEFF00FCFEFE00FFFFFF00FFFF - FF00FEFEFE00FEFAF700FEFAF700B4817600EAC8A100E3B18E00D6A894000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200FFFFFF00FFFFFF00FFFFFE00FFFEFE00FFFEFF00FFFFFF00FFFFFE00FFFF - FF00FEFFFC00FFFDF800FEFAF700B4817600EAC8A100E3B18E00D6A894000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000F8F0 - F000DED7D300FEFEFE00FBFBFC00FCFEFB00FCFEFB00FBFBFC00FCFEFB00FCFE - FB00ECEAEA00BF98950000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200FEF0E200FEF0E200F5EADF00F5EADF00EFE4D800EFE4D800EADBD400EADB - D400DED7D300DED2CA00DED2CA00B4817600DCA88700E3B18E00000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200FEF0E200FEF0E200F5EADF00F5EADF00EFE4D800EFE4D800EADBD400EADB - D400EADBD400DED2CA00DED2CA00B4817600DCA88700E3B18E00000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000CABC - B700BF989500BF989500BF989500BF989500BF989500BF989500BF989500BF98 - 9500BF989500F8F0F00000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 - 8700DCA88700DCA88700DCA88700B4817600DCA8870000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 - 8700DCA88700DCA88700DCA88700B4817600DCA8870000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000D1EF - F900F0FEFE000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000059ACD00059A - CD00BEE6F500F0F6FD0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000A3453F00A345 - 3F00994C4D00A3453F00994C4D00B5B3B300B5B3B300B5B3B300B5B3B300B5B3 - B300B5B3B300B5B3B300B5B3B300B5B3B300994C4D00994C4D00994C4D00994C - 4D00994C4D00DAD9D900000000000000000000000000000000000000000041B3 - DA0064C1E10037B4E00064C1E1009EDEEE00DFEFF90000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000059ACD00059ACD00059ACD004BBDE00090D1F100D1EFF9000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000059ACD00C7EF - F10037B4E0001DA6D500059ACD007ECBE600ACDBF400E4FDFE00000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000B4817600994C4D00CB66 - 6600CB666600CB666600994C4D00A8A8A800C4C4C400DAD9D900F8F8F800FAFA - FA00F3F3F300E6E5E400DFDFDF00DAD9D900993434009934340099343400BB57 - 5700CB666600994C4D000000000000000000000000000000000000000000059A - CD00F9FFFE0070D2FD0056C6F0004BBDE0002EACD8002EACD80064C1E1009EDE - EE00D1EFF9000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000059ACD00D1EFF90060D4F0004BBDE0002EACD800059ACD00059A - CD0041B3DA007ECBE600BEE6F500F0FEFE000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000059ACD0090D1 - F1009AE3FF007BDDFE0070D2FD0056C6F00037B4E0001DA6D500059ACD004BBD - E0009EDEEE00DFEFF90000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000B4817600CB666600CB66 - 6600CB666600CB666600994C4D00A4968E00B4817600CC9A9900DBC9C600FAFA - FA00F8F8F800EFEFEF00E6E5E400DFDFDF00993434009934340099343400BB57 - 5700CB666600994C4D00000000000000000000000000A4787400A4787400A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A478740037B4 - E00037B4E0002EACD8004BBDE00090D1F100D1EFF90000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000059ACD0090D1F1009AE3FF007BDDFE007BDDFE007BDDFE0070D2 - FD0056C6F00037B4E0001DA6D500059ACD002EACD8007ECBE600ACDBF400DFEF - F900000000000000000000000000000000000000000000000000059ACD004BBD - E000D1EFF9007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE0060D4 - F0004BBDE0002EACD800059ACD001DA6D5009EDEEE0000000000000000000000 - 00000000000000000000000000000000000000000000B4817600CB666600CB66 - 6600CB666600CB666600994C4D00B1A09700BB575700BB575700CC9A9900DFDF - DF00FAFAFA00F8FBFB00EFEFEF00E6E5E400993434009934340099343400BB57 - 5700CB666600994C4D00000000000000000000000000A4787400F3E3D100FEE4 - CA00FED6C900FED6C900FED0B700FED0B700FED0B700FED7AB00A478740084E9 - FE0084E9FE007BDDFE007BDDFE0060D4F0004BBDE0001DA6D50041B3DA00ACDB - F4000000000000000000000000000000000000000000059ACD00059ACD007ECB - E600BEE6F500059ACD004BBDE000D1EFF90084E9FE0084E9FE0084E9FE0084E9 - FE0084E9FE0084E9FE0084E9FE007BDDFE0060D4F0004BBDE0001DA6D50041B3 - DA00000000000000000000000000000000000000000000000000059ACD0037B4 - E000D1EFF90084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9 - FE0084E9FE0084E9FE0084E9FE0060D4F00037B4E00000000000000000000000 - 00000000000000000000000000000000000000000000B4817600CB666600CB66 - 6600CB666600CB666600994C4D00D5B3AF00BB575700BB575700B4817600C4C4 - C400DFDFDF00FEFAF700FBFBFC00EFEFEF00993434009934340099343400BB57 - 5700CB666600994C4D00000000000000000000000000B4817600FEEBD700FEE4 - CA00FEE4CA00FEDDBB00C7D19500448A3E0005710A0005710A000F8319000571 - 0A002A8C430060D4F00084E9FE0084E9FE007BDDFE0084E9FE007BDDFE0041B3 - DA00DFEFF90000000000000000000000000000000000059ACD009EDEEE004BBD - E0001DA6D500059ACD0037B4E000DFEFF90084E9FE0084E9FE0084E9FE0084E9 - FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0070D2FD004BBD - E000D1EFF9000000000000000000000000000000000000000000059ACD0056C6 - F00090D1F100ADFBFE0084E9FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3 - FE008DF3FE008DF3FE008DF3FE007BDDFE007BDDFE00059ACD00000000000000 - 00000000000000000000000000000000000000000000B4817600CB666600CB66 - 6600CB666600CB666600994C4D00DBC9C600A3453F00A3453F00A4787400A8A8 - A800C4C4C400DFDFDF00FAFAFA00FAFAFA00993434009934340099343400BB57 - 5700CB666600994C4D00000000000000000000000000BA8E8500FEEBD700FEEB - D700FEE4CA00FEE4CA00448A3E007DA972007DA97200448A3E000F8319001899 - 2E0025A63D000F83190069CCC0008DF3FE008DF3FE008DF3FE0084E9FE0060D4 - F00064C1E10000000000000000000000000000000000059ACD0064C1E100BEE6 - F5007BDDFE00059ACD004BBDE00090D1F100ADFBFE008DF3FE008DF3FE008DF3 - FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE0070D2FD008DF3 - FE0064C1E10000000000000000000000000000000000000000001DA6D50061C6 - FE0064C1E100C2FDFF0099FDFE0099FDFE008DF3FE0099FDFE008DF3FE0099FD - FE008DF3FE0099FDFE008DF3FE007BDDFE0084E9FE004BBDE000000000000000 - 00000000000000000000000000000000000000000000B4817600CB666600CB66 - 6600CB666600CB666600994C4D00DBC9C60099343400A3453F00B48176009F9F - 9F00A8A8A800C3C2C200DFDFDF00FAFAFA00993434009934340099343400BB57 - 5700CB666600994C4D00000000000000000000000000CB9A8200FEF4E900FEEB - D700FEEBD700FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00B4817600255E - 280025BB4F0030C369000F8319007ECBE6008DF3FE008DF3FE0084E9FE0084E9 - FE0041B3DA0000000000000000000000000000000000059ACD0037B4E000D1EF - F90060D4F000059ACD0070D2FD004BBDE000D4FDFF0099FDFE0099FDFE0099FD - FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0070D2FD008DF3 - FE004BBDE000DFEFF900000000000000000000000000000000001DA6D50070D2 - FD002EACD800E4FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD - FE0099FDFE0099FDFE0099FDFE0084E9FE009AE3FF00ADFBFE00059ACD000000 - 00000000000000000000000000000000000000000000B4817600CB666600CB66 - 6600CB666600CB666600994C4D00DBC9C600E4D1CD00DBC9C600E4D1CD00CCCA - CA00B9B9B900CABCB700C7C7C700DED7D300993434009934340099343400BB57 - 5700CB666600994C4D00000000000000000000000000CB9A8200FEF4E900FEF0 - E200FEF0E200FEEBD700FEE4CA00FEE4CA00FEDDBB00FEDDBB00B481760084E9 - FE000F83190047CE710025BB4F002A8C430099FDFE0099FDFE0084E9FE00ADFB - FE0064C1E10090D1F100000000000000000000000000059ACD0056C6F00090D1 - F1009EDEEE00059ACD0070D2FD0037B4E000E4FDFE0099FDFE0099FDFE0099FD - FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE007BDDFE00BEE6 - F500ADFBFE007ECBE600000000000000000000000000000000001DA6D50070D2 - FD002EACD8009EDEEE00D4FDFF00ADFBFE00ADFBFE00ADFBFE00ADFBFE00ADFB - FE00ADFBFE00ADFBFE00ADFBFE0084E9FE009AE3FF00D4FDFF004BBDE0000000 - 00000000000000000000000000000000000000000000B4817600CB666600CB66 - 6600CB666600CB666600CB666600B4817600B4817600B4817600B4817600CB66 - 6600CB666600986B6600CB666600B4817600BB575700BB575700A3453F00CB66 - 6600CB666600994C4D00000000000000000000000000DCA88700FEF9F300FEF4 - E900FEF0E200FEEBD700FEEBD700FEE4CA00FEE4CA00FEDDBB00B481760084E9 - FE000F83190047CE710058E087000F83190099FDFE0099FDFE008DF3FE00ADFB - FE009AE3FF004BBDE000000000000000000000000000059ACD0061C6FE002EAC - D800BEE6F5001DA6D50070D2FD0056C6F00064C1E100DFEFF900E4FDFE00E4FD - FE00D4FDFF00C2FDFF00ADFBFE00C2FDFF00ADFBFE00ADFBFE0070D2FD00C2FD - FF00D4FDFF004BBDE000F0F6FD000000000000000000000000001DA6D5007BDD - FE007BDDFE0037B4E0004BBDE0007ECBE600ACDBF400D1EFF900E4FDFE00C2FD - FF00C2FDFF00C2FDFF00C2FDFF009AE3FF009AE3FF00E4FDFE00ACDBF400059A - CD000000000000000000000000000000000000000000B4817600CB666600CB66 - 6600CB666600CB666600CB666600CB666600CB666600CB666600CB666600CB66 - 6600CB666600CB666600CB666600CB666600CB666600CB666600CB666600CB66 - 6600CB666600994C4D00000000000000000000000000E3B18E00FFFDF800FFF8 - EE00FEF4E900FEF0E200FEEBD700FEEBD700FEE4CA000F8319000F8319000F83 - 19000F83190058E0870058E087000F8319000F8319000F8319000F831900ADFB - FE00D4FDFF004BBDE000BEE6F5000000000000000000059ACD0070D2FD0037B4 - E0007ECBE6001DA6D5007BDDFE007BDDFE0060D4F0004BBDE0002EACD8002EAC - D8002EACD800D1EFF900E4FDFE00C2FDFF00C2FDFF00D4FDFF007BDDFE00D1EF - F900E4FDFE00BEE6F5009EDEEE000000000000000000000000002EACD80084E9 - FE0084E9FE007BDDFE0084E9FE004BBDE00037B4E00037B4E00041B3DA00F2FA - F500D4FDFF00D4FDFF00D4FDFF009AE3FF009EDEEE000F8319008DBC8B0064C1 - E1000000000000000000000000000000000000000000B4817600CB666600A345 - 3F00BA8E8500BF989500CC9A9900BF989500CC9A9900CC9A9900CC9A9900CC9A - 9900CC9A9900CC9A9900CC9A9900CC9A9900CC9A9900CC9A9900A4787400CB66 - 6600CB666600994C4D00000000000000000000000000EDBD9200FFFFFF00FEFA - F700FEF9F300FEF4E900FEF0E200FEEBD700FEEBD700F0E4C700267D270025A6 - 3D0047CE710058E0870058E0870058E0870025A63D00267D27009EDEEE00C2FD - FF00D4FDFF009EDEEE0041B3DA0000000000000000001DA6D5007BDDFE0061C6 - FE002EACD8001DA6D50084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9 - FE0084E9FE004BBDE000BEE6F500F0FEFE00F0FEFE00F0FEFE009AE3FF00E4FD - FE00F9FFFE00F0FEFE0064C1E1000000000000000000000000002EACD8008DF3 - FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE0071EBF9001DA6 - D50041B3DA004BBDE0004BBDE0004BBDE0002A8C430047CE71000F83190051B4 - 9400F0F6FD0000000000000000000000000000000000B4817600CB666600A345 - 3F00F3F3F300F3F3F300F3F3F300F3F3F300FAF3F200F3F3F300F3F3F300F3F3 - F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 - 5700CB666600994C4D00000000000000000000000000EDBD9200FFFFFF00FFFF - FE00FDFAFA00FEF9F300FEF4E900FEF0E200FEEBD700F3B9B500B48176000571 - 0A0047CE710058E0870058E0870058E087000F831900C7EFF100ADFBFE00D1EF - F900F0FEFE00D4FDFF0041B3DA00F0F6FD00000000001DA6D5007BDDFE0061C6 - FE0037B4E0001DA6D50099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD - FE0099FDFE0099FDFE0060D4F0004BBDE0004BBDE0004BBDE0004BBDE0004BBD - E0004BBDE0004BBDE0004BBDE0000000000000000000000000002EACD80099FD - FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD - FE0099FDFE0071EBF90060D4F000097B450025BB4F0058E0870058E087001899 - 2E00DDEADD0000000000000000000000000000000000B4817600CB666600A345 - 3F00FAF3F200F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 - F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 - 5700CB666600994C4D00000000000000000000000000EDBD9200FFFFFF00FFFE - FF00FEFEFE00FFFDF800FEF9F300FEF4E900B4817600B4817600B4817600B1A0 - 97000F83190058E0870058E08700267D2700F0FEFE00F0FEFE00D1EFF900E4FD - FE00FCFEFE00FCFEFE007ECBE60041B3DA00000000001DA6D5007BDDFE007BDD - FE0037B4E0001DA6D50099FDFE0099FDFE0099FDFE0099FDFE0099FDFE00ADFB - FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE002EACD8000000 - 000000000000000000000000000000000000000000000000000037B4E000ADFB - FE0099FDFE0099FDFE0099FDFE0099FDFE00D4FDFF00C2FDFF00C2FDFF00ADFB - FE0099FDFE0099FDFE0049AA490018992E0058E0870058E0870058E0870058E0 - 8700267D270000000000000000000000000000000000B4817600CB666600A345 - 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 - F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 - 5700CB666600994C4D00000000000000000000000000EDBD9200FCFEFB00FCFE - FE00FEFFFC00FCFEFE00FEFAF700FFF8EE00B4817600FCC47C00DCA88700FED6 - C9008DBC8B00267D27000F83190041B3DA0041B3DA0041B3DA0041B3DA0041B3 - DA0041B3DA0041B3DA0041B3DA0041B3DA00000000001DA6D5009AE3FF0084E9 - FE007BDDFE001DA6D500C2FDFF0099FDFE0099FDFE0099FDFE00C2FDFF009EDE - EE009EDEEE00ACDBF400C2FDFF00D4FDFF00C2FDFF00ADFBFE002EACD8000000 - 000000000000000000000000000000000000000000000000000037B4E000E4FD - FE00ADFBFE00ADFBFE0099FDFE00D4FDFF007ECBE60037B4E00037B4E00037B4 - E00037B4E0008DBC8B000F83190047CE710058E0870058E0870058E0870058E0 - 870058E0870068A76200000000000000000000000000B4817600CB666600A345 - 3F00F8F0F000F3F3F300CCCACA00C0C0C000C3C2C200C3C2C200C4C4C400C4C4 - C400C4C4C400C7C7C700C7C7C700C7C7C700D1D1D100F3F3F300D6BCBB00BB57 - 5700CB666600994C4D00000000000000000000000000DCA88700FFFEFE00FDFA - FA00F8F8F800F3F3F300F3F3F300ECEAEA00B4817600D6A89400F3E3D100FEF0 - E200FEF0E2007DA972008DBC8B00FECFC200B481760099FDFE0041B3DA000000 - 000000000000000000000000000000000000000000002EACD8008DF3FE008DF3 - FE008DF3FE001DA6D500DFEFF900E4FDFE00D4FDFF00D4FDFF00C7EFF1004BBD - E00060D4F00060D4F00037B4E0002EACD8002EACD8002EACD8002EACD8000000 - 000000000000000000000000000000000000000000000000000037B4E00064C1 - E10090D1F1009EDEEE00BEE6F50090D1F10037B4E00000000000000000000000 - 0000000000008DBC8B007DA97200448A3E0025BB4F0058E0870058E087000F83 - 190068A762007DA97200F3F3F3000000000000000000B4817600CB666600A345 - 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 - F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 - 5700CB666600994C4D00000000000000000000000000DCA88700DCA88700DCA8 - 8700DCA88700DCA88700DCA88700DCA88700DCA88700F5EADF00FFF8EE00FEF4 - E900FEF0E200FEEBD700FECFC200F3B9B500B481760099FDFE0041B3DA000000 - 000000000000000000000000000000000000000000001DA6D500ADFBFE0099FD - FE0099FDFE008DF3FE0041B3DA0037B4E00037B4E00037B4E00037B4E00099FD - FE0099FDFE0099FDFE0071EBF9002EACD8000000000000000000000000000000 - 00000000000000000000000000000000000000000000000000000000000037B4 - E00037B4E00037B4E00037B4E00037B4E0000000000000000000000000000000 - 00000000000000000000000000000000000025A63D0047CE710058E087000F83 - 19000000000000000000000000000000000000000000B4817600CB666600A345 - 3F00F3F3F300F3F3F300CCCACA00C0C0C000C0C0C000C0C0C000C3C2C200C3C2 - C200C4C4C400C4C4C400C4C4C400C7C7C700D1D1D100F3F3F300D6BCBB00BB57 - 5700CB666600994C4D00000000000000000000000000000000000000000041B3 - DA00D4FDFF00D4FDFF00EDBD9200FEFAF700FEFAF700FDFAFA00FEFAF700FEF9 - F300FEF4E900B4817600B4817600B4817600B481760041B3DA0041B3DA000000 - 000000000000000000000000000000000000000000002EACD800C2FDFF0099FD - FE0099FDFE0099FDFE0099FDFE00C2FDFF00C2FDFF00C2FDFF00ADFBFE0099FD - FE0099FDFE0099FDFE0071EBF9002EACD8000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000D0E2CE0025A63D0047CE710030C369008DBC - 8B000000000000000000000000000000000000000000B4817600CB666600A345 - 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 - F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 - 5700CB666600994C4D000000000000000000000000000000000000000000ACDB - F40041B3DA0037B4E000EDBD9200FFFEFF00FFFEFF00FFFFFE00FFFFFF00FEF9 - F300FEF9F300B4817600FCB04C00D3936400D8C5B50000000000000000000000 - 000000000000000000000000000000000000000000001DA6D500E4FDFE00ADFB - FE0099FDFE0099FDFE00C2FDFF009EDEEE002EACD8002EACD8002EACD8002EAC - D8002EACD8002EACD8002EACD8002EACD8000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000448A3E0025BB4F0047CE71000F831900EFF9 - F0000000000000000000000000000000000000000000B4817600CB666600A345 - 3F00F8F0F000F3F3F300C7C7C700BFBFBF00C0C0C000BFBFBF00C0C0C000C0C0 - C000C3C2C200C4C4C400C3C2C200C4C4C400CFCFCF00F3F3F300D6BCBB00BB57 - 5700CB666600994C4D0000000000000000000000000000000000000000000000 - 00000000000000000000EDBD9200FFFFFF00FCFEFB00F8FBFB00FAFAFA00F8F8 - F800F8F0F000B4817600EDBD9200D8C5B5000000000000000000000000000000 - 000000000000000000000000000000000000000000002EACD8007ECBE6007ECB - E6009EDEEE00BEE6F500ACDBF40037B4E0000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000E1F1E10068A7620018992E0030C369000F831900DDEADD000000 - 00000000000000000000000000000000000000000000B4817600CB666600A345 - 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 - F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 - 5700CB666600994C4D0000000000000000000000000000000000000000000000 - 00000000000000000000DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 - 8700DCA88700DCA88700DCA88700000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000037B4E00037B4 - E00037B4E00037B4E00041B3DA00F0FEFE000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000AFD0B00005710A00448A - 3E0005710A000F8319000EA31B0018992E00267D2700DDEADD00000000000000 - 00000000000000000000000000000000000000000000B4817600CB666600A345 - 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 - F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 - 5700CB666600994C4D0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000F2FAF500AFD0 - B00068A762007DA972007DA97200D0E2CE000000000000000000000000000000 - 00000000000000000000000000000000000000000000B4817600994C4D009934 - 3400C4C4C400C4C4C400C3C2C200C3C2C200C4C4C400C4C4C400C4C4C400C4C4 - C400C4C4C400C3C2C200C4C4C400C4C4C400C4C4C400C3C2C200BF989500994C - 4D00994C4D00DAD9D90000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000EFEFEF00C0C0C0009F9F - 9F009F9F9F00A8A8A8008F8F8F00B9B9B900E6E5E40000000000000000000000 - 0000000000000000000000000000000000000000000000000000CC9A9900CC9A - 9900CC9A9900D5B3AF00EADBD400F8F0F0000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000059ACD00059ACD0090D1F100BEE6F5000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000059ACD0064C1 - E1009EDEEE00DFEFF90000000000000000000000000000000000000000000000 - 0000000000000000000000000000AFD0B0000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000EFEFEF009F9F9F008C8988009F9F9F00B9AA - A600C8ABAB00CABCB700D6BCBB00D5B3AF00B1A0970086858500D1D1D1000000 - 0000000000000000000000000000000000000000000000000000CC9A9900CC9A - 9900D6A89400D6A89400D6A89400CC9A9900CC9A9900CDA6A000E4D1CD00F6EF - E700000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000059ACD00C7EFF10037B4E000059ACD00059ACD0041B3DA007ECBE600ACDB - F400F0F6FD000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000059ACD00DFEF - F90056C6F0002EACD8002EACD80064C1E10090D1F100D1EFF900000000000000 - 000000000000000000000000000005710A0068A7620000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000D1D1D1008C89880099969700999697008F8E8D008F8E - 8D00A4968E00A18C8200A4968E008C898800C8ABAB00FECFC200BF989500A8A8 - A800000000000000000000000000000000000000000000000000CC9A9900CC9A - 9900CC9A9900FECC9A00FECC9A00FECC9A00EDBD9200E3B18E00D6A89400CC9A - 9900CC9A9900CC9A9900DBC9C600F1E3E2000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000059ACD007ECBE6009AE3FF007BDDFE0070D2FD0056C6F00037B4E0001DA6 - D500059ACD002EACD8007ECBE6009EDEEE00DFEFF90000000000000000000000 - 0000000000000000000000000000000000000000000000000000059ACD0090D1 - F1009AE3FF007BDDFE0070D2FD0060D4F00056C6F00037B4E0001DA6D5004BBD - E00090D1F100C7EFF100D1EFF9000F8319000F831900448A3E00EFF9F0000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000A1C79E00879185009F9F9F008C898800B1A09700EDD9B700FDE8 - B700CFCFCF006682F100FDF6C600FDE8B700D8C5B50086858500E2BDB300E2BD - B3008C8988000000000000000000000000000000000000000000CC9A9900EDBD - 9200D6A89400D6A89400FED7AB00FED7AB00F3D1A300FECC9A00FECC9A00FECC - 9A00EDBD9200EDBD9200D6A89400CC9A9900CC9A9900CC9A9900D6BCBB00F1E3 - E200000000000000000000000000000000000000000000000000000000000000 - 0000059ACD0037B4E000D1EFF9007BDDFE007BDDFE007BDDFE007BDDFE007BDD - FE0051B4940056C6F00037B4E0001DA6D500059ACD001DA6D500BEE6F5000000 - 0000000000000000000000000000000000000000000000000000059ACD0064C1 - E100D1EFF9007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE0070D2 - FD0060D4F00037B4E00037B4E0000F8319000EA31B000F831900267D2700D0E2 - CE00000000000000000000000000000000000000000000000000000000000000 - 0000AFD0B0000F831900267D270087918500E3D3AA00FDE8B700FDF6C600FEF9 - F300BEC6DC004571FA00FFFDF800FEFFFC00FFF8EE00FDF6C600A4968E00D5B3 - AF00E2BDB300A8A8A80000000000000000000000000000000000CC9A9900EAC8 - A100F3D1A300CC9A9900DAB4A000FDE8B700FDE8B700FED7AB00FED7AB00FED7 - AB00F3D1A300FECC9A00FECC9A00FECC9A00FECC9A00EDBD9200D6A89400D6A8 - 9400CC9A9900CC9A990000000000000000000000000000000000000000000000 - 0000059ACD004BBDE000D1EFF90084E9FE0084E9FE0084E9FE0084E9FE0084E9 - FE002A8C430005710A0069CCC00084E9FE0084E9FE0060D4F000059ACD000000 - 0000000000000000000000000000000000000000000000000000059ACD004BBD - E000F0F6FD007BDDFE0084E9FE007BDDFE002A8C43000F8319000F8319000F83 - 19000F8319000F8319000F8319000F83190016B432000EA31B000EA31B000571 - 0A00A1C79E00000000000000000000000000000000000000000000000000D0E2 - CE00267D270058E0870030C36900448A3E00FDE8B700FDF6C600FDE8B700FED7 - AB00FED7AB00FED7AB00FDE8B700FEEBD700FFFDF800FEFFFC00FDF6C6008F8E - 8D00E2BDB300AB8FA300DAD9D900000000000000000000000000CC9A9900F3D1 - A300FDE8B700EAC8A100CC9A9900EDD9B700FDF6C600FDF6C600FDE8B700FDE8 - B700FDE8B700FDE8B700FED7AB00FED7AB00FECC9A00F3D1A300FECC9A00FECC - 9A00D6A89400CC9A990000000000000000000000000000000000000000000000 - 0000059ACD0061C6FE0090D1F100ADFBFE008DF3FE008DF3FE008DF3FE008DF3 - FE0051B4940025A63D0018992E0051B494008DF3FE0084E9FE00059ACD00D1EF - F900000000000000000000000000000000000000000000000000059ACD0056C6 - F0009EDEEE00ADFBFE0084E9FE0084E9FE002A8C430058E0870047CE710047CE - 710047CE710030C3690030C3690025BB4F0016B4320016B432000EA31B000EA3 - 1B0005710A007DA9720000000000000000000000000000000000F2FAF5001899 - 2E0030C3690058E0870058E0870025A63D007DA97200FDE8B700FDE8B700FDE8 - B700FDE8B700FED7AB00FED7AB00FED7AB00FEDDBB00FFFDF800FFFDF800FDF6 - C6008F8E8D00FECFC20099969700000000000000000000000000CC9A9900EDD9 - B700FDF6C600FDE8B700EAC8A100CC9A9900FEE4CA00FEFED300FDF6C600FDF6 - C600FDF6C600FDE8B700FDE8B700FDE8B700FDE8B700FED7AB00FED7AB00DAB4 - A000CC9A9900CC9A990000000000000000000000000000000000000000000000 - 0000059ACD0070D2FD0041B3DA00D4FDFF0099FDFE0099FDFE0099FDFE0099FD - FE0099FDFE0018992E0058E0870025A63D0051B4940084E9FE00ADFBFE0064C1 - E100000000000000000000000000000000000000000000000000059ACD0056C6 - F0002EACD800D4FDFF0084E9FE008DF3FE002A8C430058E0870058E0870058E0 - 870047CE710030C3690047CE710025BB4F0025BB4F0016B4320016B432000EA3 - 1B000EA31B0005710A0068A76200000000000000000000000000448A3E0025A6 - 3D0047CE710058E0870058E0870058E087000EA31B00A1C79E00FDE8B700FDE8 - B700FDE8B700FDE8B700FDE8B700FED7AB00FED7AB00FDE8B700FCFEFB00FFF8 - EE00B9AAA600D5B3AF00A4968E00ECEAEA000000000000000000CC9A9900FDE8 - B700FEFED300FEFED300FDF6C600C8ABAB00DAB4A000FEFFE100FEFED300FEFE - D300FEFED300FEFED300FDF6C600FDF6C600FDF6C600FDF6C600EAC8A100CC9A - 9900DAB4A000CDA6A00000000000000000000000000000000000000000000000 - 00001DA6D50070D2FD0037B4E000D1EFF90099FDFE0099FDFE0099FDFE0099FD - FE0099FDFE0051B4940025A63D0058E0870018992E007ECBE600C2FDFF00059A - CD00DFEFF9000000000000000000000000000000000000000000059ACD0061C6 - FE0037B4E000F0F6FD0099FDFE008DF3FE002A8C430058E0870058E0870058E0 - 870047CE710047CE710030C3690047CE710025BB4F0025BB4F0016B4320016B4 - 32000EA31B0005710A008DBC8B0000000000000000007DA972000F83190030C3 - 690047CE710058E0870058E0870058E0870058E087000F831900C7D19500FDF6 - C600FDF6C600FDE8B700FDE8B700FDE8B700FED7AB00FED7AB00FEF0E200FCFE - FE00FDE8B700A4968E00C8ABAB00C7C7C7000000000000000000CC9A9900F0E4 - C700FEFED300FEFED300FEFED300FDE8B700CDA6A000D8C5B500FEFFE100FEFF - E100FEFFE100FEFED300FEFED300FEFED300FEFED300EDD9B700CC9A9900EAC8 - A100FECC9A00D6A8940000000000000000000000000000000000000000000000 - 00001DA6D5007BDDFE0056C6F0007ECBE600E4FDFE00D4FDFF00C2FDFF00C2FD - FF00ADFBFE00ADFBFE0025A63D0058E0870030C3690051B49400D4FDFF00C2FD - FF00059ACD000000000000000000000000000000000000000000059ACD0070D2 - FD0061C6FE009EDEEE00C2FDFF0099FDFE002A8C430049AA490025A63D0025A6 - 3D0025A63D0025A63D0018992E0025BB4F0030C3690030C3690025BB4F0016B4 - 320005710A00AFD0B0000000000000000000A1C79E0005710A0025BB4F0025BB - 4F0030C3690030C3690047CE710058E0870058E0870047CE7100267D2700FDF6 - C600E3D3AA00CB9A8200EDBD9200FDE8B700FDE8B700FED7AB00FDE8B700FFFF - FE00FDF6C60080808000D6BCBB00B5B3B3000000000000000000CC9A9900F0E4 - C700FEFED300FEFED300FEFED300FEFED300E5CDBE00CC9A9900F3E3D100FFF8 - EE00FEFFE100FEFFE100FEFFE100FEFFE100EDD9B700CC9A9900DAB4A000FDE8 - B700FDE8B700D6A8940000000000000000000000000000000000000000000000 - 00001DA6D50084E9FE007BDDFE0056C6F00037B4E00037B4E00037B4E00037B4 - E000E4FDFE00ADFBFE0025A63D0058E0870058E08700267D2700E4FDFE00E4FD - FE00059ACD000000000000000000000000000000000000000000059ACD0070D2 - FD0061C6FE009BC3D800D4FDFF0099FDFE0099FDFE0099FDFE0099FDFE0099FD - FE0099FDFE0099FDFE0099FDFE0025A63D0030C3690030C3690018992E002A8C - 430069CCC000D1EFF900000000000000000068A76200448A3E00448A3E000F83 - 190025BB4F0047CE710047CE710025BB4F00267D2700448A3E00448A3E008DBC - 8B0060606000707070004E4940006F5B4D006F5B4D00826A5B00FED7AB00D0E3 - EC004571FA0099969700D6BCBB00B9B9B9000000000000000000CC9A9900F3E3 - D100FEFFE100FEFFE100FEFFE100F0E4C700CDA6A000CC9A9900CC9A9900EADB - D400FFF8EE00FFFDF800FEF9F300F5EADF00CDA6A000E2BDB300FDF6C600FDF6 - C600FDF6C600DAB4A00000000000000000000000000000000000000000000000 - 00002EACD8009AE3FF0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE007BDD - FE0037B4E000E4FDFE0025A63D0058E0870058E0870018992E00FCFEFE00F9FF - FE00BEE6F500059ACD0000000000000000000000000000000000059ACD007BDD - FE007BDDFE00B4817600BEE6F500F0FEFE00E4FDFE00D4FDFF00C2FDFF00C2FD - FF00ADFBFE00ADFBFE00C2FDFF0025A63D0047CE710018992E002A8C4300C7EF - F100D4FDFF002EACD80000000000000000000000000000000000E6E5E4000F83 - 190016B4320025BB4F0030C3690018992E00C0D5C200FEFFE100FEFED300FEFE - D300606060008F8F8F0030303000927D7000A18C8200CB9A8200FED7AB00ECEA - EA004571FA008F8E8D00C8ABAB00B9B9B9000000000000000000CC9A9900EFE4 - D800FEFFE100FEEBD700D5B3AF00CC9A9900BFBFBF00C7EFF100C0C0C000CC9A - 9900CC9A9900CDA6A000D5B3AF00CDA6A000CC9A9900F0E4C700FEFED300FEFE - D300FEFED300DAB4A00000000000000000000000000000000000000000000000 - 000037B4E00099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD - FE008DF3FE0037B4E00025A63D0058E0870058E0870018992E00BEE6F500BEE6 - F500FCFEFB00059ACD0000000000000000000000000000000000059ACD007BDD - FE007BDDFE00BA8E8500C7DDE1009BC3D80037B4E00041B3DA0037B4E0002EAC - D800F0FEFE00C2FDFF00C2FDFF0025A63D000F83190068A762009AE3FF00E4FD - FE00E4FDFE002EACD800DFEFF900000000000000000000000000ECEAEA000571 - 0A0016B4320025BB4F0025BB4F0018992E00C0D5C200FFFDF800FEFFE100FEFF - E100F0E4C700C6B8AB004040400059504700FED7AB00FDE8B700FDE8B700FFF8 - EE00FDE8B70086858500B9AAA600B9B9B9000000000000000000CC9A9900EADB - D400E2BDB300CC9A9900C6B8AB00E6E5E400F0FEFE00F9FFFE00E4FDFE00C2FD - FF00C7DDE100BEC6DC00C4C4C400AFCFDE00C8ABAB00CC9A9900FEEBD700FEFE - D300FEFED300DAB4A00000000000000000000000000000000000000000000000 - 000037B4E00099FDFE0099FDFE0099FDFE0099FDFE0099FDFE00ADFBFE0099FD - FE0099FDFE0099FDFE0025A63D0047CE710058E0870018992E002EACD80037B4 - E00037B4E000ACDBF400000000000000000000000000000000001DA6D50084E9 - FE0084E9FE00CB9A8200F8F3EC00FEF4E900FEF0E200FEEBD700FEEBD700C0D5 - C2002EACD800F0FEFE00F0FEFE0005710A00A1C79E00D4FDFF009AE3FF00F0FE - FE00F0FEFE00F0FEFE00059ACD0000000000000000000000000000000000448A - 3E000F83190016B4320025BB4F0018992E00A1C79E00FFFDF800FEFFE100FEFF - E100FEFFE100FEFED300E3D3AA0043424200826A5B00FED7AB00FDF6C600FEEB - D700C7D195008F8F8F0099969700D1D1D1000000000000000000CC9A9900CC9A - 9900CDA6A000BEC6DC00F9FFFE00FFFFFF00F9FFFE00F9FFFE00F0FEFE00F0FE - FE00E4FDFE00E4FDFE00E4FDFE00E4FDFE00D1EFF900C8ABAB00CC9A9900F0E4 - C700FEFFE100DAB4A00000000000000000000000000000000000000000000000 - 000037B4E000D4FDFF0099FDFE0099FDFE0099FDFE00C2FDFF00ACDBF40037B4 - E0002EACD80051B4940025A63D0047CE710058E0870018992E00000000000000 - 00000000000000000000000000000000000000000000000000001DA6D5008DF3 - FE008DF3FE00CB9A8200F8F3EC00FEF9F300FEF4E900FEF0E200FEEBD700FEEB - D700C0D5C2002EACD8002EACD8002EACD800DFEFF900FCFEFE00E4FDFE00FEFE - FE00FEFEFE00FCFEFE00059ACD0000000000000000000000000000000000AFAF - AF0005710A000EA31B0016B4320016B43200448A3E00FFFFFF00FCFEFB00FEF9 - F300FEFFE100FEFED300FEFED300E3D3AA0040404000826A5B00FED7AB00FDE8 - B700B1A097009996970086858500000000000000000000000000CC9A9900CC9A - 9900CC9A9900BEC6DC00FFFEFF00FFFEFF00FFFFFF00FFFFFF00F9FFFE00F9FF - FE00F0FEFE00E4FDFE00F0FEFE00E4FDFE00E4FDFE00D1EFF900B9AAA600CDA6 - A000FEEBD700D5B3AF0000000000000000000000000000000000000000000000 - 00002EACD8009EDEEE00E4FDFE00E4FDFE00D4FDFF00BEE6F5007ECBE600FEFF - FC00FFFEFF0005710A0025BB4F0030C3690047CE710018992E00000000000000 - 00000000000000000000000000000000000000000000000000001DA6D50099FD - FE0099FDFE00DCA88700F8F8F800FEFAF700FFF8EE00FEF4E900FEF0E200FEEB - D700FEEBD700FEE4CA00FED6C900B481760071EBF900059ACD00059ACD00059A - CD00059ACD00059ACD00059ACD0000000000000000000000000000000000E6E5 - E400668E67000F8319000EA31B0016B432000F8319008DBC8B00FFFEFF00FFFF - FF00FFFFFE00FFFDF800FEFED300FEFED300E3D3AA0040404000E3B18E00E3D3 - AA008C89880099969700A8A8A800000000000000000000000000F6EFE700CC9A - 9900C8ABAB0090D1F100FFFFFE00FFFEFF00FFFFFF00FEFEFE00FFFEFF00FFFC - FF00FFFFFF00F0FEFE00F0FEFE00E4FDFE00BEE6F500ACDBF4007ECBE600AB8F - A300CDA6A000CDA6A00000000000000000000000000000000000000000000000 - 0000F0F6FD0037B4E00041B3DA0037B4E00037B4E00005710A0005710A00448A - 3E00448A3E000F83190030C3690047CE710047CE710018992E00000000000000 - 00000000000000000000000000000000000000000000000000001DA6D50099FD - FE0099FDFE00E3B18E00F8FBFB00FFFEFE00FEF9F300FFF8EE00FEF4E900FEF0 - E200FEEBD700FEEBD700FEE4CA00B481760099FDFE0099FDFE002EACD8000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000B9B9B900668E67000F8319000EA31B0016B432000F831900448A3E00A1C7 - 9E00DDEADD00D1EFF900FFFDF8008DBC8B00C7D19500EDD9B700E3D3AA008C89 - 8800A8A8A8008F8E8D00EFEFEF0000000000000000000000000000000000F8F0 - F000CC9A9900A9ACD400FEFFFC00FFFFFF00FFFCFF00D0E3EC00ACDBF40090D1 - F10090D1F10098A9EF0085AFCB00AB8FA300AB8FA300CC9A9900B7A2B600B7A2 - B600CC9A9900CC9A990000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000049AA49000F83190016B4 - 320016B4320025BB4F0025BB4F0030C3690030C3690025BB4F000F8319000F83 - 190005710A0068A76200000000000000000000000000000000001DA6D500D4FD - FF0099FDFE00EDBD9200FFFCFF00FFFFFE00FFFFFF00FEFAF700FFF8EE00FEF4 - E900FEF0E200FECFC200FCA3A200B4817600C2FDFF00ADFBFE002EACD8000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000FAF3F200B5B3B3008DBC8B000F8319000F83190016B4320025BB4F001899 - 2E000F83190005710A0005710A00267D2700E3D3AA00EAC8A10099969700AFAF - AF0099969700CFCFCF0000000000000000000000000000000000000000000000 - 0000F8F0F000A9ACD40098A9EF0070D2FD0070D2FD0070D2FD009AE3FF0090D1 - F10090D1F10090D1F10090D1F10090D1F10098A9EF0098A9EF00A9ACD400B7A2 - B600CC9A9900CC9A990000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000EFF9F00005710A000EA3 - 1B0016B4320016B4320025BB4F0025BB4F0030C3690030C3690025BB4F000F83 - 19008DBC8B0000000000000000000000000000000000000000001DA6D500DFEF - F900E4FDFE00EDBD9200FEFEFE00FFFFFE00FEFEFE00FFFFFF00FEF9F300FEF9 - F300B4817600B4817600B4817600B48176002EACD8002EACD8002EACD8000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000EFEFEF00B9B9B900DDEADD007DA97200267D270005710A000F83 - 190005710A00448A3E007DA97200DAB4A000B1A09700A8A8A800B9B9B9009F9F - 9F00CFCFCF000000000000000000000000000000000000000000000000000000 - 00000000000000000000CDA6A000BFBFBF008DF3FE009AE3FF009AE3FF009AE3 - FF009AE3FF0090D1F10090D1F10090D1F10090D1F10090D1F10098A9EF00B7A2 - B600CC9A9900F1E3E20000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000AFD0B0000571 - 0A000EA31B0016B4320016B4320025BB4F0025BB4F0018992E00267D2700D0E2 - CE000000000000000000000000000000000000000000000000000000000041B3 - DA0041B3DA00EDBD9200FFFEFF00FFFFFF00FFFFFF00FFFFFF00FEFFFC00FEF9 - F300B4817600FECC9A00DCA88700ECDACC000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000EFEFEF00B9B9B900DAD9D900FBFBFC00E6E5E400C7C7 - C700BFBFBF00B9B9B900B5B3B300C4C4C400CFCFCF00C0C0C0009F9F9F00DAD9 - D900000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000CDA6A000BFBFBF008DF3FE008DF3FE009AE3 - FF009AE3FF009AE3FF0090D1F10090D1F10090D1F1009BC3D800CC9A9900D5B3 - AF00000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000000000000000000049AA - 49000F8319000EA31B0016B4320018992E000F8319008DBC8B00000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000EDBD9200FEF0E200FEF0E200F5EADF00EFE4D800EFE4D800EADB - D400B4817600E3B18E00EADBD400000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000DAD9D900B9B9B900C0C0C000DAD9 - D900DAD9D900D1D1D100CCCACA00B9B9B900A8A8A800C4C4C400000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000CDA6A000BFBFBF0099FDFE008DF3 - FE009AE3FF009AE3FF009AE3FF0090D1F100B7A2B600CC9A9900F1E3E2000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000F3F3 - F30005710A000EA31B0005710A00448A3E00E1F1E10000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 - 8700DCA88700EFE4D80000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000F3F3F300DFDF - DF00CCCACA00CCCACA00DAD9D900ECEAEA000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000CDA6A000B5B3B3009EDE - EE008DF3FE009AE3FF009BC3D800CC9A9900D5B3AF0000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000AFD0B00005710A00A1C79E00000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000D6BCBB00CC9A - 9900CC9A9900CC9A9900CC9A9900F1E3E2000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000009F9F9F0080808000C8AB - AB00000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000D0E3EC0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000D0E3EC00F3F3 - F30000000000000000000000000000000000000000000000000000000000A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 7400A47874008C5D5C0000000000000000000000000000000000000000000000 - 0000000000000000000000000000C8ABAB00CB9A8200BA8E8500B4817600C8AB - AB00E4D1CD00FAF3F20000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000A8A8A8005D71AB005D71AB008D82 - A600C8ABAB000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000066A6C3000C7CAC000C7CAC00D0E3EC000000000000000000000000000000 - 000000000000000000000000000000000000F0F6FD00458EB60066A6C3000C7C - AC0000000000000000000000000000000000000000000000000000000000A478 - 7400E2BDB300F3B9B500F3B9B500F3B9B500F3B9B500F3B9B500F3B9B500F3B9 - B500F3B9B500F3B9B500F3B9B500E2BDB300F3B9B500D5B3AF00F3B9B500F3B9 - B500E3B18E008C5D5C0000000000000000000000000000000000000000000000 - 00000000000000000000BF989500DCA88700EDBD9200F3E3D100F0E4C700E5CD - BE00DAB4A000C8ABAB00CC9A9900D6BCBB00E2D6E200FAF3F200000000000000 - 00000000000000000000000000000000000085AFCB004BAFFF003A80E0005D71 - AB008D82A600CDA6A00000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000C7CAC0084E9FE0004B8EA000C7CAC00AFCFDE0000000000000000000000 - 0000000000000000000000000000D0E3EC002783AC009BC3D800D4FDFF002EAC - D800C0D9E600000000000000000000000000000000000000000000000000A478 - 7400DED2CA00FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD - BB00FEDDBB00FED7AB00FEDDBB00FED7AB00FED7AB00FED7AB00F3D1A300FECC - 9A00FECFC2008C5D5C0000000000000000000000000000000000000000000000 - 0000ECEAEA00D6A89400E3B18E00EDBD9200EAC8A100EFE4D800F3E3D100F0E4 - C700ECDACC00EDD9B700B4817600BA8E8500BA8E8500BA8E8500BF989500C8AB - AB00DBC9C600F8F0F0000000000000000000ACDBF40061C6FE004BAFFF003A80 - E0005D71AB008D82A600C8ABAB00000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000458EB600D0E3EC0010D1FE0004B8EA000C7CAC009BC3D800000000000000 - 00000000000000000000D0E3EC002783AC00ACDBF4008DF3FE003FE0FF000C7C - AC0000000000000000000000000000000000000000000000000000000000A478 - 7400DED7D300FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEDD - BB00FDE8B700FDE8B700FED7AB00FED7AB00FED7AB00FED7AB00FED7AB00F3D1 - A300FECFC2008C5D5C000000000000000000000000000000000000000000F6EF - E700D6A89400EDBD9200EDBD9200EDBD9200EAC8A100FEEBD700EFE4D800F3E3 - D100F0E4C700ECDACC00A4787400A4787400B4817600BF989500E3D3AA00DAB4 - A000D6A89400A18C8200BF9895000000000000000000ACDBF40061C6FE004BAF - FF003A80E0005D71AB008D82A600C8ABAB000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000AFCFDE0066A6C30084E9FE0010D1FE0010D1FE00059ACD0066A6C3000000 - 0000000000009BC3D8002783AC00C7EFF10063E5FD0010D1FE001DA6D50066A6 - C30000000000000000000000000000000000000000000000000000000000B481 - 7600DED7D300FEEBD700FEEBD700FEEBD700029A0300FEE4CA00FEE4CA00C7D1 - 95007DA972008DBC8B00E3D3AA00FEDDBB00FED7AB00FED7AB00FED7AB00FED7 - AB00FECFC2008C5D5C0000000000000000000000000000000000EADBD400D6A8 - 9400F3D1A300FECC9A00EAC8A100EDBD9200EAC8A100F5EADF00EFE4D800EFE4 - D800F3E3D100ECDACC00986B6600986B6600986B6600BA8E8500E3D3AA00E3D3 - AA00E3D3AA00EAC8A100BA8E850000000000000000000000000090D1F10061C6 - FE004BAFFF003A80E0005D71AB008D82A600C8ABAB0000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000002783AC00D1EFF90010D1FE0010D1FE003FE0FF002EACD8002783 - AC0085AFCB00458EB600DFEFF90084E9FE0010D1FE0010D1FE000C7CAC00DFEF - F90000000000000000000000000000000000000000000000000000000000B481 - 7600DAD9D900FEF0E200FEEBD700FEEBD700029A03007ACE7F0049AA4900029A - 0300029A0300029A0300029A030068A76200FDE8B700FED7AB00FED7AB00FED7 - AB00FECFC2008C5D5C00000000000000000000000000F1E3E200DAB4A000FED7 - AB00F3D1A300F3D1A300FECC9A00EDBD9200F3D1A300F6EFE700F5EADF00F5EA - DF00F3E3D100F3E3D100986B6600986B6600986B6600A18C8200EDD9B700E3D3 - AA00E3D3AA00E3D3AA00BA8E850000000000000000000000000000000000ACDB - F40061C6FE004BAFFF003A80E0005D71AB008D82A600D5B3AF00000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000009BC3D80085AFCB0063E5FD0010D1FE0010D1FE003FE0FF004BBD - E00066A6C300F0FEFE008DF3FE003FE0FF0010D1FE0004B8EA002783AC000000 - 000000000000000000000000000000000000000000000000000000000000B481 - 7600DFDFDF00FEF0E200FEF0E200FEEBD700029A0300029A0300029A0300029A - 0300029A0300029A0300029A0300029A030068A76200FED7AB00FED7AB00FED7 - AB00FECFC2008C5D5C00000000000000000000000000DAB4A000E3D3AA00EDD9 - B700F3D1A300F3D1A300F3D1A300EAC8A100F3D1A300F6EFE700F6EFE700F5EA - DF00EFE4D800F3E3D100986B6600986B6600986B6600B4817600EDD9B700EDD9 - B700EDD9B700E3D3AA00BA8E8500000000000000000000000000000000000000 - 000090D1F10061C6FE004BAFFF003A80E0005D71AB00AB8FA300000000000000 - 0000F8F0F000DED7D300DED2CA00EADBD400EFE4D800F1E3E200F8F0F0000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000F3F3F3002783AC00D1EFF90010D1FE0010D1FE003FE0FF0063E5 - FD0099FDFE00ADFBFE008DF3FE0063E5FD003FE0FF00059ACD009BC3D8000000 - 000000000000000000000000000000000000000000000000000000000000B481 - 7600E1E0E000FEF4E900FEF0E200FEF0E200029A0300029A0300029A0300029A - 03000EA31B007ACE7F0068A76200029A0300029A0300C7D19500FED7AB00FEDD - BB00FECFC2008C5D5C00000000000000000000000000DAB4A000F3D1A300FED7 - AB00FED7AB00F3D1A300F3D1A300FECC9A00EDD9B700F8F3EC00FEF4E900F5EA - DF00F5EADF00EFE4D800986B6600986B6600986B6600B4817600EDD9B700EDD9 - B700E3D3AA00E3D3AA00BA8E8500000000000000000000000000000000000000 - 000000000000ACDBF4005DB2FC004BAFFF00458EB60099969700D1D1D100E4D1 - CD00BF989500BA8E8500BF989500CDA6A000C8ABAB00CDA6A000D5B3AF00E4D1 - CD00000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000066A6C300ACDBF4003FE0FF0010D1FE0010D1FE0063E5 - FD0071EBF90099FDFE0099FDFE0071EBF9003FE0FF000C7CAC00F2FAF5000000 - 000000000000000000000000000000000000000000000000000000000000BA8E - 8500E6E5E400FEF4E900FEF4E900FEF0E200029A0300029A0300029A0300029A - 03008DBC8B00FEE4CA00FEE4CA00EDD9B70049AA490049AA4900FEDDBB00FEDD - BB00FED6C9008C5D5C00000000000000000000000000DAB4A000E3D3AA00FDE8 - B700FED7AB00F3D1A300EDBD9200EDBD9200F3D1A300FEF9F300F8F3EC00F6EF - E700F5EADF00F5EADF00986B6600986B6600986B6600B4817600ECDACC00EDD9 - B700EDD9B700EDD9B700BA8E8500000000000000000000000000000000000000 - 00000000000000000000ACDBF40090D1F100CFCFCF00A4968E00A18C8200CB9A - 8200EAC8A100FDE8B700FDF6C600FEFED300FEFED300FDF6C600E5CDBE00CDA6 - A000D5B3AF000000000000000000000000000000000000000000000000000000 - 000000000000000000009BC3D800458EB6009AE3FF0010D1FE0010D1FE003FE0 - FF0071EBF9008DF3FE0099FDFE008DF3FE004BBDE000458EB600000000000000 - 000000000000000000000000000000000000000000000000000000000000BA8E - 8500E6E5E400FFF8EE00FEF4E900FEF0E200029A0300029A0300029A0300029A - 0300029A0300029A0300FEE4CA00FEE4CA00EDD9B70025A63D00FED7AB00FEDD - BB00FECFC200986B6600000000000000000000000000DAB4A000EDD9B700FDE8 - B700EAC8A10085AFCB004493ED004493ED00CABCB700FDFAFA00F9F6F500F8F3 - EC00F6EFE700F5EADF00E4D1CD00D8C5B500CDA6A000BF989500F0E4C700ECDA - CC00EDD9B700EDD9B700BA8E8500000000000000000000000000000000000000 - 0000000000000000000000000000F0F6FD00E6E5E400BF989500CB9A8200EDD9 - B700FDF6C600FEFED300FEFED300FEFED300FEFFE100FEFFE100FFFDF800F6EF - E700C8ABAB00C8ABAB0000000000000000000000000000000000000000000000 - 00000000000085AFCB000C7CAC0004B8EA0010D1FE0010D1FE0010D1FE003FE0 - FF003FE0FF008DF3FE0099FDFE0099FDFE0063E5FD001DA6D500458EB600DFEF - F90000000000000000000000000000000000000000000000000000000000BA8E - 8500ECEAEA00FFF8EE00FEF4E900FEF4E900FEF4E900FEF4E900FEF0E200FEF0 - E200FEEBD700FEE4CA00FEEBD700FEE4CA00FEE4CA00FEE4CA00EDD9B700FEDD - BB00FECFC200986B6600000000000000000000000000DAB4A000F3D1A300EAC8 - A10085AFCB004BAFFF00399CFE00399CFE004493ED00D1EFF900FEFAF700F9F6 - F500F8F3EC00F6EFE700F6EFE700F5EADF00EFE4D800F3E3D100F3E3D100F0E4 - C700ECDACC00EDD9B700BA8E8500000000000000000000000000000000000000 - 000000000000000000000000000000000000EADBD400CC9A9900FED7AB00FDF6 - C600FDF6C600FDF6C600FEFED300FEFFE100FEFFE100FFFDF800FFFFFF00FEFE - FE00F6EFE700BF989500DFDFDF0000000000000000000000000000000000C0D9 - E6002783AC00059ACD0010D1FE003FE0FF003FE0FF0010D1FE0010D1FE0010D1 - FE003FE0FF0071EBF90099FDFE0099FDFE0071EBF90063E5FD0004B8EA000C7C - AC0085AFCB00000000000000000000000000000000000000000000000000CB9A - 8200ECEAEA00FDFAFA00FEF9F3007ACE7F00FEF0E200FEF0E200FEF0E200FEF0 - E200FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD - BB00FED6C900986B6600000000000000000000000000DAB4A000C6B8AB0061C6 - FE0061C6FE004BAFFF004BAFFF00399CFE00399CFE004493ED00DFEFF900FDFA - FA00FEF9F300F8F3EC00F6EFE700F5EADF00F5EADF00EFE4D800F3E3D100F3E3 - D100ECDACC00ECDACC00BF989500F1E3E2000000000000000000000000000000 - 000000000000000000000000000000000000E2BDB300DAB4A000FDF6C600FDE8 - B700FDE8B700FEFED300FEFED300FEFFE100FEF9F300FFFDF800FEFEFE00FFFD - F800FEFFE100E5CDBE00BF9895000000000000000000F3F3F30066A6C3000C7C - AC0004B8EA0063E5FD0071EBF90063E5FD003FE0FF0010D1FE0010D1FE0010D1 - FE003FE0FF0063E5FD008DF3FE0099FDFE008DF3FE0071EBF9003FE0FF0010D1 - FE00059ACD000C7CAC00C0D9E60000000000000000000000000000000000CB9A - 8200EFEFEF00FFFDF800FEF9F3007ACE7F00BBE5B900FEF4E900FEF4E900FEF0 - E200029A0300029A0300029A0300029A0300029A0300FEE4CA00FEE4CA00FEE4 - CA00FED6C900986B6600000000000000000000000000C6B8AB0061C6FE0061C6 - FE0061C6FE0061C6FE004BAFFF004BAFFF00399CFE00399CFE004493ED00DFEF - F900FDFAFA00FEF9F300F8F3EC00F6EFE700F5EADF00F5EADF00EFE4D800F3E3 - D100EADBD400BF989500BF989500F1E3E2000000000000000000000000000000 - 0000000000000000000000000000ECEAEA00CDA6A000EDD9B700FDF6C600FED7 - AB00FDE8B700FEFED300FEFED300FEFFE100FFFDF800FFFDF800FFFDF800FEF9 - F300FEFFE100F0E4C700B4817600ECEAEA009BC3D8000C7CAC0004B8EA0010D1 - FE008DF3FE0099FDFE008DF3FE0071EBF9003FE0FF0010D1FE0010D1FE0010D1 - FE0010D1FE003FE0FF0071EBF90099FDFE0099FDFE008DF3FE003FE0FF0010D1 - FE0010D1FE0010D1FE000C7CAC0085AFCB00000000000000000000000000CB9A - 8200EFEFEF00F9FFFE00FEFAF700D0E2CE000EA31B00BBE5B900FEF4E900FEF0 - E200FEEBD70025A63D00029A0300029A0300029A0300FEEBD700FEE4CA00FEE4 - CA00FECFC200986B66000000000000000000ACDBF4004BAFFF005DB2FC0061C6 - FE0061C6FE0061C6FE0061C6FE005DB2FC004BAFFF00399CFE00399CFE005DB2 - FC00F0F6FD00F9F6F500F9F6F500F8F3EC00F6EFE700F5EADF00F5EADF00EFE4 - D800AB8FA3008D82A600DED7D300000000000000000000000000000000000000 - 0000000000000000000000000000F1E3E200CDA6A000FDF6C600FDE8B700FED7 - AB00FDE8B700FDF6C600FEFED300FEFFE100FEFFE100FFF8EE00FFF8EE00FEFF - E100FEFED300FDF6C600BF989500F1E3E2002783AC003FE0FF0063E5FD00ADFB - FE00C2FDFF00C2FDFF00D4FDFF00D4FDFF00D4FDFF00FCFEFE003FE0FF0010D1 - FE0010D1FE003FE0FF0063E5FD00E4FDFE00E4FDFE00D4FDFF00ADFBFE008DF3 - FE0063E5FD003FE0FF0063E5FD002783AC00000000000000000000000000CB9A - 8200F3F3F300FFFFFF00FFFEFF00FFFDF80049AA4900029A03007ACE7F00D8EB - CC00D8EBCC0049AA4900029A0300029A0300029A0300FEEBD700FEEBD700FEE4 - CA00FECFC200A47874000000000000000000DFEFF9004BAFFF004BAFFF005DB2 - FC0061C6FE0061C6FE0061C6FE0061C6FE005DB2FC004BAFFF00399CFE00399C - FE005DB2FC00F0F6FD00FEF9F300F9F6F500F6EFE700F6EFE700F5EADF008D82 - A6008D82A600D1CDE40000000000000000000000000000000000000000000000 - 0000000000000000000000000000F1E3E200CDA6A000FDF6C600FDE8B700FED7 - AB00FDE8B700FDF6C600FEFED300FEFED300FEFFE100FEFFE100FEFFE100FEFE - D300FEFED300FEFED300BF989500F1E3E200458EB60066A6C30085AFCB0066A6 - C30066A6C300458EB600458EB6002783AC002783AC0066A6C3007BDDFE0010D1 - FE0010D1FE003FE0FF0037B4E0002783AC002783AC002783AC00458EB60066A6 - C30066A6C30085AFCB0085AFCB002783AC00000000000000000000000000DCA8 - 8700F3F3F300FFFFFF00FEFEFE00FFFEFF00D8EBCC00029A0300029A0300029A - 0300029A0300029A0300029A0300029A0300029A0300FEEBD700FEEBD700FEEB - D700F3B9B500A4787400000000000000000000000000DFEFF9004BAFFF004BAF - FF005DB2FC0061C6FE0061C6FE0061C6FE0061C6FE005DB2FC004BAFFF004BAF - FF00399CFE004BAFFF00F0F6FD00FEF9F300F9F6F500F8F0F00085AFCB005D71 - AB00BEC6DC000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000F1E3E200CDA6A000FDF6C600FDF6C600FDE8 - B700FDE8B700FDE8B700FDF6C600FEFED300FEFED300FEFED300FEFED300FEFE - D300FEFED300FDE8B700BA8E8500E6E5E400F0F6FD009BC3D800C0D9E600C0D9 - E600C0D9E600F2FAF500000000000000000000000000458EB600ADFBFE0010D1 - FE0010D1FE0010D1FE00059ACD00C7DDE100000000000000000000000000C0D9 - E600C0D9E600C0D9E6009BC3D800DFEFF900000000000000000000000000DCA8 - 8700F3F3F300FFFFFF00FFFEFF00FFFFFF00FFFDF80091DEA900029A0300029A - 0300029A0300029A0300029A03000EA31B00029A0300FEEBD700F3B9B500FCA3 - A200FCA3A200A478740000000000000000000000000000000000DFEFF9004BAF - FF004BAFFF004BAFFF0061C6FE0061C6FE0061C6FE0061C6FE005DB2FC004BAF - FF004BAFFF00399CFE005DB2FC00F0F6FD00F0FEFE0085AFCB005D71AB00BEC6 - DC00000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000F8F0F000DAB4A000EDD9B700FEFFE100FEE4 - CA00FDE8B700FED7AB00FDE8B700FDE8B700FDF6C600FDF6C600FDF6C600FDF6 - C600FEFED300FED7AB00B4817600F8F0F0000000000000000000000000000000 - 00000000000000000000000000000000000000000000458EB600D1EFF90010D1 - FE0010D1FE0010D1FE000C7CAC00000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000DCA8 - 8700F8F8F800FFFFFF00FEFEFE00FFFFFF00FFFEFF00FEFEFE00DDEADD007ACE - 7F0049AA490049AA49007ACE7F00FEEBD700029A0300E2BDB300D6A89400DCA8 - 8700CB9A8200A47874000000000000000000000000000000000000000000D1EF - F9004BAFFF004BAFFF004BAFFF0061C6FE0061C6FE0061C6FE0061C6FE0061C6 - FE004BAFFF004BAFFF00399CFE005DB2FC0098A9EF003A80E000C0D9E6000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000D6BCBB00E2BDB300FFF8EE00FEFF - FC00FEF9F300FEE4CA00FDE8B700FDE8B700FDE8B700FDE8B700FDE8B700FDF6 - C600FDF6C600D6A89400C8ABAB00000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000085AFCB00AFCFDE003FE0 - FF0010D1FE0004B8EA00458EB600000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000DCA8 - 8700FAFAFA00FFFFFF00FCFEFE00FFFFFF00FFFFFF00FFFEFE00FEFAF700FEFA - F700FFF8EE00FFF8EE00FEF4E900FEF4E900FEF4E900B4817600B4817600B481 - 7600B4817600A478740000000000000000000000000000000000000000000000 - 0000D1EFF9005DB2FC004BAFFF004BAFFF0061C6FE0061C6FE0070D2FD0061C6 - FE0061C6FE004BAFFF004BAFFF00399CFE00399CFE006682F100000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000ECEAEA00CDA6A000ECDACC00FFFF - FF00FFFFFF00FEE4CA00FDE8B700FED7AB00F3D1A300FED7AB00FDF6C600FDF6 - C600EDBD9200B481760000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000C7DDE10066A6C30084E9 - FE0010D1FE0004B8EA0085AFCB00000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000E3B1 - 8E00FFFDF800FFFEFF00FFFFFF00FFFFFF00FFFEFF00FFFFFE00FFFEFF00FFFF - FF00FFFDF800FDFAFA00FEF9F300FEF9F300FFF8EE00B4817600FCB04C00DCA8 - 8700CB9A8200E4D1CD0000000000000000000000000000000000000000000000 - 000000000000000000005DB2FC004BAFFF004BAFFF005DB2FC0061C6FE0061C6 - FE0061C6FE0061C6FE004BAFFF004493ED003A80E0006682F100000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000DED7D300CDA6A000EADB - D400FCFEFE00FFF8EE00FDF6C600FDF6C600FDF6C600FEFED300FDE8B700EDBD - 9200BA8E8500DFDFDF0000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000000000002783AC00C2FD - FF0010D1FE00059ACD00C0D9E600000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000E3B1 - 8E00FEFEFE00FFFEFE00FFFFFE00FFFFFF00FFFFFF00FEFEFE00FFFFFE00FFFF - FE00FFFEFF00FFFDF800FFFDF800FEF9F300FEF9F300B4817600DCA88700CB9A - 8200ECDACC000000000000000000000000000000000000000000000000000000 - 00000000000000000000F0F6FD004BAFFF004BAFFF004BAFFF005DB2FC0070D2 - FD009AE3FF00D1EFF9007C7FCC0002019A000732DE006682F100000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000EADBD400BA8E - 8500D6A89400EDD9B700FDE8B700FDF6C600FDE8B700E3D3AA00D6A89400BF98 - 9500F1E3E2000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000458EB600DFEF - F9003FE0FF000C7CAC0000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000E3B1 - 8E00FFFFFE00FCFEFB00FFFCFF00F8FBFB00FAFAFA00FAFAFA00F9F6F500F9F6 - F500F3F3F300EFEFEF00EFEFEF00ECEAEA00ECEAEA00B4817600DCA88700EADB - D400000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000F0FEFE00ACDBF400BEE6F500F0F6FD000000 - 000000000000000000007C7FCC0002019A00295AF7006682F100000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000FAF3 - F200CABCB700BA8E8500B4817600BA8E8500BF989500CC9A9900D6BCBB000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000066A6C300AFCF - DE0060D4F000458EB60000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 - 8700DCA88700DCA88700DCA88700DCA88700DCA88700B4817600F3E3D1000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000BEC6DC008D82A6008D82A600D1CDE400000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000002783 - AC000C7CAC00AFCFDE0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000D0E2CE00C0D5C200C7C7 - C700C0D5C200C0D5C200EFEFEF00000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000EFF9F000C0D5C200C7C7 - C700C0D5C200C0D5C200DDEADD00000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000E1F1E1007DA97200267D27000450070005710A000571 - 0A0005710A000450070004500700255E2800A8A8A80000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000A1C79E00267D27000450070005710A000571 - 0A0005710A000450070004500700255E2800668E6700DDEADD00000000000000 - 000000000000000000000000000000000000000000000000000000000000A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 7400A47874008C5D5C000000000000000000CC670100CC670100CC670100CC67 - 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 - 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 - 0100CC670100CC670100CC670100000000000000000000000000000000000000 - 0000000000008DBC8B0005710A000F8319000EA31B0016B4320009B2190009B2 - 190009B2190009B21900029A03000F83190004500700255E2800D1D1D1000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000D0E2CE000F83190005710A0018992E0009B2190009B2190009B2 - 190009B2190009B2190009B21900029A030005710A0004500700879185000000 - 000000000000000000000000000000000000000000000000000000000000A478 - 7400E2BDB300F3B9B500F3B9B500F3B9B500F3B9B500F3B9B500F3B9B500F3B9 - B500F3B9B500F3B9B500F3B9B500E2BDB300F3B9B500D5B3AF00F3B9B500F3B9 - B500E3B18E008C5D5C000000000000000000CC670100FFFFFE00FFFFFF00FEFA - F700FEF9F300FFF8EE00FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEEB - D700FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7 - AB00FED7AB00FED7AB00CC67010000000000000000000000000000000000EFF9 - F00049AA490005710A0016B4320016B4320016B4320016B4320016B4320009B2 - 190009B2190009B2190009B2190009B2190009B2190005710A0004500700A8A8 - A800000000000000000000000000000000000000000000000000000000000000 - 0000A1C79E0005710A0018992E0016B4320016B4320016B4320016B4320009B2 - 190009B2190009B2190009B2190009B2190009B21900029A030004500700668E - 670000000000000000000000000000000000000000000000000000000000A478 - 7400DED2CA00FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD - BB00FEDDBB00FEDDBB00FEDDBB00FED0B700FED7AB00FED7AB00F3D1A300FECC - 9A00FECFC2008C5D5C000000000000000000CC670100FFFFFE00FFFFFF00FFFF - FF00FEF9F300FEF4E900FEF0E200FEF0E200FEF0E200FEEBD700FEE4CA00FEE4 - CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FED7AB00FED7AB00FED7 - AB00FED7AB00FED0B700CC6701000000000000000000000000000000000068A7 - 62000F83190025BB4F0025BB4F0016B4320016B4320016B4320016B4320016B4 - 320016B4320009B2190009B2190009B2190009B2190009B21900029A03000450 - 0700B9B9B900000000000000000000000000000000000000000000000000A1C7 - 9E000F83190016B4320025BB4F0025BB4F0016B4320016B4320016B4320016B4 - 320016B4320009B2190009B2190009B2190009B2190009B21900029A03000450 - 0700668E6700000000000000000000000000000000000000000000000000A478 - 7400DAD9D900FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD - BB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00F3D1 - A300FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFFFE00FFFF - FF00999697009996970099969700FEF4E900FEF4E900FEF0E200999697009996 - 970099969700FEE4CA00FEE4CA00FEE4CA00999697009996970099969700FED7 - AB00FEDDBB00FED7AB00CC6701000000000000000000000000008DBC8B000F83 - 190025BB4F0025BB4F0025BB4F0025BB4F0025BB4F0016B4320030C36900EFF9 - F000EFF9F00047CE710009B2190009B2190009B2190009B2190009B21900029A - 030004500700DDEADD0000000000000000000000000000000000E1F1E1000F83 - 190025A63D0025BB4F0025BB4F0025BB4F0025BB4F0016B4320047CE7100F2FA - F500F2FAF50039C3390016B4320009B2190009B2190009B2190009B219000EA3 - 1B00045007009F9F9F000000000000000000000000000000000000000000B481 - 7600DED7D300FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4 - CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7 - AB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFFFF00FFFF - FE00FFFFFF00FFFFFF00FFFDF800FEF9F300FFF8EE00FFF8EE00F6EFE700F6EF - E700F3E3D100FEE4CA00FEEBD700FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD - BB00FEDDBB00FEDDBB00CC6701000000000000000000DDEADD000F83190025BB - 4F0030C3690025BB4F0025BB4F0025BB4F0025BB4F0047CE7100EFF9F000FFFF - FE00FEFEFE0091DEA90016B4320016B4320009B2190009B2190009B2190009B2 - 190005710A00668E67000000000000000000000000000000000049AA49001899 - 2E0030C3690030C3690025BB4F0025BB4F0025BB4F0025BB4F0091DEA900FEFE - FE00FFFFFF00F2FAF50030C3690016B4320009B2190009B2190009B2190009B2 - 19000EA31B0004500700F3F3F30000000000000000000000000000000000B481 - 7600DAD9D900FEF0E200FEEBD700FEEBD700FEEBD700FEEBD700FEE4CA00FEE4 - CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7 - AB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFEFF00FFFF - FF004571FA004571FA004571FA00FFFCFB00FEFAF700F6EFE700A23F0800A23F - 0800A23F0800FEEBD700FEEBD700FEE4CA00059ACD00059ACD00059ACD00FEDD - BB00FEDDBB00FEDDBB00CC67010000000000000000007ACE7F0018992E0030C3 - 690030C3690030C3690030C3690030C3690047CE7100F2FAF500FFFFFF00FCFE - FE00EFF9F00025BB4F0016B4320016B4320016B4320016B4320009B2190009B2 - 190009B2190004500700DDEADD000000000000000000D9F3E2000F83190030C3 - 690030C3690030C3690030C3690030C3690025BB4F0025BB4F0047CE7100EFF9 - F000FFFFFF00FCFEFE00F8F8F80039C3390016B4320016B4320009B2190009B2 - 190009B2190005710A007DA9720000000000000000000000000000000000B481 - 7600DFDFDF00FEF0E200FEF0E200FEEBD700FEEBD700A9ACD400EADBD400FEE4 - CA00FEE4CA00FEDDBB00CCC0CC00FEDDBB00FEDDBB00FED7AB00FEDDBB00FED7 - AB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFFFE00FFFF - FF004571FA006682F1004571FA00FFFFFF00FEFAF700FEF9F300A23F0800A23F - 0800A23F0800F3E3D100FEEBD700FEEBD700059ACD00059ACD00059ACD00FEDD - BB00FEDDBB00FEDDBB00CC670100000000000000000018992E0025BB4F0030C3 - 690030C3690030C3690030C3690047CE7100F8FBFB00FFFFFF00FEFFFC00F2FA - F50047CE710025BB4F0016B4320016B4320016B4320016B4320016B4320009B2 - 190009B2190005710A007DA9720000000000000000007ACE7F0018992E0030C3 - 690030C3690030C3690030C3690030C3690030C3690025BB4F0025BB4F0047CE - 7100F2FAF500FFFEFF00FFFFFF00EFF9F00030C3690016B4320016B4320009B2 - 190009B219000EA31B00255E280000000000000000000000000000000000B481 - 7600E1E0E000FEF4E900FEF0E200FEF0E20098A9EF000335FB00295AF700EADB - D400FEE4CA0098A9EF000335FB006682F100FED0B700FEDDBB00FEDDBB00FED7 - AB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFEFF00FFFF - FF004571FA004571FA004571FA00FFFFFF00FEFEFE00F8F8F800A23F0800A23F - 0800A23F0800FEF0E200FEF0E200FEEBD700059ACD00059ACD00059ACD00FEE4 - CA00FEE4CA00FEE4CA00CC67010000000000E1F1E100029A030030C3690030C3 - 690030C3690030C3690047CE7100F2FAF500FFFFFF00FFFFFF00F8FBFB0047CE - 710025BB4F0025BB4F0025BB4F0016B4320016B4320016B4320016B4320016B4 - 320016B432000F831900448A3E00000000000000000049AA490025BB4F0030C3 - 690030C3690030C3690030C3690030C3690030C3690030C3690030C3690030C3 - 690047CE7100F2FAF500FCFEFE00FFFFFE00F2FAF50030C3690016B4320016B4 - 320016B4320009B2190004500700DDEADD00000000000000000000000000BA8E - 8500E6E5E400FEF4E900FEF4E900E2D6E2000335FB000335FB000335FB00295A - F70098A9EF000335FB000335FB000335FB00A9ACD400FEDDBB00FEDDBB00FEDD - BB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFFFF00FFFF - FE00FFFEFF00FCFEFE00FEFFFC00FFFFFF00FFFEFF00FFFDF800FEF9F300FFF8 - EE00FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4 - CA00FEE4CA00FEE4CA00CC67010000000000BBE5B9000EA31B0030C3690030C3 - 690030C3690058E08700F9F6F500FCFEFE00FFFFFE00FFFFFE00AFE6BF0091DE - A90091DEA90091DEA90091DEA90091DEA90091DEA90091DEA90047CE710016B4 - 320016B432000EA31B0005710A0000000000000000000EA31B0025BB4F0030C3 - 690030C3690058E08700AFE6BF0091DEA90091DEA90091DEA90091DEA90091DE - A90091DEA900AFE6BF00FFFFFF00FFFEFF00FFFFFF00F2FAF50030C3690016B4 - 320016B4320016B4320005710A00C0D5C200000000000000000000000000BA8E - 8500E6E5E400FFF8EE00FEF4E900FEF4E90098A9EF000335FB000335FB000335 - FB000335FB000335FB000335FB006682F100FEDDBB00FEE4CA00FEDDBB00FEDD - BB00FED6C900986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF - FF00B5B3B300B5B3B300B5B3B300FFFFFE00FFFFFE00FFFFFF00B5B3B300B5B3 - B300AFAFAF00FFF8EE00FEF4E900FEF0E200AFAFAF00AFAFAF00B9AAA600FEE4 - CA00FEE4CA00FEE4CA00CC67010000000000BBE5B9000EA31B0030C3690030C3 - 690047CE7100F2FAF500FFFFFF00FFFFFE00FCFEFE00FFFFFF00FFFFFF00FFFE - FF00FFFFFF00FFFEFF00FCFEFE00FFFFFF00FFFEFF00FEFFFC0091DEA90016B4 - 320016B432000EA31B0005710A000000000000000000029A030030C3690030C3 - 690030C3690091DEA900FFFFFF00FFFEFF00FEFEFE00FFFFFF00FFFFFF00FEFE - FE00FFFFFF00FFFEFF00FFFFFF00FFFEFF00FFFFFF00FEFEFE00F2FAF50030C3 - 690025BB4F0016B4320005710A00C0D5C200000000000000000000000000BA8E - 8500E6E5E400FEF9F300FFF8EE00FEF4E900FEF4E900BEC6DC000335FB000335 - FB000335FB000335FB006682F100FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD - BB00FECFC200986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF - FF00FEFEFE00FFFFFF00FFFFFE00FFFEFF00FFFFFF00FFFFFE00FFFFFF00FFFF - FF00FEF9F300FFF8EE00FFF8EE00FFF8EE00FEF4E900FEF4E900FEEBD700FEEB - D700FEEBD700FEEBD700CC67010000000000BBE5B9000EA31B0030C3690030C3 - 690058E08700F0F6FD00FFFFFF00FCFEFE00FFFEFF00FFFEFF00FFFEFF00FFFF - FF00FEFEFE00FFFEFF00F9FFFE00F9FFFE00FFFFFF00FFFFFF0091DEA90025BB - 4F0016B4320016B4320005710A000000000000000000029A030030C3690030C3 - 690030C3690091DEA900FBFBFC00FCFEFE00FFFFFF00F9FFFE00FCFEFE00FFFF - FF00FFFFFF00FFFFFF00FCFEFE00FFFEFF00FCFEFE00FFFFFF00D9F3E20025BB - 4F0016B4320016B432000F831900C0D5C200000000000000000000000000CB9A - 8200ECEAEA00FEFAF700FEF9F300FFF8EE00FFF8EE0098A9EF000335FB000335 - FB000335FB000335FB00295AF700EADBD400FEE4CA00FEE4CA00FEE4CA00FEDD - BB00FED6C900986B66000000000000000000CC670100FFFFFF00FFFFFF00FCFE - FE00CC9A9900CC9A9900CC9A9900FFFEFF00FEFFFC00FFFFFF00E27E0300E27E - 0300E27E0300FEF9F300FEFAF700FEF4E900029A0300029A0300029A0300FEEB - D700FEEBD700FEEBD700CC67010000000000BBE5B90009B2190030C3690030C3 - 690030C3690047CE7100F3F3F300FFFEFF00FFFFFF00FCFEFE00D9F3E20091DE - A90091DEA90091DEA900AFE6BF0091DEA90091DEA90091DEA90047CE710025BB - 4F0025BB4F0025A63D002A8C4300000000000000000016B4320030C3690030C3 - 690030C3690058E08700AFE6BF0091DEA90091DEA900AFE6BF0091DEA90091DE - A90091DEA900D9F3E200FFFFFF00FFFEFF00FFFFFF00D9F3E20030C3690025BB - 4F0025BB4F0025BB4F0005710A00C0D5C200000000000000000000000000CB9A - 8200EFEFEF00FFFDF800FEFAF700FEF9F30098A9EF000335FB000335FB000335 - FB000335FB000335FB000335FB00295AF700EADBD400FEE4CA00FEE4CA00FEE4 - CA00FED6C900986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF - FF00CC9A9900CC9A9900CC9A9900FFFFFF00FFFEFF00FCFEFE00E27E0300E27E - 0300E27E0300FBFBFC00FEFAF700FFF8EE00029A0300029A0300029A0300FEF0 - E200FEF0E200FEF0E200CC67010000000000EFF9F000029A030047CE710047CE - 710030C3690030C3690058E08700F0F6FD00FFFEFF00FFFFFF00FFFFFF0091DE - A90030C3690030C3690030C3690030C3690030C3690030C3690025BB4F0025BB - 4F0025BB4F0018992E0049AA4900000000000000000039C3390039C3390047CE - 710030C3690030C3690030C3690030C3690030C3690030C3690030C3690030C3 - 690091DEA900F9FFFE00FFFEFF00FEFEFE00D9F3E20030C3690030C3690025BB - 4F0025BB4F0025BB4F0005710A0000000000000000000000000000000000CB9A - 8200EFEFEF00FFFEFE00FDFAFA00DFEFF9000335FB000335FB000335FB006682 - F100D1CDE4000335FB000335FB000335FB0098A9EF00FEE4CA00FEE4CA00FEE4 - CA00FECFC200986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF - FE00CC9A9900CC9A9900CC9A9900FFFFFE00FFFFFF00FFFFFF00E27E0300E27E - 0300E27E0300FEFFFC00FFFEFF00FEFAF700029A0300029A0300029A0300FEF0 - E200FEF0E200FEF0E200CC670100000000000000000039C3390047CE71007ACE - 7F0047CE710030C3690030C3690047CE7100FCFEFB00FCFEFE00FFFFFF00FCFE - FE0091DEA90030C3690030C3690030C3690030C3690030C3690030C3690030C3 - 690025BB4F000F831900A1C79E00000000000000000091DEA90016B432007ACE - 7F0047CE710047CE710030C3690030C3690030C3690030C3690030C3690091DE - A900FFFFFE00FFFFFF00FCFEFE00D9F3E20030C3690030C3690030C3690030C3 - 690025BB4F0018992E0049AA490000000000000000000000000000000000CB9A - 8200F3F3F300FFFFFE00FEFEFE00FEFEFE0098A9EF000335FB006682F100FEF4 - E900FEF4E900D1CDE4000335FB006682F100FEEBD700FEEBD700FEEBD700FEE4 - CA00FECFC200986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF - FF00FEFEFE00FFFEFE00FFFFFF00FFFEFF00FFFFFE00FFFEFF00FFFEFF00FFFF - FE00FFFEFF00FFFFFF00FFFFFF00FDFAFA00FEFAF700FFF8EE00FEF4E900FEF0 - E200FEF0E200FEF0E200CC670100000000000000000091DEA90009B2190091DE - A9007ACE7F0058E0870047CE710030C3690051B49400F8FBFB00FFFCFF00FFFE - FF00FFFFFF0047CE710030C3690030C3690030C3690030C3690030C3690030C3 - 690025BB4F000F831900EFF9F0000000000000000000EFF9F00009B219007ACE - 7F0091DEA90058E0870047CE710030C3690030C3690030C3690058E08700FBFB - FC00FEFEFE00FFFEFF00D9F3E20047CE710030C3690030C3690030C3690030C3 - 690030C369000F831900AFD0B00000000000000000000000000000000000DCA8 - 8700F3F3F300FCFEFE00FFFEFF00FFFCFB00FEFAF700CED7FA00FFF8EE00FAF3 - F200FEF4E900FEF4E900E2D6E200FEF0E200FEEBD700FEEBD700FEEBD700FEEB - D700F3B9B500A47874000000000000000000CC670100FFFFFF00FFFFFF00FFFF - FF00FFFFFF00FFFFFF00FEFEFE00FFFFFF00FFFEFF00FFFFFF00FFFFFE00FFFF - FF00FFFFFF00FFFEFF00FEFFFC00FEFAF700FEF9F300FFF8EE00FEF4E900FEF0 - E200FEF0E200FEF0E200CC67010000000000000000000000000039C3390047CE - 710091DEA90091DEA9007ACE7F0058E0870030C3690058E08700F0FEFE00FFFF - FF00FBFBFC0058E0870030C3690030C3690030C3690030C3690030C3690030C3 - 69000F831900AFD0B000000000000000000000000000000000007ACE7F0016B4 - 320091DEA90091DEA90058E087007ACE7F0047CE710030C369007ACE7F00FFFE - FF00FEFEFE00D9F3E20047CE710030C3690030C3690030C3690030C3690030C3 - 690025A63D0049AA49000000000000000000000000000000000000000000DCA8 - 8700F9F6F500FFFFFF00FFFFFF00FFFFFF00FEFEFE00FFFDF800FEFAF700FFF8 - EE00FFF8EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700F3B9B500FCA3 - A200FCA3A200A47874000000000000000000CC670100E27E0300E27E0300E27E - 0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E - 0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E - 0300E27E0300E27E0300CC670100000000000000000000000000D8EBCC0009B2 - 190091DEA90091DEA90091DEA9007ACE7F0058E0870047CE710051B49400AFE6 - BF00AFE6BF0030C3690030C3690030C3690030C3690030C3690030C369001899 - 2E0049AA490000000000000000000000000000000000000000000000000039C3 - 390039C33900AFE6BF0091DEA90091DEA90058E0870047CE710047CE710091DE - A900AFE6BF0047CE710030C3690030C3690030C3690030C3690030C3690025BB - 4F000F831900D9F3E2000000000000000000000000000000000000000000DCA8 - 8700F8F8F800FFFEFF00FFFFFF00FFFEFF00FFFFFF00FFFEFF00FFFDF800FEFA - F700FFF8EE00FFF8EE00FEF4E900FEF4E900FEF4E900E2BDB300D6A89400DCA8 - 8700CB9A8200A47874000000000000000000D3936400CC670100CC670100CC67 - 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 - 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 - 0100CC670100CC670100CC6701000000000000000000000000000000000091DE - A90009B2190091DEA900BBE5B90091DEA90091DEA9007ACE7F0047CE710047CE - 710030C3690030C3690030C3690030C3690030C3690030C369000EA31B0025A6 - 3D00EFF9F000000000000000000000000000000000000000000000000000EFF9 - F00039C3390039C33900BBE5B90091DEA90091DEA9007ACE7F0058E0870051B4 - 940030C3690030C3690030C3690030C3690030C3690030C3690025BB4F00029A - 0300A1C79E00000000000000000000000000000000000000000000000000E3B1 - 8E00FAFAFA00FFFFFF00FFFFFF00FFFEFE00FFFFFF00FFFFFF00FFFEFE00FFFD - F800FEFAF700FEF9F300FFF8EE00FEF4E900FEF4E900B4817600B4817600B481 - 7600B4817600A47874000000000000000000F3E3D100C56E2600CC670100CC67 - 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 - 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 - 0100CC670100CC670100EAC8A100000000000000000000000000000000000000 - 0000BBE5B90009B2190047CE7100BBE5B900AFE6BF0091DEA90091DEA90058E0 - 870047CE710030C3690030C3690030C3690025BB4F000EA31B0025A63D00EFF9 - F000000000000000000000000000000000000000000000000000000000000000 - 0000EFF9F00039C3390039C3390091DEA900BBE5B90091DEA90091DEA90058E0 - 870058E0870030C3690030C3690030C3690030C3690016B432000EA31B00AFD0 - B00000000000000000000000000000000000000000000000000000000000E3B1 - 8E00FBFBFC00FFFFFF00FFFEFF00FFFFFE00FFFEFF00FFFFFE00FFFFFF00FFFF - FF00FFFDF800FFFDF800FEFAF700FFF8EE00FFF8EE00B4817600FCB04C00DCA8 - 8700CB9A8200E4D1CD0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000E1F1E10047CE710009B2190039C339007ACE7F007ACE7F007ACE - 7F007ACE7F0030C3690025BB4F000EA31B000EA31B007ACE7F00000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000007ACE7F0009B2190039C339007ACE7F007ACE7F007ACE - 7F007ACE7F0047CE710025BB4F0016B43200029A030047CE7100E1F1E1000000 - 000000000000000000000000000000000000000000000000000000000000E3B1 - 8E00FEFEFE00FFFFFF00FFFFFE00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FEFE - FE00FFFFFF00FFFDF800FDFAFA00FEF9F300FEF9F300B4817600DCA88700CB9A - 8200EADBD4000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000D9F3E2007ACE7F0039C3390009B2190009B2 - 190009B2190039C3390039C3390091DEA900EFF9F00000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000EFF9F00091DEA90039C3390039C3390009B2 - 190009B2190009B2190039C3390091DEA900E1F1E10000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000E3B1 - 8E00FCFEFE00FCFEFE00FEFEFE00FBFBFC00FAFAFA00F8F8F800F8F8F800F3F3 - F300F3F3F300F3F3F300EFEFEF00ECEAEA00F6EFE700B4817600DCA88700EADB - D400000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 - 8700DCA88700DCA88700DCA88700DCA88700DCA88700B4817600F3E3D1000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000FAF3F200D6A89400D6A89400F1E3E20000000000000000000000 - 0000000000000000000000000000FAF3F200DAB4A000D6A89400FAF3F2000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000A4787400A4787400A4787400A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 7400A47874008C5D5C0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000F3F3F300A23F0800A23F0800AF4B0200A23F0800DAB4A000000000000000 - 00000000000000000000EADBD400A23F0800AF4B0200AF4B0200A23F0800EADB - D400000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000A4787400A4787400A4787400A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 7400A4787400A47874008C5D5C00000000000000000000000000000000000000 - 000000000000000000000000000000000000A4787400D8C5B500FECFC200FECF - C200FECFC200FECFC200FECFC200FECFC200FECFC200FECFC200FECFC200FECF - C200FECFC2008C5D5C0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000D3936400AF4B0200AF4B0200A23F0800AF4B0200A23F0800EADBD4000000 - 000000000000F5EADF00A23F0800AF4B0200A23F0800AF4B0200BD580100BD79 - 5800000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000B4817600DBC9C600FECFC200FECF - C200FECFC200FECFC200FECFC200FECFC200FECFC200FECFC200FECFC200FECF - C200FECFC200FECFC2008C5D5C00000000000000000000000000000000007ECB - E60041B3DA0041B3DA0041B3DA0041B3DA00A4787400DBC9C600F6EFE700F6EF - E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF - E700FEDDBB008C5D5C00000000000000000000000000059ACD00059ACD00D1EF - F900000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000B3664100BD580100CB9A8200F8F3EC00BD795800AF4B0200BD7958000000 - 000000000000B4817600AF4B0200BD79580000000000BD795800BD580100AB56 - 2900000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000B4817600D8C5B500F6EFE700F6EF - E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF - E700F6EFE700FEDDBB008C5D5C000000000000000000000000007ECBE6001DA6 - D50060D4F00071EBF90071EBF90071EBF900B4817600E5CDBE00F6EFE700FEDD - BB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FED7AB00F6EF - E700FEDDBB00986B6600000000000000000000000000059ACD0064C1E1004BBD - E000059ACD0041B3DA007ECBE600BEE6F500F0F6FD0000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000B3664100AF4B0200E2BDB30000000000F1E3E200AF4B0200AF4B02000000 - 000000000000AB562900AF4B02000000000000000000E2BDB300AF4B0200AB56 - 2900000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000B4817600E5CDBE00F6EFE700F6EF - E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF - E700F6EFE700FEDDBB008C5D5C0000000000000000000000000041B3DA008DF3 - FE0070D2FD0070D2FD0070D2FD0070D2FD00B4817600DED2CA00F6EFE700FECC - 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00F6EF - E700FEDDBB008C5D5C00000000000000000000000000059ACD004BBDE000D1EF - F90070D2FD0056C6F00037B4E0001DA6D500059ACD002EACD80064C1E100BEE6 - F500DFEFF9000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000BD795800BD580100BD79580000000000F8F3EC00AF4B0200AF4B02000000 - 000000000000AF4B0200AF4B02000000000000000000BD795800CC670100BD79 - 5800000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000BA8E8500E5CDBE00F6EFE700FEE4 - CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7 - AB00F6EFE700FEDDBB008C5D5C0000000000000000000000000041B3DA008DF3 - FE0070D2FD0070D2FD0070D2FD0070D2FD00BA8E8500DED2CA00F6EFE700F6EF - E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF - E700FEDDBB00986B6600000000000000000000000000059ACD00059ACD00F0F6 - FD007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE0056C6F0004BBDE0001DA6 - D500059ACD001DA6D50064C1E10090D1F100D1EFF90000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000E5CDBE00AF4B0200AF4B0200BD795800BD795800BD580100AF4B0200F1E3 - E20000000000A23F0800BD580100BD795800BD795800BD580100AF4B0200DAB4 - A000000000000000000000000000000000000000000000000000A4787400A478 - 7400A4787400A4787400A4787400A4787400BA8E8500DED2CA00F6EFE700FECC - 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC - 9A00F6EFE700FEDDBB008C5D5C0000000000000000000000000041B3DA008DF3 - FE007BDDFE007BDDFE007BDDFE007BDDFE00BA8E8500ECDACC00F6EFE700FEEB - D700FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00F6EF - E700FEDDBB00986B6600000000000000000000000000059ACD0056C6F0009EDE - EE009AE3FF007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE007BDD - FE0070D2FD003A80E00037B4E00037B4E000059ACD001DA6D500ACDBF4000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000BD795800AF4B0200CC670100CC670100CC670100AF4B0200DAB4 - A000E5CDBE00AF4B0200CC670100BD580100CC670100BD580100BD7958000000 - 0000000000000000000000000000000000000000000000000000A4787400FED6 - C900FED6C900FED6C900FED6C900FED6C900BA8E8500E4D1CD00F6EFE700F6EF - E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700FEF0E200F6EF - E700F6EFE700FEDDBB00986B660000000000000000000000000041B3DA0099FD - FE007BDDFE007BDDFE007BDDFE007BDDFE00CB9A8200EADBD400F6EFE700FECC - 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00F6EF - E700FEDDBB00986B6600000000000000000000000000059ACD0070D2FD004BBD - E000D4FDFF0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0071EBF9007BDD - FE00295AF7000335FB004493ED0084E9FE0084E9FE0056C6F00037B4E0000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000CB9A8200A23F0800A23F0800A23F0800A23F0800BD79 - 5800B4817600A23F0800A23F0800A23F0800A23F0800BA8E8500FAF3F2000000 - 0000000000000000000000000000000000000000000000000000A4787400F6EF - E700F6EFE700F6EFE700F6EFE700F6EFE700CB9A8200ECDACC00F6EFE700FED7 - AB00FED7AB00FED7AB00FED7AB00FED7AB00FED7AB00FED7AB00F3D1A300F3D1 - A300F6EFE700FEDDBB00986B660000000000000000000000000041B3DA0099FD - FE0084E9FE0084E9FE0084E9FE0084E9FE00CB9A8200EADBD400F6EFE700F6EF - E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF - E700FEDDBB00986B6600000000000000000000000000059ACD0090D1F1002EAC - D800F9FFFE0084E9FE008DF3FE008DF3FE008DF3FE0084E9FE008DF3FE004571 - FA000335FB000335FB00295AF7008DF3FE008DF3FE0061C6FE0060D4F000BEE6 - F500000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000B4817600AB562900826A - 5B00826A5B00B3664100BD795800000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000A4787400F6EF - E700F6EFE700F6EFE700F6EFE700F8F3EC00CB9A8200EADBD400F6EFE700FECC - 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC - 9A00F6EFE700FEDDBB00986B660000000000000000000000000041B3DA00ADFB - FE0084E9FE0084E9FE0084E9FE0084E9FE00CB9A8200EFE4D800F9F6F500FEDD - BB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00F6EF - E700FEDDBB00A4787400000000000000000000000000059ACD0070D2FD0037B4 - E000ACDBF400ADFBFE008DF3FE008DF3FE008DF3FE0099FDFE004571FA000335 - FB000335FB000335FB000335FB007BDDFE008DF3FE0061C6FE0099FDFE004BBD - E000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000F6EFE700826A5B00927D - 7000B1A09700826A5B00DED7D300000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000B4817600F6EF - E700FEDDBB00FEDDBB00FEDDBB00FED7AB00CB9A8200EFE4D800F6EFE700F6EF - E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF - E700F6EFE700FEDDBB00A478740000000000000000000000000041B3DA00ADFB - FE008DF3FE0084E9FE008DF3FE0084E9FE00DCA88700EFE4D800F9F6F500FECC - 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00F6EF - E700FEDDBB00A4787400000000000000000000000000059ACD009AE3FF0056C6 - F00064C1E100D4FDFF0099FDFE0099FDFE0099FDFE004571FA000335FB000335 - FB005DB2FC00295AF7000335FB005DB2FC0099FDFE0070D2FD00C2FDFF004BBD - E000DFEFF9000000000000000000000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000C3C2C200826A5B00F1E3 - E200C4C4C400826A5B00A4968E00000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000B4817600F6EF - E700FECC9A00FECC9A00FECC9A00FECC9A00DCA88700EFE4D800F9F6F500FEDD - BB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDD - BB00F6EFE700FEDDBB00A478740000000000000000000000000041B3DA00ADFB - FE008DF3FE008DF3FE008DF3FE008DF3FE00DCA88700F5EADF00FFFEFE00FFFF - FF00FFFCFB00FEFAF700FFF8EE00FEF4E900FEF4E900FEF0E200FEEBD700FEE4 - CA00FED0B700A4787400000000000000000000000000059ACD009AE3FF0061C6 - FE0037B4E000F0FEFE0099FDFE00ADFBFE00399CFE000335FB000335FB005DB2 - FC0099FDFE004BAFFF000335FB004571FA0099FDFE0061C6FE00C2FDFF00ADFB - FE0064C1E1000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000ECEAEA00826A5B00CABCB700B1A0 - 9700826A5B00B9AAA600826A5B00DED7D3000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000B4817600F6EF - E700F6EFE700F6EFE700F6EFE700F6EFE700DCA88700EFE4D800F9F6F500FECC - 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC - 9A00F6EFE700FEDDBB00A478740000000000000000000000000041B3DA00C2FD - FF008DF3FE0099FDFE008DF3FE0099FDFE00E3B18E00F5EADF00FFFFFF00FEFE - FE00FFFFFF00FFFCFF00FEF9F300FEF9F300FFF8EE00FEF0E200F3E3D100F3B9 - B500FCA3A200B48176000000000000000000000000001DA6D5009AE3FF0070D2 - FD004BBDE0009EDEEE00F0FEFE00D4FDFF0090D1F100295AF7005DB2FC00C2FD - FF00ADFBFE009AE3FF000335FB000335FB00ADFBFE0061C6FE00D4FDFF00D4FD - FF004BBDE000F0FEFE0000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000A4968E00B9AAA600B9B9B900826A - 5B00927D7000B9AAA600C8ABAB00927D70000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000BA8E8500F8F3 - EC00FEDDBB00FEDDBB00FEDDBB00FEDDBB00DCA88700F5EADF00FFFFFF00FFFF - FE00FFFFFF00FDFAFA00FEFAF700FFF8EE00FEF4E900FEF4E900FEF0E200FEF0 - E200F6EFE700FEDDBB00A478740000000000000000000000000041B3DA00C2FD - FF0099FDFE0099FDFE0099FDFE0099FDFE00E3B18E00FEF0E200FFFEFF00FFFE - FF00FFFFFE00FFFFFF00FFFCFB00FEFAF700FFF8EE00FFF8EE00B4817600B481 - 7600B4817600B48176000000000000000000000000001DA6D5009AE3FF007BDD - FE007BDDFE004BBDE0004BBDE0004BBDE0004BBDE000ACDBF400D1EFF900F0FE - FE00C2FDFF00C2FDFF00295AF7000335FB005DB2FC0061C6FE00E4FDFE00E4FD - FE00BEE6F5007ECBE60000000000000000000000000000000000000000000000 - 0000000000000000000000000000ECEAEA00826A5B00CCCACA00A4968E00C6B8 - AB00DED7D300927D7000C0C0C000927D7000DED7D30000000000000000000000 - 0000000000000000000000000000000000000000000000000000BA8E8500F8F3 - EC00FECC9A00FECC9A00FECC9A00FECC9A00E3B18E00F5EADF00FFFEFF00FFFE - FF00FFFFFE00FFFEFF00FEFAF700FEF9F300FFF8EE00FEF4E900FEF4E900FEE4 - CA00F3B9B500FCA3A200B481760000000000000000000000000041B3DA00D4FD - FF0099FDFE0099FDFE0099FDFE0099FDFE00EDBD9200FEF0E200FFFFFE00FFFE - FF00FFFFFF00FFFFFE00FFFFFF00FCFEFE00FEFAF700FFF8EE00B4817600FCC4 - 7C00DCA88700E2BDB3000000000000000000000000001DA6D5009AE3FF0084E9 - FE0084E9FE0084E9FE0084E9FE0084E9FE007BDDFE0060D4F00037B4E00064C1 - E100F8FBFB00E4FDFE0098A9EF000335FB00295AF70070D2FD00F0FEFE00F0FE - FE00F0FEFE004BBDE00000000000000000000000000000000000000000000000 - 0000000000000000000000000000B9AAA600B9AAA600C7C7C700927D70000000 - 000000000000A18C8200B1A09700B9AAA600B1A0970000000000000000000000 - 0000000000000000000000000000000000000000000000000000CB9A8200F9F6 - F500FEF9F300FEF9F300FEF4E900FEF4E900E3B18E00F5EADF00FFFFFF00FCFE - FE00FFFFFF00FFFEFF00FEFFFC00FFFCFB00FEF9F300FFF8EE00FFF8EE00B481 - 7600B4817600B4817600B481760000000000000000000000000041B3DA00D4FD - FF0099FDFE00ADFBFE0099FDFE00ADFBFE00EDBD9200FEF4E900FFFFFE00FEFE - FE00FBFBFC00FBFBFC00FAFAFA00FAFAFA00F9F6F500F8F3EC00B4817600E3B1 - 8E00D8C5B500000000000000000000000000000000001DA6D500ADFBFE008DF3 - FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE0060D4 - F0004BBDE0009EDEEE00ACDBF4000335FB000335FB00ACDBF400FFFFFE00FFFE - FF00FFFCFB00BEE6F5004BBDE000000000000000000000000000000000000000 - 0000000000000000000000000000927D7000F3F3F300927D7000DED7D3000000 - 000000000000ECEAEA00927D7000E1E0E000826A5B0000000000000000000000 - 0000000000000000000000000000000000000000000000000000CB9A8200FAFA - FA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00EDBD9200FEF0E200FFFEFF00FFFF - FF00FFFFFE00FCFEFE00FFFFFF00FEFEFE00FFFDF800FEF9F300FEFAF700B481 - 7600FCC47C00CB9A8200E2BDB30000000000000000000000000041B3DA00D4FD - FF00ADFBFE00ADFBFE00ADFBFE00ADFBFE00EDBD9200DCA88700DCA88700DCA8 - 8700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700B4817600E5CD - BE0000000000000000000000000000000000000000001DA6D500ADFBFE0099FD - FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD - FE0099FDFE0071EBF90060D4F0004571FA000335FB00295AF7004BBDE0004BBD - E0004BBDE0004BBDE0004BBDE000000000000000000000000000000000000000 - 00000000000000000000E1E0E000A18C8200C6B8AB00B1A09700000000000000 - 00000000000000000000C6B8AB00B1A09700B9AAA600C3C2C200000000000000 - 0000000000000000000000000000000000000000000000000000DCA88700FBFB - FC00FECC9A00FECC9A00FECC9A00FECC9A00EDBD9200FEF0E200FCFEFB00FCFE - FB00F8FBFB00F9F6F500F9F6F500F3F3F300F3F3F300EFEFEF00EFEFEF00B481 - 7600E3B18E00D8C5B5000000000000000000000000000000000041B3DA00E4FD - FE00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FD - FF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF0090D1F100059A - CD0000000000000000000000000000000000000000001DA6D500ADFBFE0099FD - FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD - FE0099FDFE0099FDFE0099FDFE0070D2FD000335FB000335FB00000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000B1A09700B1A09700826A5B0000000000000000000000 - 0000000000000000000000000000927D7000B1A09700A18C8200000000000000 - 0000000000000000000000000000000000000000000000000000DCA88700FBFB - FC00FFFEFF00FFFFFF00FFFDF800FEF9F300EDBD9200DCA88700DCA88700DCA8 - 8700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700B481 - 7600E5CDBE00000000000000000000000000000000000000000041B3DA00E4FD - FE00D4FDFF00C2FDFF009EDEEE009EDEEE009EDEEE009AE3FF009EDEEE009AE3 - FF009EDEEE009AE3FF009EDEEE009EDEEE00D4FDFF00D4FDFF0090D1F100059A - CD0000000000000000000000000000000000000000001DA6D500E4FDFE0099FD - FE0099FDFE0099FDFE0099FDFE0099FDFE00F0FEFE00BEE6F500BEE6F500E4FD - FE00D4FDFF00C2FDFF00ADFBFE00ADFBFE00295AF7000335FB0098A9EF00F0F6 - FD00000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000927D7000826A5B00DED2CA0000000000000000000000 - 0000000000000000000000000000E1E0E000826A5B00826A5B00000000000000 - 0000000000000000000000000000000000000000000000000000E3B18E00FBFB - FC00FFFFFF00FFFFFE00FEFEFE00FFFDF800FEFAF700FFF8EE00FEF4E900FEF0 - E200FEEBD700D6A89400D6A89400B48176000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000041B3DA00F0FE - FE00D4FDFF009BC3D800A18C8200A18C8200A18C8200A18C8200A18C8200A18C - 8200A18C8200A18C8200A18C8200A18C820090D1F100D4FDFF0090D1F100059A - CD0000000000000000000000000000000000000000002EACD80090D1F100D4FD - FF00D4FDFF00C2FDFF00ADFBFE00E4FDFE0064C1E1001DA6D5001DA6D5001DA6 - D5001DA6D5001DA6D5001DA6D5001DA6D5001DA6D5000335FB004571FA000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000FAF3F200826A5B00927D70000000000000000000000000000000 - 000000000000000000000000000000000000B1A097006F5B4D00DED7D3000000 - 0000000000000000000000000000000000000000000000000000E3B18E00FFFE - FE00FFFFFE00FFFFFF00FFFFFF00FFFFFF00FFFCFB00FEFAF700FFF8EE00FFF8 - EE00B4817600B4817600B4817600B48176000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000041B3DA00F0FE - FE00E4FDFE00B1A09700DED7D300DED7D300DED7D300DED7D300DED7D300DED7 - D300DED7D300DED7D300DED7D3008C8988004BBDE000E4FDFE0090D1F100059A - CD000000000000000000000000000000000000000000000000001DA6D5007ECB - E6007ECBE6009EDEEE00BEE6F500ACDBF4001DA6D50000000000000000000000 - 00000000000000000000000000000000000000000000CED7FA00DFEFF9000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000DED7D3006F5B4D00ECEAEA000000000000000000000000000000 - 00000000000000000000000000000000000000000000826A5B00CABCB7000000 - 0000000000000000000000000000000000000000000000000000EDBD9200FFFE - FE00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFEFE00FFFDF800FEFAF700FFF8 - EE00B4817600FCC47C00DCA88700E2BDB3000000000000000000000000000000 - 00000000000000000000000000000000000000000000000000007ECBE6002EAC - D80099FDFE009BC3D800A4968E00C6B8AB00FFFFFF00FFFFFF00FCFEFE00FFFE - FF00FFFFFE00DED7D300A4968E008791850041B3DA007ECBE600059ACD00DFEF - F900000000000000000000000000000000000000000000000000000000001DA6 - D5001DA6D5001DA6D5001DA6D5001DA6D5000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000ECEAEA00DBC9C600000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000DED7D300DED7D3000000 - 0000000000000000000000000000000000000000000000000000EDBD9200FCFE - FE00FFFCFF00FEFEFE00FCFEFB00FAFAFA00F8F8F800F8F8F800F9F6F500F8F3 - EC00B4817600E3B18E00D8C5B500000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000000000000000000090D1 - F10041B3DA0041B3DA0041B3DA00A18C8200CABCB700CABCB700CABCB700CABC - B700CABCB700B9AAA60066A6C30041B3DA0041B3DA004BBDE000BEE6F5000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000EDBD9200DCA8 - 8700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 - 8700B4817600E5CDBE0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000A18C8200A18C8200A18C8200A18C8200A18C - 8200A18C8200A18C8200DBC9C600000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000CED7FA00295AF70098A9EF00000000000000000000000000000000000000 - 000000000000000000000000000000000000EADBD400D3936400000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000FAF3F200A23F0800DAB4A00000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 - 74008C5D5C000000000000000000000000000000000000000000D0E3EC00D1CD - E400ECEAEA000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000CED7 - FA000732DE000732DE000732DE00000000000000000000000000000000000000 - 0000000000000000000000000000EADBD400A23F0800A23F0800D39364000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000AB562900BD580100A23F0800DAB4A000000000000000 - 000000000000000000000000000000000000000000000000000000000000A478 - 7400ECDACC00FED6C900FED6C900FED6C900FED6C900FED6C900FED6C900FED6 - C900FED0B700FECFC200FECFC200FED0B700FED0B700FED0B700FED0B700FED0 - B7008C5D5C0000000000000000000000000000000000F0F6FD000732DE000732 - DE000732DE00C7DDE10000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000D1CDE4000732 - DE000732DE000732DE00CED7FA00000000000000000000000000000000000000 - 00000000000000000000EADBD400AF4B0200CC670100CC670100A23F0800DAB4 - A000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000BD795800BD580100CC670100CC670100A23F0800DAB4A0000000 - 000000000000000000000000000000000000000000000000000000000000A478 - 7400F3E3D100FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD - BB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FECC9A00FED0 - B7008C5D5C0000000000000000000000000000000000D1CDE4000732DE000732 - DE000732DE000732DE00CED7FA00000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000098A9EF000732DE000732 - DE000732DE00CED7FA0000000000000000000000000000000000000000000000 - 000000000000EADBD400A23F0800BD580100CC670100CC670100AF4B0200D6A8 - 9400000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000DAB4A000A23F0800CC670100CC670100CC670100A23F0800E5CD - BE0000000000000000000000000000000000000000000000000000000000A478 - 7400EFE4D800FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD - BB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FED0 - B7008C5D5C0000000000000000000000000000000000F0F6FD000732DE000732 - DE000732DE000732DE000732DE00BEC6DC000000000000000000000000000000 - 00000000000000000000000000000000000098A9EF000732DE000732DE000732 - DE00CED7FA000000000000000000000000000000000000000000000000000000 - 0000F8F0F000AB562900BD580100CC670100CC670100AF4B0200DAB4A0000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 00000000000000000000DAB4A000A23F0800CC670100CC670100CC670100A23F - 0800FAF3F200000000000000000000000000000000000000000000000000A478 - 7400F1E3E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FED6C900FEDD - BB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED0 - B7008C5D5C000000000000000000000000000000000000000000F3F3F300295A - F7000732DE000732DE000732DE000732DE0098A9EF0000000000000000000000 - 00000000000000000000000000006682F1000732DE000732DE000732DE00CED7 - FA00000000000000000000000000000000000000000000000000000000000000 - 0000D3936400AF4B0200CC670100CC670100AF4B0200BD795800000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000DAB4A000A23F0800CC670100CC670100BD58 - 0100B3664100000000000000000000000000000000000000000000000000B481 - 7600F5EADF00FEF0E200FEEBD700FEEBD700FEE4CA00F3E3D100CCC0CC0098A9 - EF00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FECF - C2008C5D5C000000000000000000000000000000000000000000000000000000 - 00006682F1000732DE000732DE000732DE000732DE0098A9EF00000000000000 - 000000000000000000006682F1000732DE000732DE000732DE00CED7FA000000 - 000000000000000000000000000000000000000000000000000000000000E5CD - BE00A23F0800CC670100CC670100AF4B0200BD79580000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000CB9A8200AF4B0200CC670100CC67 - 0100A23F0800E2BDB3000000000000000000000000000000000000000000B481 - 7600F5EADF00FEF0E200FEF0E200FEEBD700EFE4D8006682F1000335FB000335 - FB00CFC4D600FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED0 - B7008C5D5C000000000000000000000000000000000000000000000000000000 - 00000000000098A9EF000732DE000732DE000732DE000732DE0098A9EF000000 - 0000F3F3F300295AF7000732DE000732DE000732DE00CED7FA00000000000000 - 000000000000000000000000000000000000000000000000000000000000B366 - 4100BD580100CC670100BD580100AB562900F8F0F00000000000ECDACC00A23F - 0800AF4B0200AF4B0200AF4B0200AF4B0200AF4B0200AF4B0200AF4B0200AF4B - 0200AF4B0200AF4B0200B3664100000000000000000000000000B3664100BD58 - 0100BD580100BD580100BD580100BD580100BD580100BD580100BD580100BD58 - 0100BD580100A23F0800E2BDB3000000000000000000B3664100BD580100CC67 - 0100CC670100AB5629000000000000000000000000000000000000000000B481 - 7600F6EFE700FEF4E900FEF0E20098A9EF000335FB000335FB000335FB000335 - FB00295AF700FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED0 - B700986B66000000000000000000000000000000000000000000000000000000 - 00000000000000000000CED7FA00295AF7000732DE000732DE000732DE0098A9 - EF00295AF7000732DE000732DE000732DE00CED7FA0000000000000000000000 - 0000000000000000000000000000000000000000000000000000E5CDBE00AF4B - 0200CC670100CC670100A23F0800E5CDBE000000000000000000CB9A8200BD58 - 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 - 0100CC670100CC670100B3664100000000000000000000000000B3664100CC67 - 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 - 0100CC670100BD580100CB9A82000000000000000000F5EADF00A23F0800CC67 - 0100CC670100AF4B0200DAB4A00000000000000000000000000000000000B481 - 7600F8F3EC00FEF4E90098A9EF000335FB000335FB000335FB0098A9EF000335 - FB000335FB00A9ACD400FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FECF - C200986B66000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000F0F6FD004571FA000732DE000335FB000732 - DE000732DE000732DE000732DE00CED7FA000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000BD795800CC67 - 0100E27E0300BD580100BD795800000000000000000000000000D6A89400AF4B - 0200BD580100BD580100BD580100BD580100BD580100CC670100CC670100CC67 - 0100CC670100CC670100B3664100000000000000000000000000B3664100E27E - 0300E27E0300CC670100CC670100BD580100AF4B0200AF4B0200AF4B0200AF4B - 0200AF4B0200A23F0800ECDACC00000000000000000000000000B4817600BD58 - 0100CC670100BD580100BD79580000000000000000000000000000000000BA8E - 8500F8F3EC00FFF8EE00ECEAEA000335FB006682F100F1E3E200FEEBD7006682 - F1000335FB000335FB00FED6C900FEE4CA00FEDDBB00FEDDBB00FEDDBB00FED6 - C900986B66000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000006682F1000732DE000732 - DE000335FB000732DE00CED7FA00000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000AF4B0200E27E - 0300E27E0300AF4B0200F5EADF0000000000000000000000000000000000D6A8 - 9400CB9A8200CB9A8200CB9A8200D3936400A23F0800BD580100CC670100CC67 - 0100CC670100CC670100B3664100000000000000000000000000B3664100E27E - 0300E27E0300E27E0300CC670100CC670100A23F0800D6A89400E5CDBE00E5CD - BE00E5CDBE00F1E3E20000000000000000000000000000000000F5EADF00AF4B - 0200CC670100CC670100A23F080000000000000000000000000000000000BA8E - 8500FAF3F200FEF9F300FFF8EE00FEF4E900FEF4E900FEF0E200FEF0E200EFE4 - D8000335FB000335FB006682F100FEE4CA00FEE4CA00FEE4CA00FEDDBB00FED6 - C900986B66000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000BEC6DC000732DE000335FB000732 - DE000732DE000335FB006682F100000000000000000000000000000000000000 - 00000000000000000000000000000000000000000000ECDACC00BD580100FB9B - 0F00E27E0300AB56290000000000000000000000000000000000000000000000 - 00000000000000000000F8F0F000AB562900BD580100CC670100CC670100CC67 - 0100CC670100CC670100B3664100000000000000000000000000B3664100E27E - 0300E27E0300E27E0300E27E0300E27E0300CC670100A23F0800EADBD4000000 - 000000000000000000000000000000000000000000000000000000000000AB56 - 2900CC670100CC670100AF4B0200ECDACC00000000000000000000000000CB9A - 8200F9F6F500FFFCFB00FEF9F300FFF8EE00FEF4E900FEF4E900FEF0E200FEF0 - E20098A9EF000335FB000335FB00CFC4D600FEE4CA00FEE4CA00FEE4CA00FED6 - C900986B66000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000098A9EF000732DE000335FB000732DE000335 - FB00295AF7000335FB000732DE006682F1000000000000000000000000000000 - 00000000000000000000000000000000000000000000E5CDBE00CC670100FB9B - 0F00E27E0300BD79580000000000000000000000000000000000000000000000 - 000000000000FAF3F200AB562900BD580100CC670100CC670100BD580100AF4B - 0200CC670100CC670100B3664100000000000000000000000000B3664100FB9B - 0F00E27E0300AF4B0200AF4B0200E27E0300E27E0300CC670100A23F0800EADB - D40000000000000000000000000000000000000000000000000000000000B366 - 4100CC670100CC670100AF4B0200E5CDBE00000000000000000000000000CB9A - 8200FDFAFA00FFFDF800FEFAF700FEF9F300FFF8EE00FEF4E900FEF4E900FEF0 - E200F5EADF004571FA000335FB00295AF700FEEBD700FEE4CA00FEE4CA00FED6 - C900A47874000000000000000000000000000000000000000000000000000000 - 0000000000000000000098A9EF000335FB000335FB000732DE000335FB00CED7 - FA00F0F6FD006682F1000335FB000335FB006682F10000000000000000000000 - 00000000000000000000000000000000000000000000E5CDBE00CC670100FB9B - 0F00FB9B0F00B366410000000000000000000000000000000000000000000000 - 0000FAF3F200AB562900BD580100CC670100CC670100BD580100AB562900AF4B - 0200CC670100CC670100B3664100000000000000000000000000B3664100FB9B - 0F00FB9B0F00AF4B0200DAB4A000AF4B0200E27E0300E27E0300CC670100A23F - 0800E5CDBE00000000000000000000000000000000000000000000000000AB56 - 2900CC670100CC670100AF4B0200F1E3E200000000000000000000000000CB9A - 8200FAFAFA00FFFFFF00FFFCFB00FEFAF700FEF9F300FFF8EE00FEF4E900FEF4 - E900FEF0E200D1CDE4000335FB000335FB00CFC4D600FEE4CA00FEE4CA00FED6 - C900A47874000000000000000000000000000000000000000000000000000000 - 0000000000006682F1000335FB000732DE000335FB000335FB00CED7FA000000 - 0000000000000000000098A9EF000335FB000335FB006682F100000000000000 - 00000000000000000000000000000000000000000000F8F3EC00AF4B0200FCB0 - 4C00FB9B0F00AF4B0200F1E3E20000000000000000000000000000000000E5CD - BE00A23F0800BD580100CC670100CC670100BD580100BD795800E5CDBE00AF4B - 0200CC670100CC670100B3664100000000000000000000000000B3664100FB9B - 0F00FB9B0F00AF4B020000000000DAB4A000BD580100E27E0300E27E0300E27E - 0300A23F0800CB9A820000000000000000000000000000000000DAB4A000AF4B - 0200CC670100CC670100A23F080000000000000000000000000000000000DCA8 - 8700F8FBFB00FFFFFF00FFFFFF00FFFDF800FEFAF700FEF9F300FFF8EE00FEF4 - E900FEF0E200FEF0E20098A9EF00E2D6E200FEEBD700FEEBD700FEE4CA00FECF - C200A47874000000000000000000000000000000000000000000000000000000 - 00006682F1000335FB000335FB000335FB000335FB00CED7FA00000000000000 - 0000000000000000000000000000CED7FA00295AF7000335FB006682F1000000 - 0000000000000000000000000000000000000000000000000000AB562900FB9B - 0F00FCB04C00E27E0300AB562900F1E3E20000000000F5EADF00BD795800AF4B - 0200E27E0300E27E0300CC670100BD580100BD79580000000000E5CDBE00AF4B - 0200CC670100CC670100B3664100000000000000000000000000B3664100FCB0 - 4C00FB9B0F00AF4B02000000000000000000DAB4A000AF4B0200E27E0300E27E - 0300E27E0300BD580100AB562900D6A89400E5CDBE00CB9A8200A23F0800CC67 - 0100CC670100BD580100BD79580000000000000000000000000000000000DCA8 - 8700FBFBFC00FCFEFE00FFFFFE00FFFFFF00FFFCFB00FEFAF700FEF9F300FFF8 - EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700FED6C900FECFC200F3B9 - B500A47874000000000000000000000000000000000000000000F0F6FD00295A - F7000335FB000335FB000335FB000335FB00CED7FA0000000000000000000000 - 000000000000000000000000000000000000F0F6FD004571FA000335FB004571 - FA00000000000000000000000000000000000000000000000000DAB4A000C56E - 2600FCB04C00FCB04C00E27E0300BD580100AF4B0200BD580100CC670100E27E - 0300E27E0300E27E0300AF4B0200BD7958000000000000000000E5CDBE00AF4B - 0200CC670100CC670100B3664100000000000000000000000000B3664100FCB0 - 4C00FCB04C00AF4B0200000000000000000000000000DAB4A000AF4B0200E27E - 0300E27E0300E27E0300E27E0300BD580100BD580100BD580100CC670100CC67 - 0100CC670100A23F0800F5EADF0000000000000000000000000000000000DCA8 - 8700FCFEFE00FFFEFF00FFFEFF00FFFFFF00FFFEFE00FFFCFB00FEF9F300FFF8 - EE00FFF8EE00FEF4E900FEF4E900FEF0E200FED6C900F3B9B500FCA3A200FCA3 - A200B481760000000000000000000000000000000000F0F6FD00295AF7000335 - FB000335FB000335FB000335FB00CED7FA000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000098A9EF000335 - FB00CED7FA00000000000000000000000000000000000000000000000000B366 - 4100D3936400FCB04C00FCB04C00FCB04C00FB9B0F00FB9B0F00FB9B0F00FB9B - 0F00E27E0300A23F0800D6A89400000000000000000000000000E5CDBE00AF4B - 0200CC670100CC670100B3664100000000000000000000000000B3664100FCB0 - 4C00FCC47C00AF4B020000000000000000000000000000000000ECDACC00AB56 - 2900CC670100E27E0300E27E0300E27E0300E27E0300E27E0300CC670100CC67 - 0100A23F0800DAB4A0000000000000000000000000000000000000000000E3B1 - 8E00FCFEFB00FFFFFE00FFFEFF00FEFEFE00FFFFFE00FEFEFE00FFFDF800FEFA - F700FFF8EE00FFF8EE00FEF4E900B4817600B4817600B4817600B4817600B481 - 7600B4817600000000000000000000000000000000006682F1000335FB000335 - FB000335FB000335FB00CED7FA00000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000F8F3 - EC00BD795800C56E2600FCB04C00FCB04C00FCB04C00FB9B0F00E27E0300BD58 - 0100AB562900ECDACC000000000000000000000000000000000000000000AB56 - 2900AF4B0200AF4B0200E2BDB300000000000000000000000000E5CDBE00AB56 - 2900AF4B0200CB9A82000000000000000000000000000000000000000000FAF3 - F200CB9A8200AF4B0200CC670100CC670100E27E0300CC670100BD580100A23F - 0800DAB4A000000000000000000000000000000000000000000000000000E3B1 - 8E00FFFEFE00FFFFFE00FFFFFF00FFFEFF00FEFEFE00FFFEFF00FFFEFF00FDFA - FA00FEFAF700FFF8EE00FFF8EE00B4817600E3B18E00FCB04C00FB9B0F00D393 - 6400CB9A8200000000000000000000000000000000006682F1000335FB000335 - FB000335FB00CED7FA0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000DAB4A000BD795800AF4B0200AB562900A23F0800BD795800E2BD - B30000000000000000000000000000000000000000000000000000000000F8F0 - F000DAB4A000ECDACC0000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000F8F3EC00DAB4A000D3936400B3664100BD795800DAB4A000F8F3 - EC0000000000000000000000000000000000000000000000000000000000EDBD - 9200FFFCFF00FFFFFF00FFFFFF00FFFEFF00FFFFFE00FFFFFF00FFFFFF00FFFF - FE00FEFAF700FEFAF700FFF8EE00B4817600EAC8A100FCC47C00CB9A8200DCA8 - 8700F6EFE70000000000000000000000000000000000CED7FA004571FA004571 - FA00CED7FA000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200FFFFFE00FFFEFF00FFFFFF00FEFEFE00FFFFFF00FEFEFE00FFFFFF00FFFF - FE00FEFEFE00FEFAF700FEFAF700B4817600EAC8A100E3B18E00DCA88700F6EF - E700000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200FEF0E200FEF0E200F5EADF00F5EADF00EFE4D800EFE4D800EADBD400EADB - D400ECDACC00E4D1CD00DED2CA00B4817600DCA88700D6A89400F6EFE7000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 000000000000000000000000000000000000000000000000000000000000EDBD - 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 - 8700DCA88700DCA88700DCA88700B4817600DCA88700F6EFE700000000000000 - 000000000000000000000000000000000000424D3E000000000000003E000000 - 2800000060000000A80000000100010000000000E00700000000000000000000 - 000000000000000000000000FFFFFF00FF003FFFFFFFFFFFFF000000FC000FE0 - 0007E00007000000F00003E00007C00007000000E00000E00007C00007000000 - C00000E00007C00007000000800001E00007E00007000000800001E00007E000 - 07000000800001E00007E00007000000800001E00007E00007000000800001E0 - 0007E00007000000800001E00007E00007000000C00003E00007E00007000000 - E00003E00007E00007000000F80007E00007E00007000000F8001FE00007E000 - 07000000FC007FE00007E00007000000FC00FFE00007E00007000000F800FFE0 - 0007E00007000000F800FFE00007E00007000000F801FFE00007E00007000000 - F001FFE0000FE0000F000000F001FFE0001FE0001F000000E003FFE0003FE000 - 3F000000E003FFE0007FE0007F000000FFFFFFFFFFFFFFFFFFFFFFFFE7FFFFFF - FFFFC3FFFFC00003E07FFFF81FFFC03FFF800003E007FFF800FFC003FF800003 - 80007FF8000FC0007F80000380000F80000FC0007F800003800007800007C000 - 3F800003800007800007C0003F800003800007800003C0001F80000380000380 - 0003C0001F800003800003800001C0000F800003800001800001C0000F800003 - 800001800001C00007800003800000800001C0000780000380000080001FC000 - 0780000380000080001FC0000380000380001F80001FC0780180000380001F80 - 00FFE0FF0F800003E0001F8000FFFFFE0F800003E0007F8000FFFFFE0F800003 - FC00FF80FFFFFFF81F800003FC01FFC0FFFFFF803F800003FFFFFFFFFFFFFFC0 - FF800003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF807FC0 - FFFFF0FFFFC3FEFFFE001FC00FFFF007FFC03E7FFC000FC000FFF0007FC0001F - F80007C0000FF0001FC0000FF00003C00003F0001FC00007E00001C00003F000 - 0FC00003C00001C00003F0000FC00001C00000C00003F00007C00001800000C0 - 0003F00007C00003000000C00003F00007C00003000000C00003F00003C00003 - C00000C00003F00003C00001C00000C00003F00003C00001E00000C00003F000 - 3FC00001E00001C00003F0003FC00001E00001C00003F0003FC0001FF00001E0 - 0003FF8003C0001FF00003F00003FF8007C0001FF80007FC0003FFC00FE000FF - FC000FFE000FFFE03FF801FFFF003FFF001FFFE07FF803FFFFC0FFFF807FFFF1 - FFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFBFFCFE00003FE - 03FF07FFFFF0FF0FE00003FC003F03FFFFF07E07E00003F0000301FFFFF03C0F - E00003E0000180FFFFF0180FE00003C00001C07FFFF8000FE00003800001E03F - FFF8001FE00003800001F0301FF8001FE00003800001F8000FFC001FE0000380 - 0001FC0007FC003FE00003800001FE0003F8000FE00003800001FF0001E00007 - E00003800000FF0001800001E00003800000FE0000000000E00003000001FE00 - 00000000E00003000003FE0000000000E00003800007FE00000380E0E00003C0 - 000FFE0000FF81FFE00003E0001FFF0001FF81FFE00003F0003FFF0003FF81FF - E00003FC003FFF8003FFC1FFE00007FC003FFFC007FFC3FFE0000FFE1C3FFFE0 - 1FFFC3FFE0001FFFFC3FFFFFFFFFE3FFFFFFFFFF81FFFF81FFFFFFFFFFFFFFFC - 007FFE003FE00003000001F8001FF8001FE00003000001E0000FF0000FE00003 - 000001E00007E00007E00003000001C00003C00003E00003000001800003C000 - 01E00003000001800001800001E00003000001800001800001E0000300000100 - 0001800000E00003000001000001800000E00003000001000001800000E00003 - 000001000001800000E00003000001000001800000E000030000010000018000 - 01E00003000001800001800001E00003000001800001800001E00003000001C0 - 0003C00003E00003000001C00007E00003E00003000001E00007E00007E00003 - 000001F0000FF0000FE00003FFFFFFF8003FFC001FE00007FFFFFFFE007FFE00 - 7FE0000FFFFFFFFFFFFFFFFFFFE0001FF87E1FFFFFFFFF0003FFFFFFF03C0FFF - 0001FF0003FFFFFFF0180FFF0001E000038FFFFFF0188FFF0001C00003807FFF - F1198FFF0001C000038007FFF1198FFF0001C0000380007FF0080FC00001C000 - 0380001FF8001FC00001C0000380001FFC001FC00001C0000380000FFF81FFC0 - 0001C0000380000FFF81FFC00001C00003800007FF81FFC00001C00003800007 - FF00FFC00001C00003800003FF00FFC00001C00003800003FE007FC00001C000 - 03800003FE187FC00001C00007800001FE187FC00001C0000F800001FC3C3FC0 - 0003C0000F80003FFC7E3FC00007C0000F80000FFC7E3FC000FFC0000F80001F - F8FF1FC000FFC0000FC07F9FF8FF9FC000FFC0000FE0FFFFF9FF9FC001FFE000 - 1FFFFFFFFFFFFFC003FFFE01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1FF - 3FFFFFFC7FE00007C7FFE1FE1FFFFFFC3FE0000783FFC1FC0FFFFFF81FE00007 - 81FF83F80FFFFFF80FE0000780FF07F01FFFFFFC07E00007C07E0FF03FFFFFFE - 07E00007F03C1FE07FFFFFFF03E00007F8103FE04001C00183E00007FC007FC0 - C001C00181E00007FE00FFC1C001C001C1E00007FF81FFC1E001C003C1E00007 - FF01FF83FC01C01FE0E00007FE00FF83F801C00FE0E00007FC007F83F001C007 - E0E00007F81C3F81E001C203C1E00007F03E1FC08041C30001E00007C07F0FC0 - 00C1C38001E0000780FFC7E001C1C3C003E0000781FFFFE003E1C3E007E00007 - 83FFFFF80FE3FFF80FE0000787FFFFFFFFFFFFFFFFE0000FFFFFFFFFFFFFFFFF - FFE0001FFFFFFFFFFFFFFFFFFFE0003F00000000000000000000000000000000 - 000000000000} - end -end +object FormModelView: TFormModelView + Left = 0 + Top = 0 + Caption = 'Charity Events' + ClientHeight = 532 + ClientWidth = 872 + Color = clBtnFace + Font.Charset = ANSI_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Segoe UI' + Font.Style = [] + OldCreateOrder = False + Position = poScreenCenter + ShowHint = True + OnCreate = FormCreate + PixelsPerInch = 96 + TextHeight = 13 + object Label1: TLabel + Left = 520 + Top = 456 + Width = 313 + Height = 57 + AutoSize = False + Caption = + 'Click on a column header to sort. See design notes at the top of' + + ' source files.' + Font.Charset = ANSI_CHARSET + Font.Color = clWindowText + Font.Height = -13 + Font.Name = 'Segoe UI' + Font.Style = [] + ParentFont = False + WordWrap = True + end + object btDisplayStars: TButton + Left = 48 + Top = 480 + Width = 201 + Height = 25 + Caption = 'Display Star Events Only' + TabOrder = 0 + OnClick = btDisplayStarsClick + end + object btDisplayAll: TButton + Left = 288 + Top = 480 + Width = 201 + Height = 25 + Caption = 'Display All' + TabOrder = 1 + OnClick = btDisplayAllClick + end + object VST: TVirtualStringTree + Left = 0 + Top = 0 + Width = 872 + Height = 441 + Align = alTop + DefaultNodeHeight = 21 + Font.Charset = ANSI_CHARSET + Font.Color = clWindowText + Font.Height = -16 + Font.Name = 'Segoe UI' + Font.Style = [] + Header.AutoSizeIndex = 0 + Header.Height = 21 + Header.MainColumn = -1 + Header.Options = [hoColumnResize, hoDrag, hoShowHint, hoShowSortGlyphs] + HintMode = hmTooltip + Images = ImageList1 + ParentFont = False + TabOrder = 2 + Columns = <> + end + object ImageList1: TImageList + Height = 24 + Width = 24 + Left = 16 + Top = 376 + Bitmap = { + 494C01011B003000480018001800FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 + 000000000000360000002800000060000000A8000000010020000000000000FC + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000C4C4C4009996970099969700D1D1 + D100F9F6F500FBFBFC00DBC9C600B5B3B300CFCFCF00DAD9D900000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000CCCACA00B9B9B900DAD9D900DFDFDF00AFAFAF00B5B3 + B30099969700BA8E8500A18C8200AFAFAF00DAD9D900C7C7C700BFBFBF00D1D1 + D10000000000000000000000000000000000000000000000000000000000A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 74008C5D5C00000000000000000000000000000000000000000000000000A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 74008C5D5C000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000ECEAEA00B9B9B900CFCFCF00EFEFEF00EFEFEF00DAD9D900AFAFAF00B5B3 + B3008F8F8F004F4C4C0060606000606060008C89880099969700D1D1D100D1D1 + D100B9B9B900CCCACA000000000000000000000000000000000000000000A478 + 7400FED6C900FED6C900FED6C900FED6C900FED6C900FED6C900FED6C900FED6 + C900FECFC200FECFC200FECFC200FED0B700FED0B700FED0B700FED0B700FED0 + B7008C5D5C000000000000000000000000000000000000000000B5B3B300897D + 8B00CC9A9900CABCB700FEE4CA00FEE4CA00FEE4CA00FED6C900FEE4CA00FED6 + C900FEDDBB00FEDDBB00FEDDBB00FED0B700FED0B700FED0B700FED0B700FED0 + B7008C5D5C000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000BFBF + BF00C0C0C000ECEAEA00F3F3F300EFEFEF00ECEAEA00D1D1D100A8A8A800AFAF + AF008F8F8F004F4C4C00434242006060600060606000868585008F8F8F00AFAF + AF00C7C7C700DAD9D900BFBFBF00DAD9D900000000000000000000000000A478 + 7400F3E3D100FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD + BB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FECC9A00FED0 + B7008C5D5C000000000000000000000000000000000000000000458EB6005D71 + AB00AB8FA300C8ABAB00FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4 + CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FECC9A00FED0 + B7008C5D5C000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000B9B9B900DFDF + DF00FAFAFA00F9F6F500EFEFEF00ECEAEA00E1E0E000D1D1D100A8A8A800A8A8 + A800AFAFAF00AFAFAF00A8A8A8008C8988007070700060606000868585008F8F + 8F00B5B3B3009F9F9F00CFCFCF00F3F3F300000000000000000000000000A478 + 7400EFE4D800FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD + BB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FED0 + B7008C5D5C00000000000000000000000000000000000000000061C6FE003A80 + E0005D71AB00AB8FA300D5B3AF00FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4 + CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED0 + B7008C5D5C000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000B9B9B900FEFEFE00FBFB + FC00F9F6F500EFEFEF00ECEAEA00DAD9D900B5B3B3008F8F8F00808080008F8F + 8F00A8A8A800AFAFAF00AFAFAF00B5B3B300B9B9B900AFAFAF009F9F9F006060 + 600051515100CCCACA00E1E0E00000000000000000000000000000000000A478 + 7400F5EADF00FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4 + CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED0 + B7008C5D5C000000000000000000000000000000000000000000000000005DB2 + FC003A80E0005D71AB00AB8FA300D5B3AF00FEEBD700FEEBD700FEE4CA00FEE4 + CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED0 + B7008C5D5C000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000AFAFAF00FBFBFC00F9F6 + F500EFEFEF00DAD9D9009F9F9F009F9F9F00B9B9B900C7C7C7009F9F9F008F8E + 8D0080808000808080008F8F8F009F9F9F00B5B3B300B9B9B900BFBFBF00C3C2 + C200B9B9B900C4C4C400E1E0E00000000000000000000000000000000000B481 + 7600F5EADF00FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4 + CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED0 + B7008C5D5C000000000000000000000000000000000000000000000000008F8F + 8F005DB2FC003A80E0005D71AB00AB8FA300E2BDB300FEEBD700FEEBD700FEE4 + CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED0 + B7008C5D5C000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000A8A8A800F3F3F300CFCF + CF00999697009F9F9F00D1D1D100DAD9D900D1D1D100D1D1D100CCCACA00BFBF + BF00A8A8A800A8A8A8009F9F9F008F8F8F00868585008C8988009F9F9F00B9B9 + B9008DBC8B008DBC8B00E1E0E00000000000000000000000000000000000B481 + 7600F6EFE700FEF0E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4 + CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED0 + B7008C5D5C00000000000000000000000000000000000000000000000000B481 + 7600CABCB7005DB2FC003A80E0005D71AB00B9AAA600F3E3D100E2BDB300BF98 + 9500CDA6A000C8ABAB00E2BDB300FEDDBB00FEDDBB00FEDDBB00FED7AB00FECF + C2008C5D5C000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000009F9F9F008C8988009996 + 9700DAD9D900DFDFDF00DAD9D900D1D1D100CCCACA00ECEAEA00E6E5E400E6E5 + E400ECEAEA00DAD9D900CCCACA00B9B9B900AFAFAF00AFAFAF00999697008685 + 85008080800099969700DFDFDF0000000000000000000000000000000000B481 + 7600F6EFE700FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4 + CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FECF + C2008C5D5C00000000000000000000000000000000000000000000000000B481 + 7600F8F3EC00FEF4E9005DB2FC0085AFCB008F8F8F00BF989500DAB4A000FDE8 + B700FEFED300FDF6C600D8C5B500CC9A9900E5CDBE00FEDDBB00FEDDBB00FED0 + B7008C5D5C000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000008F8F8F00ECEAEA00E1E0 + E000DFDFDF00DAD9D900D1D1D100CFCFCF00E1E0E000DAD9D900B5B3B300B9B9 + B900B9B9B900BFBFBF00CFCFCF00E6E5E400E6E5E400D1D1D100C7C7C700BFBF + BF00B9B9B900A8A8A800DAD9D90000000000000000000000000000000000BA8E + 8500F6EFE700FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEEB + D700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FECF + C200986B6600000000000000000000000000000000000000000000000000B481 + 7600F6EFE700FEF4E900F8F3EC00DED7D300BF989500FED7AB00FDF6C600FEFE + D300FEFFE100FEFFE100FFFDF800F6EFE700BF989500EDD9B700FEDDBB00FECF + C200986B66000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000008C898800E1E0E000DFDF + DF00DAD9D900D1D1D100CFCFCF00DAD9D900E6E5E400C0C0C000EFEFEF00EFEF + EF00ECEAEA00CFCFCF00BFBFBF00B9B9B900B9B9B900BFBFBF00D1D1D100E1E0 + E000DFDFDF00D1D1D100E6E5E40000000000000000000000000000000000BA8E + 8500F8F3EC00FFF8EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700FEEB + D700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FECF + C200986B6600000000000000000000000000000000000000000000000000BA8E + 8500F8F3EC00FFF8EE00FFF8EE00D5B3AF00EAC8A100FDF6C600FDE8B700FEFE + D300FEFFE100FEF9F300FEFFFC00FFFFFF00DED2CA00CDA6A000FEDDBB00FED6 + C900986B66000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000D1D1D100DAD9 + D900D1D1D100CFCFCF00DFDFDF00CFCFCF00C3C2C200F3F3F300F3F3F300EFEF + EF00EFEFEF00EFEFEF00ECEAEA00ECEAEA00DAD9D900C7C7C700B9B9B900B9B9 + B900B9B9B900CCCACA000000000000000000000000000000000000000000BA8E + 8500F8F3EC00FEF9F300FFF8EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEB + D700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FECF + C200986B6600000000000000000000000000000000000000000000000000BA8E + 8500FAF3F200FFF8EE00FEF9F300C8ABAB00FDE8B700FDE8B700FDF6C600FEFE + D300FEFFE100FFFDF800FEFFFC00FFF8EE00FDF6C600BF989500FEDDBB00FED6 + C900986B66000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000E6E5 + E400AFAFAF00BFBFBF00C3C2C200D1D1D100C4C4C400CCCACA00E6E5E400F3F3 + F300EFEFEF00EFEFEF00EFEFEF00ECEAEA00ECEAEA00E6E5E400E6E5E400D1D1 + D100AFAFAF00E6E5E4000000000000000000000000000000000000000000CB9A + 8200F9F6F500FEFAF700FFF8EE00FFF8EE00FEF4E900FEF4E900FEF0E200FEF0 + E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FED6 + C900986B6600000000000000000000000000000000000000000000000000CB9A + 8200F9F6F500FEF9F300FFFDF800CDA6A000FDF6C600FED7AB00FDE8B700FEFE + D300FEFED300FEFFE100FEFFE100FEFFE100FEFED300BF989500FEE4CA00FED6 + C900986B66000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000A8A8A800E6E5E400E6E5E400AFAFAF00B5B3B300B9B9B900B9B9 + B900C3C2C200CCCACA00DAD9D900E6E5E400E6E5E400ECEAEA00CCCACA00B5B3 + B300DFDFDF00000000000000000000000000000000000000000000000000CB9A + 8200F8F8F800FFFDF800FEF9F300FFF8EE00FFF8EE00FEF4E900FEF4E900FEF0 + E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FED6 + C900A4787400000000000000000000000000000000000000000000000000CB9A + 8200F8F8F800FFFCFB00FEFFFC00CDA6A000FDF6C600FDE8B700FDE8B700FEFE + D300FEFED300FEFED300FEFFE100FEFED300FDF6C600BF989500FEE4CA00FED6 + C900A47874000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000A8A8A800FEF9F300FEF0E200ECDACC00D1D1D100C4C4C400C4C4 + C400B9B9B900C0C0C000BFBFBF00B9AAA600B9AAA600B5B3B300DAD9D9000000 + 000000000000000000000000000000000000000000000000000000000000CB9A + 8200FBFBFC00FFFEFE00FFFDF800FEF9F300FFF8EE00FFF8EE00FEF4E900FEF4 + E900FEF0E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FED6 + C900A4787400000000000000000000000000000000000000000000000000CB9A + 8200FCFEFB00FFFEFE00FFFFFF00D5B3AF00EDD9B700FEF0E200FDE8B700FED7 + AB00FDF6C600FDF6C600FDF6C600FDF6C600EDD9B700BF989500FEE4CA00FED6 + C900A47874000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000CB9A8200FED7AB00FED7AB00FED7AB00FEDDBB00FEDD + BB00FEEBD700F3E3D100EFE4D800C8ABAB00DAD9D90000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000DCA8 + 8700F8FBFB00FFFFFF00FFFEFE00FFFCFB00FEFAF700FFF8EE00FFF8EE00FEF4 + E900FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEEBD700FEE4CA00FECF + C200A4787400000000000000000000000000000000000000000000000000DCA8 + 8700FBFBFC00FFFEFF00FFFEFF00DED7D300E2BDB300F9F6F500F9FFFE00FDE8 + B700FDE8B700FED7AB00FDE8B700FDE8B700D6A89400D8C5B500FEE4CA00FED0 + B700A47874000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000D6A89400FEDDBB00FED7AB00FEDDBB00FED7AB00FEDD + BB00FED7AB00FEDDBB00F3D1A300BF9895000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000DCA8 + 8700F8FBFB00FFFFFF00FFFFFE00FFFEFF00FEFAF700FEFAF700FEF9F300FFF8 + EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700FED6C900FECFC200F3B9 + B500A4787400000000000000000000000000000000000000000000000000DCA8 + 8700FBFBFC00FFFFFF00FFFFFF00FFFEFF00D5B3AF00D8C5B500EFE4D800F5EA + DF00FDE8B700FDE8B700EDD9B700D6A89400CDA6A000FED6C900FECFC200F3B9 + B500A47874000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000E6E5E400FED0B700FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDD + BB00FEDDBB00FEDDBB00D5B3AF00BF9895000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000DCA8 + 8700FFFDF800FFFFFF00FFFFFE00FFFFFE00FFFEFE00FEFAF700FEFAF700FFF8 + EE00FFF8EE00FEF4E900FEF4E900FEF0E200FED6C900F3B9B500FCA3A200FCA3 + A200B4817600000000000000000000000000000000000000000000000000DCA8 + 8700FCFEFB00FFFFFE00FEFFFC00FFFEFF00FFFFFF00D6BCBB00CDA6A000DAB4 + A000C8ABAB00DAB4A000CDA6A000E2BDB300FED6C900F3B9B500FCA3A200FCA3 + A200B48176000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000D8C5B500FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4 + CA00FEE4CA00FEE4CA00BF989500EADBD4000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000E3B1 + 8E00FCFEFE00FFFFFF00FFFEFF00FFFFFF00FFFFFF00FEFFFC00FEFAF700FEFA + F700FFF8EE00FFF8EE00FEF4E900B4817600B4817600B4817600A4787400B481 + 7600B4817600000000000000000000000000000000000000000000000000E3B1 + 8E00FCFEFE00FFFFFF00FCFEFE00FFFFFF00FFFFFF00FFFFFF00FFFFFF00EADB + D400EFE4D800F1E3E200FFF8EE00B4817600B4817600B4817600A4787400B481 + 7600B48176000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000BF989500FEEBD700FEEBD700FEEBD700FEEBD700FEEBD700FEEB + D700FEEBD700ECDACC00BF989500000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000E3B1 + 8E00FCFEFE00FFFEFE00FCFEFE00FFFFFF00FFFFFE00FEFEFE00FFFFFF00FDFA + FA00FEFAF700FFF8EE00FFF8EE00B4817600E3B18E00FCB04C00FB9B0F00D393 + 6400D6A89400000000000000000000000000000000000000000000000000E3B1 + 8E00FCFEFE00FFFFFF00FFFEFF00FFFFFF00FFFFFE00FFFFFF00FFFFFE00FFFE + FF00FFFEFE00FEFAF700FEF9F300B4817600E3B18E00FCB04C00FB9B0F00D393 + 6400D6A894000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000ECEAEA00DBC9C600FEF0E200FEF0E200FEF0E200FEF0E200FEF0E200FEF0 + E200FEF0E200D5B3AF00BF989500000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200FFFEFE00FFFEFF00FEFEFE00FFFFFF00FFFFFE00FFFEFF00FCFEFE00FFFF + FF00FEFAF700FEFAF700FFF8EE00B4817600EAC8A100FCC47C00DCA88700D6A8 + 940000000000000000000000000000000000000000000000000000000000EDBD + 9200FEFEFE00FFFFFF00FFFFFE00FFFFFF00FFFFFF00FFFFFF00FFFEFF00FCFE + FE00FEFAF700FEF9F300FEF9F300B4817600EAC8A100FCC47C00DCA88700D6A8 + 9400000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000D6BCBB00FEF4E900FFF8EE00FFF8EE00FFF8EE00FFF8EE00FFF8EE00FFF8 + EE00FEF4E900BF989500E4D1CD00000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200FCFEFE00FFFFFE00FFFFFE00FFFFFF00FFFEFF00FCFEFE00FFFFFF00FFFF + FF00FEFEFE00FEFAF700FEFAF700B4817600EAC8A100E3B18E00D6A894000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200FFFFFF00FFFFFF00FFFFFE00FFFEFE00FFFEFF00FFFFFF00FFFFFE00FFFF + FF00FEFFFC00FFFDF800FEFAF700B4817600EAC8A100E3B18E00D6A894000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000F8F0 + F000DED7D300FEFEFE00FBFBFC00FCFEFB00FCFEFB00FBFBFC00FCFEFB00FCFE + FB00ECEAEA00BF98950000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200FEF0E200FEF0E200F5EADF00F5EADF00EFE4D800EFE4D800EADBD400EADB + D400DED7D300DED2CA00DED2CA00B4817600DCA88700E3B18E00000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200FEF0E200FEF0E200F5EADF00F5EADF00EFE4D800EFE4D800EADBD400EADB + D400EADBD400DED2CA00DED2CA00B4817600DCA88700E3B18E00000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000CABC + B700BF989500BF989500BF989500BF989500BF989500BF989500BF989500BF98 + 9500BF989500F8F0F00000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 + 8700DCA88700DCA88700DCA88700B4817600DCA8870000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 + 8700DCA88700DCA88700DCA88700B4817600DCA8870000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000D1EF + F900F0FEFE000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000059ACD00059A + CD00BEE6F500F0F6FD0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000A3453F00A345 + 3F00994C4D00A3453F00994C4D00B5B3B300B5B3B300B5B3B300B5B3B300B5B3 + B300B5B3B300B5B3B300B5B3B300B5B3B300994C4D00994C4D00994C4D00994C + 4D00994C4D00DAD9D900000000000000000000000000000000000000000041B3 + DA0064C1E10037B4E00064C1E1009EDEEE00DFEFF90000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000059ACD00059ACD00059ACD004BBDE00090D1F100D1EFF9000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000059ACD00C7EF + F10037B4E0001DA6D500059ACD007ECBE600ACDBF400E4FDFE00000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000B4817600994C4D00CB66 + 6600CB666600CB666600994C4D00A8A8A800C4C4C400DAD9D900F8F8F800FAFA + FA00F3F3F300E6E5E400DFDFDF00DAD9D900993434009934340099343400BB57 + 5700CB666600994C4D000000000000000000000000000000000000000000059A + CD00F9FFFE0070D2FD0056C6F0004BBDE0002EACD8002EACD80064C1E1009EDE + EE00D1EFF9000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000059ACD00D1EFF90060D4F0004BBDE0002EACD800059ACD00059A + CD0041B3DA007ECBE600BEE6F500F0FEFE000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000059ACD0090D1 + F1009AE3FF007BDDFE0070D2FD0056C6F00037B4E0001DA6D500059ACD004BBD + E0009EDEEE00DFEFF90000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000B4817600CB666600CB66 + 6600CB666600CB666600994C4D00A4968E00B4817600CC9A9900DBC9C600FAFA + FA00F8F8F800EFEFEF00E6E5E400DFDFDF00993434009934340099343400BB57 + 5700CB666600994C4D00000000000000000000000000A4787400A4787400A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A478740037B4 + E00037B4E0002EACD8004BBDE00090D1F100D1EFF90000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000059ACD0090D1F1009AE3FF007BDDFE007BDDFE007BDDFE0070D2 + FD0056C6F00037B4E0001DA6D500059ACD002EACD8007ECBE600ACDBF400DFEF + F900000000000000000000000000000000000000000000000000059ACD004BBD + E000D1EFF9007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE0060D4 + F0004BBDE0002EACD800059ACD001DA6D5009EDEEE0000000000000000000000 + 00000000000000000000000000000000000000000000B4817600CB666600CB66 + 6600CB666600CB666600994C4D00B1A09700BB575700BB575700CC9A9900DFDF + DF00FAFAFA00F8FBFB00EFEFEF00E6E5E400993434009934340099343400BB57 + 5700CB666600994C4D00000000000000000000000000A4787400F3E3D100FEE4 + CA00FED6C900FED6C900FED0B700FED0B700FED0B700FED7AB00A478740084E9 + FE0084E9FE007BDDFE007BDDFE0060D4F0004BBDE0001DA6D50041B3DA00ACDB + F4000000000000000000000000000000000000000000059ACD00059ACD007ECB + E600BEE6F500059ACD004BBDE000D1EFF90084E9FE0084E9FE0084E9FE0084E9 + FE0084E9FE0084E9FE0084E9FE007BDDFE0060D4F0004BBDE0001DA6D50041B3 + DA00000000000000000000000000000000000000000000000000059ACD0037B4 + E000D1EFF90084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9 + FE0084E9FE0084E9FE0084E9FE0060D4F00037B4E00000000000000000000000 + 00000000000000000000000000000000000000000000B4817600CB666600CB66 + 6600CB666600CB666600994C4D00D5B3AF00BB575700BB575700B4817600C4C4 + C400DFDFDF00FEFAF700FBFBFC00EFEFEF00993434009934340099343400BB57 + 5700CB666600994C4D00000000000000000000000000B4817600FEEBD700FEE4 + CA00FEE4CA00FEDDBB00C7D19500448A3E0005710A0005710A000F8319000571 + 0A002A8C430060D4F00084E9FE0084E9FE007BDDFE0084E9FE007BDDFE0041B3 + DA00DFEFF90000000000000000000000000000000000059ACD009EDEEE004BBD + E0001DA6D500059ACD0037B4E000DFEFF90084E9FE0084E9FE0084E9FE0084E9 + FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0070D2FD004BBD + E000D1EFF9000000000000000000000000000000000000000000059ACD0056C6 + F00090D1F100ADFBFE0084E9FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3 + FE008DF3FE008DF3FE008DF3FE007BDDFE007BDDFE00059ACD00000000000000 + 00000000000000000000000000000000000000000000B4817600CB666600CB66 + 6600CB666600CB666600994C4D00DBC9C600A3453F00A3453F00A4787400A8A8 + A800C4C4C400DFDFDF00FAFAFA00FAFAFA00993434009934340099343400BB57 + 5700CB666600994C4D00000000000000000000000000BA8E8500FEEBD700FEEB + D700FEE4CA00FEE4CA00448A3E007DA972007DA97200448A3E000F8319001899 + 2E0025A63D000F83190069CCC0008DF3FE008DF3FE008DF3FE0084E9FE0060D4 + F00064C1E10000000000000000000000000000000000059ACD0064C1E100BEE6 + F5007BDDFE00059ACD004BBDE00090D1F100ADFBFE008DF3FE008DF3FE008DF3 + FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE0070D2FD008DF3 + FE0064C1E10000000000000000000000000000000000000000001DA6D50061C6 + FE0064C1E100C2FDFF0099FDFE0099FDFE008DF3FE0099FDFE008DF3FE0099FD + FE008DF3FE0099FDFE008DF3FE007BDDFE0084E9FE004BBDE000000000000000 + 00000000000000000000000000000000000000000000B4817600CB666600CB66 + 6600CB666600CB666600994C4D00DBC9C60099343400A3453F00B48176009F9F + 9F00A8A8A800C3C2C200DFDFDF00FAFAFA00993434009934340099343400BB57 + 5700CB666600994C4D00000000000000000000000000CB9A8200FEF4E900FEEB + D700FEEBD700FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00B4817600255E + 280025BB4F0030C369000F8319007ECBE6008DF3FE008DF3FE0084E9FE0084E9 + FE0041B3DA0000000000000000000000000000000000059ACD0037B4E000D1EF + F90060D4F000059ACD0070D2FD004BBDE000D4FDFF0099FDFE0099FDFE0099FD + FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0070D2FD008DF3 + FE004BBDE000DFEFF900000000000000000000000000000000001DA6D50070D2 + FD002EACD800E4FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD + FE0099FDFE0099FDFE0099FDFE0084E9FE009AE3FF00ADFBFE00059ACD000000 + 00000000000000000000000000000000000000000000B4817600CB666600CB66 + 6600CB666600CB666600994C4D00DBC9C600E4D1CD00DBC9C600E4D1CD00CCCA + CA00B9B9B900CABCB700C7C7C700DED7D300993434009934340099343400BB57 + 5700CB666600994C4D00000000000000000000000000CB9A8200FEF4E900FEF0 + E200FEF0E200FEEBD700FEE4CA00FEE4CA00FEDDBB00FEDDBB00B481760084E9 + FE000F83190047CE710025BB4F002A8C430099FDFE0099FDFE0084E9FE00ADFB + FE0064C1E10090D1F100000000000000000000000000059ACD0056C6F00090D1 + F1009EDEEE00059ACD0070D2FD0037B4E000E4FDFE0099FDFE0099FDFE0099FD + FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE007BDDFE00BEE6 + F500ADFBFE007ECBE600000000000000000000000000000000001DA6D50070D2 + FD002EACD8009EDEEE00D4FDFF00ADFBFE00ADFBFE00ADFBFE00ADFBFE00ADFB + FE00ADFBFE00ADFBFE00ADFBFE0084E9FE009AE3FF00D4FDFF004BBDE0000000 + 00000000000000000000000000000000000000000000B4817600CB666600CB66 + 6600CB666600CB666600CB666600B4817600B4817600B4817600B4817600CB66 + 6600CB666600986B6600CB666600B4817600BB575700BB575700A3453F00CB66 + 6600CB666600994C4D00000000000000000000000000DCA88700FEF9F300FEF4 + E900FEF0E200FEEBD700FEEBD700FEE4CA00FEE4CA00FEDDBB00B481760084E9 + FE000F83190047CE710058E087000F83190099FDFE0099FDFE008DF3FE00ADFB + FE009AE3FF004BBDE000000000000000000000000000059ACD0061C6FE002EAC + D800BEE6F5001DA6D50070D2FD0056C6F00064C1E100DFEFF900E4FDFE00E4FD + FE00D4FDFF00C2FDFF00ADFBFE00C2FDFF00ADFBFE00ADFBFE0070D2FD00C2FD + FF00D4FDFF004BBDE000F0F6FD000000000000000000000000001DA6D5007BDD + FE007BDDFE0037B4E0004BBDE0007ECBE600ACDBF400D1EFF900E4FDFE00C2FD + FF00C2FDFF00C2FDFF00C2FDFF009AE3FF009AE3FF00E4FDFE00ACDBF400059A + CD000000000000000000000000000000000000000000B4817600CB666600CB66 + 6600CB666600CB666600CB666600CB666600CB666600CB666600CB666600CB66 + 6600CB666600CB666600CB666600CB666600CB666600CB666600CB666600CB66 + 6600CB666600994C4D00000000000000000000000000E3B18E00FFFDF800FFF8 + EE00FEF4E900FEF0E200FEEBD700FEEBD700FEE4CA000F8319000F8319000F83 + 19000F83190058E0870058E087000F8319000F8319000F8319000F831900ADFB + FE00D4FDFF004BBDE000BEE6F5000000000000000000059ACD0070D2FD0037B4 + E0007ECBE6001DA6D5007BDDFE007BDDFE0060D4F0004BBDE0002EACD8002EAC + D8002EACD800D1EFF900E4FDFE00C2FDFF00C2FDFF00D4FDFF007BDDFE00D1EF + F900E4FDFE00BEE6F5009EDEEE000000000000000000000000002EACD80084E9 + FE0084E9FE007BDDFE0084E9FE004BBDE00037B4E00037B4E00041B3DA00F2FA + F500D4FDFF00D4FDFF00D4FDFF009AE3FF009EDEEE000F8319008DBC8B0064C1 + E1000000000000000000000000000000000000000000B4817600CB666600A345 + 3F00BA8E8500BF989500CC9A9900BF989500CC9A9900CC9A9900CC9A9900CC9A + 9900CC9A9900CC9A9900CC9A9900CC9A9900CC9A9900CC9A9900A4787400CB66 + 6600CB666600994C4D00000000000000000000000000EDBD9200FFFFFF00FEFA + F700FEF9F300FEF4E900FEF0E200FEEBD700FEEBD700F0E4C700267D270025A6 + 3D0047CE710058E0870058E0870058E0870025A63D00267D27009EDEEE00C2FD + FF00D4FDFF009EDEEE0041B3DA0000000000000000001DA6D5007BDDFE0061C6 + FE002EACD8001DA6D50084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0084E9 + FE0084E9FE004BBDE000BEE6F500F0FEFE00F0FEFE00F0FEFE009AE3FF00E4FD + FE00F9FFFE00F0FEFE0064C1E1000000000000000000000000002EACD8008DF3 + FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE0071EBF9001DA6 + D50041B3DA004BBDE0004BBDE0004BBDE0002A8C430047CE71000F83190051B4 + 9400F0F6FD0000000000000000000000000000000000B4817600CB666600A345 + 3F00F3F3F300F3F3F300F3F3F300F3F3F300FAF3F200F3F3F300F3F3F300F3F3 + F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 + 5700CB666600994C4D00000000000000000000000000EDBD9200FFFFFF00FFFF + FE00FDFAFA00FEF9F300FEF4E900FEF0E200FEEBD700F3B9B500B48176000571 + 0A0047CE710058E0870058E0870058E087000F831900C7EFF100ADFBFE00D1EF + F900F0FEFE00D4FDFF0041B3DA00F0F6FD00000000001DA6D5007BDDFE0061C6 + FE0037B4E0001DA6D50099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD + FE0099FDFE0099FDFE0060D4F0004BBDE0004BBDE0004BBDE0004BBDE0004BBD + E0004BBDE0004BBDE0004BBDE0000000000000000000000000002EACD80099FD + FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD + FE0099FDFE0071EBF90060D4F000097B450025BB4F0058E0870058E087001899 + 2E00DDEADD0000000000000000000000000000000000B4817600CB666600A345 + 3F00FAF3F200F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 + F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 + 5700CB666600994C4D00000000000000000000000000EDBD9200FFFFFF00FFFE + FF00FEFEFE00FFFDF800FEF9F300FEF4E900B4817600B4817600B4817600B1A0 + 97000F83190058E0870058E08700267D2700F0FEFE00F0FEFE00D1EFF900E4FD + FE00FCFEFE00FCFEFE007ECBE60041B3DA00000000001DA6D5007BDDFE007BDD + FE0037B4E0001DA6D50099FDFE0099FDFE0099FDFE0099FDFE0099FDFE00ADFB + FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE002EACD8000000 + 000000000000000000000000000000000000000000000000000037B4E000ADFB + FE0099FDFE0099FDFE0099FDFE0099FDFE00D4FDFF00C2FDFF00C2FDFF00ADFB + FE0099FDFE0099FDFE0049AA490018992E0058E0870058E0870058E0870058E0 + 8700267D270000000000000000000000000000000000B4817600CB666600A345 + 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 + F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 + 5700CB666600994C4D00000000000000000000000000EDBD9200FCFEFB00FCFE + FE00FEFFFC00FCFEFE00FEFAF700FFF8EE00B4817600FCC47C00DCA88700FED6 + C9008DBC8B00267D27000F83190041B3DA0041B3DA0041B3DA0041B3DA0041B3 + DA0041B3DA0041B3DA0041B3DA0041B3DA00000000001DA6D5009AE3FF0084E9 + FE007BDDFE001DA6D500C2FDFF0099FDFE0099FDFE0099FDFE00C2FDFF009EDE + EE009EDEEE00ACDBF400C2FDFF00D4FDFF00C2FDFF00ADFBFE002EACD8000000 + 000000000000000000000000000000000000000000000000000037B4E000E4FD + FE00ADFBFE00ADFBFE0099FDFE00D4FDFF007ECBE60037B4E00037B4E00037B4 + E00037B4E0008DBC8B000F83190047CE710058E0870058E0870058E0870058E0 + 870058E0870068A76200000000000000000000000000B4817600CB666600A345 + 3F00F8F0F000F3F3F300CCCACA00C0C0C000C3C2C200C3C2C200C4C4C400C4C4 + C400C4C4C400C7C7C700C7C7C700C7C7C700D1D1D100F3F3F300D6BCBB00BB57 + 5700CB666600994C4D00000000000000000000000000DCA88700FFFEFE00FDFA + FA00F8F8F800F3F3F300F3F3F300ECEAEA00B4817600D6A89400F3E3D100FEF0 + E200FEF0E2007DA972008DBC8B00FECFC200B481760099FDFE0041B3DA000000 + 000000000000000000000000000000000000000000002EACD8008DF3FE008DF3 + FE008DF3FE001DA6D500DFEFF900E4FDFE00D4FDFF00D4FDFF00C7EFF1004BBD + E00060D4F00060D4F00037B4E0002EACD8002EACD8002EACD8002EACD8000000 + 000000000000000000000000000000000000000000000000000037B4E00064C1 + E10090D1F1009EDEEE00BEE6F50090D1F10037B4E00000000000000000000000 + 0000000000008DBC8B007DA97200448A3E0025BB4F0058E0870058E087000F83 + 190068A762007DA97200F3F3F3000000000000000000B4817600CB666600A345 + 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 + F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 + 5700CB666600994C4D00000000000000000000000000DCA88700DCA88700DCA8 + 8700DCA88700DCA88700DCA88700DCA88700DCA88700F5EADF00FFF8EE00FEF4 + E900FEF0E200FEEBD700FECFC200F3B9B500B481760099FDFE0041B3DA000000 + 000000000000000000000000000000000000000000001DA6D500ADFBFE0099FD + FE0099FDFE008DF3FE0041B3DA0037B4E00037B4E00037B4E00037B4E00099FD + FE0099FDFE0099FDFE0071EBF9002EACD8000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000000037B4 + E00037B4E00037B4E00037B4E00037B4E0000000000000000000000000000000 + 00000000000000000000000000000000000025A63D0047CE710058E087000F83 + 19000000000000000000000000000000000000000000B4817600CB666600A345 + 3F00F3F3F300F3F3F300CCCACA00C0C0C000C0C0C000C0C0C000C3C2C200C3C2 + C200C4C4C400C4C4C400C4C4C400C7C7C700D1D1D100F3F3F300D6BCBB00BB57 + 5700CB666600994C4D00000000000000000000000000000000000000000041B3 + DA00D4FDFF00D4FDFF00EDBD9200FEFAF700FEFAF700FDFAFA00FEFAF700FEF9 + F300FEF4E900B4817600B4817600B4817600B481760041B3DA0041B3DA000000 + 000000000000000000000000000000000000000000002EACD800C2FDFF0099FD + FE0099FDFE0099FDFE0099FDFE00C2FDFF00C2FDFF00C2FDFF00ADFBFE0099FD + FE0099FDFE0099FDFE0071EBF9002EACD8000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000D0E2CE0025A63D0047CE710030C369008DBC + 8B000000000000000000000000000000000000000000B4817600CB666600A345 + 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 + F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 + 5700CB666600994C4D000000000000000000000000000000000000000000ACDB + F40041B3DA0037B4E000EDBD9200FFFEFF00FFFEFF00FFFFFE00FFFFFF00FEF9 + F300FEF9F300B4817600FCB04C00D3936400D8C5B50000000000000000000000 + 000000000000000000000000000000000000000000001DA6D500E4FDFE00ADFB + FE0099FDFE0099FDFE00C2FDFF009EDEEE002EACD8002EACD8002EACD8002EAC + D8002EACD8002EACD8002EACD8002EACD8000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000448A3E0025BB4F0047CE71000F831900EFF9 + F0000000000000000000000000000000000000000000B4817600CB666600A345 + 3F00F8F0F000F3F3F300C7C7C700BFBFBF00C0C0C000BFBFBF00C0C0C000C0C0 + C000C3C2C200C4C4C400C3C2C200C4C4C400CFCFCF00F3F3F300D6BCBB00BB57 + 5700CB666600994C4D0000000000000000000000000000000000000000000000 + 00000000000000000000EDBD9200FFFFFF00FCFEFB00F8FBFB00FAFAFA00F8F8 + F800F8F0F000B4817600EDBD9200D8C5B5000000000000000000000000000000 + 000000000000000000000000000000000000000000002EACD8007ECBE6007ECB + E6009EDEEE00BEE6F500ACDBF40037B4E0000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000E1F1E10068A7620018992E0030C369000F831900DDEADD000000 + 00000000000000000000000000000000000000000000B4817600CB666600A345 + 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 + F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 + 5700CB666600994C4D0000000000000000000000000000000000000000000000 + 00000000000000000000DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 + 8700DCA88700DCA88700DCA88700000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000037B4E00037B4 + E00037B4E00037B4E00041B3DA00F0FEFE000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000AFD0B00005710A00448A + 3E0005710A000F8319000EA31B0018992E00267D2700DDEADD00000000000000 + 00000000000000000000000000000000000000000000B4817600CB666600A345 + 3F00F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3 + F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300F3F3F300D6BCBB00BB57 + 5700CB666600994C4D0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000F2FAF500AFD0 + B00068A762007DA972007DA97200D0E2CE000000000000000000000000000000 + 00000000000000000000000000000000000000000000B4817600994C4D009934 + 3400C4C4C400C4C4C400C3C2C200C3C2C200C4C4C400C4C4C400C4C4C400C4C4 + C400C4C4C400C3C2C200C4C4C400C4C4C400C4C4C400C3C2C200BF989500994C + 4D00994C4D00DAD9D90000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000EFEFEF00C0C0C0009F9F + 9F009F9F9F00A8A8A8008F8F8F00B9B9B900E6E5E40000000000000000000000 + 0000000000000000000000000000000000000000000000000000CC9A9900CC9A + 9900CC9A9900D5B3AF00EADBD400F8F0F0000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000059ACD00059ACD0090D1F100BEE6F5000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000059ACD0064C1 + E1009EDEEE00DFEFF90000000000000000000000000000000000000000000000 + 0000000000000000000000000000AFD0B0000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000EFEFEF009F9F9F008C8988009F9F9F00B9AA + A600C8ABAB00CABCB700D6BCBB00D5B3AF00B1A0970086858500D1D1D1000000 + 0000000000000000000000000000000000000000000000000000CC9A9900CC9A + 9900D6A89400D6A89400D6A89400CC9A9900CC9A9900CDA6A000E4D1CD00F6EF + E700000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000059ACD00C7EFF10037B4E000059ACD00059ACD0041B3DA007ECBE600ACDB + F400F0F6FD000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000059ACD00DFEF + F90056C6F0002EACD8002EACD80064C1E10090D1F100D1EFF900000000000000 + 000000000000000000000000000005710A0068A7620000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000D1D1D1008C89880099969700999697008F8E8D008F8E + 8D00A4968E00A18C8200A4968E008C898800C8ABAB00FECFC200BF989500A8A8 + A800000000000000000000000000000000000000000000000000CC9A9900CC9A + 9900CC9A9900FECC9A00FECC9A00FECC9A00EDBD9200E3B18E00D6A89400CC9A + 9900CC9A9900CC9A9900DBC9C600F1E3E2000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000059ACD007ECBE6009AE3FF007BDDFE0070D2FD0056C6F00037B4E0001DA6 + D500059ACD002EACD8007ECBE6009EDEEE00DFEFF90000000000000000000000 + 0000000000000000000000000000000000000000000000000000059ACD0090D1 + F1009AE3FF007BDDFE0070D2FD0060D4F00056C6F00037B4E0001DA6D5004BBD + E00090D1F100C7EFF100D1EFF9000F8319000F831900448A3E00EFF9F0000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000A1C79E00879185009F9F9F008C898800B1A09700EDD9B700FDE8 + B700CFCFCF006682F100FDF6C600FDE8B700D8C5B50086858500E2BDB300E2BD + B3008C8988000000000000000000000000000000000000000000CC9A9900EDBD + 9200D6A89400D6A89400FED7AB00FED7AB00F3D1A300FECC9A00FECC9A00FECC + 9A00EDBD9200EDBD9200D6A89400CC9A9900CC9A9900CC9A9900D6BCBB00F1E3 + E200000000000000000000000000000000000000000000000000000000000000 + 0000059ACD0037B4E000D1EFF9007BDDFE007BDDFE007BDDFE007BDDFE007BDD + FE0051B4940056C6F00037B4E0001DA6D500059ACD001DA6D500BEE6F5000000 + 0000000000000000000000000000000000000000000000000000059ACD0064C1 + E100D1EFF9007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE0070D2 + FD0060D4F00037B4E00037B4E0000F8319000EA31B000F831900267D2700D0E2 + CE00000000000000000000000000000000000000000000000000000000000000 + 0000AFD0B0000F831900267D270087918500E3D3AA00FDE8B700FDF6C600FEF9 + F300BEC6DC004571FA00FFFDF800FEFFFC00FFF8EE00FDF6C600A4968E00D5B3 + AF00E2BDB300A8A8A80000000000000000000000000000000000CC9A9900EAC8 + A100F3D1A300CC9A9900DAB4A000FDE8B700FDE8B700FED7AB00FED7AB00FED7 + AB00F3D1A300FECC9A00FECC9A00FECC9A00FECC9A00EDBD9200D6A89400D6A8 + 9400CC9A9900CC9A990000000000000000000000000000000000000000000000 + 0000059ACD004BBDE000D1EFF90084E9FE0084E9FE0084E9FE0084E9FE0084E9 + FE002A8C430005710A0069CCC00084E9FE0084E9FE0060D4F000059ACD000000 + 0000000000000000000000000000000000000000000000000000059ACD004BBD + E000F0F6FD007BDDFE0084E9FE007BDDFE002A8C43000F8319000F8319000F83 + 19000F8319000F8319000F8319000F83190016B432000EA31B000EA31B000571 + 0A00A1C79E00000000000000000000000000000000000000000000000000D0E2 + CE00267D270058E0870030C36900448A3E00FDE8B700FDF6C600FDE8B700FED7 + AB00FED7AB00FED7AB00FDE8B700FEEBD700FFFDF800FEFFFC00FDF6C6008F8E + 8D00E2BDB300AB8FA300DAD9D900000000000000000000000000CC9A9900F3D1 + A300FDE8B700EAC8A100CC9A9900EDD9B700FDF6C600FDF6C600FDE8B700FDE8 + B700FDE8B700FDE8B700FED7AB00FED7AB00FECC9A00F3D1A300FECC9A00FECC + 9A00D6A89400CC9A990000000000000000000000000000000000000000000000 + 0000059ACD0061C6FE0090D1F100ADFBFE008DF3FE008DF3FE008DF3FE008DF3 + FE0051B4940025A63D0018992E0051B494008DF3FE0084E9FE00059ACD00D1EF + F900000000000000000000000000000000000000000000000000059ACD0056C6 + F0009EDEEE00ADFBFE0084E9FE0084E9FE002A8C430058E0870047CE710047CE + 710047CE710030C3690030C3690025BB4F0016B4320016B432000EA31B000EA3 + 1B0005710A007DA9720000000000000000000000000000000000F2FAF5001899 + 2E0030C3690058E0870058E0870025A63D007DA97200FDE8B700FDE8B700FDE8 + B700FDE8B700FED7AB00FED7AB00FED7AB00FEDDBB00FFFDF800FFFDF800FDF6 + C6008F8E8D00FECFC20099969700000000000000000000000000CC9A9900EDD9 + B700FDF6C600FDE8B700EAC8A100CC9A9900FEE4CA00FEFED300FDF6C600FDF6 + C600FDF6C600FDE8B700FDE8B700FDE8B700FDE8B700FED7AB00FED7AB00DAB4 + A000CC9A9900CC9A990000000000000000000000000000000000000000000000 + 0000059ACD0070D2FD0041B3DA00D4FDFF0099FDFE0099FDFE0099FDFE0099FD + FE0099FDFE0018992E0058E0870025A63D0051B4940084E9FE00ADFBFE0064C1 + E100000000000000000000000000000000000000000000000000059ACD0056C6 + F0002EACD800D4FDFF0084E9FE008DF3FE002A8C430058E0870058E0870058E0 + 870047CE710030C3690047CE710025BB4F0025BB4F0016B4320016B432000EA3 + 1B000EA31B0005710A0068A76200000000000000000000000000448A3E0025A6 + 3D0047CE710058E0870058E0870058E087000EA31B00A1C79E00FDE8B700FDE8 + B700FDE8B700FDE8B700FDE8B700FED7AB00FED7AB00FDE8B700FCFEFB00FFF8 + EE00B9AAA600D5B3AF00A4968E00ECEAEA000000000000000000CC9A9900FDE8 + B700FEFED300FEFED300FDF6C600C8ABAB00DAB4A000FEFFE100FEFED300FEFE + D300FEFED300FEFED300FDF6C600FDF6C600FDF6C600FDF6C600EAC8A100CC9A + 9900DAB4A000CDA6A00000000000000000000000000000000000000000000000 + 00001DA6D50070D2FD0037B4E000D1EFF90099FDFE0099FDFE0099FDFE0099FD + FE0099FDFE0051B4940025A63D0058E0870018992E007ECBE600C2FDFF00059A + CD00DFEFF9000000000000000000000000000000000000000000059ACD0061C6 + FE0037B4E000F0F6FD0099FDFE008DF3FE002A8C430058E0870058E0870058E0 + 870047CE710047CE710030C3690047CE710025BB4F0025BB4F0016B4320016B4 + 32000EA31B0005710A008DBC8B0000000000000000007DA972000F83190030C3 + 690047CE710058E0870058E0870058E0870058E087000F831900C7D19500FDF6 + C600FDF6C600FDE8B700FDE8B700FDE8B700FED7AB00FED7AB00FEF0E200FCFE + FE00FDE8B700A4968E00C8ABAB00C7C7C7000000000000000000CC9A9900F0E4 + C700FEFED300FEFED300FEFED300FDE8B700CDA6A000D8C5B500FEFFE100FEFF + E100FEFFE100FEFED300FEFED300FEFED300FEFED300EDD9B700CC9A9900EAC8 + A100FECC9A00D6A8940000000000000000000000000000000000000000000000 + 00001DA6D5007BDDFE0056C6F0007ECBE600E4FDFE00D4FDFF00C2FDFF00C2FD + FF00ADFBFE00ADFBFE0025A63D0058E0870030C3690051B49400D4FDFF00C2FD + FF00059ACD000000000000000000000000000000000000000000059ACD0070D2 + FD0061C6FE009EDEEE00C2FDFF0099FDFE002A8C430049AA490025A63D0025A6 + 3D0025A63D0025A63D0018992E0025BB4F0030C3690030C3690025BB4F0016B4 + 320005710A00AFD0B0000000000000000000A1C79E0005710A0025BB4F0025BB + 4F0030C3690030C3690047CE710058E0870058E0870047CE7100267D2700FDF6 + C600E3D3AA00CB9A8200EDBD9200FDE8B700FDE8B700FED7AB00FDE8B700FFFF + FE00FDF6C60080808000D6BCBB00B5B3B3000000000000000000CC9A9900F0E4 + C700FEFED300FEFED300FEFED300FEFED300E5CDBE00CC9A9900F3E3D100FFF8 + EE00FEFFE100FEFFE100FEFFE100FEFFE100EDD9B700CC9A9900DAB4A000FDE8 + B700FDE8B700D6A8940000000000000000000000000000000000000000000000 + 00001DA6D50084E9FE007BDDFE0056C6F00037B4E00037B4E00037B4E00037B4 + E000E4FDFE00ADFBFE0025A63D0058E0870058E08700267D2700E4FDFE00E4FD + FE00059ACD000000000000000000000000000000000000000000059ACD0070D2 + FD0061C6FE009BC3D800D4FDFF0099FDFE0099FDFE0099FDFE0099FDFE0099FD + FE0099FDFE0099FDFE0099FDFE0025A63D0030C3690030C3690018992E002A8C + 430069CCC000D1EFF900000000000000000068A76200448A3E00448A3E000F83 + 190025BB4F0047CE710047CE710025BB4F00267D2700448A3E00448A3E008DBC + 8B0060606000707070004E4940006F5B4D006F5B4D00826A5B00FED7AB00D0E3 + EC004571FA0099969700D6BCBB00B9B9B9000000000000000000CC9A9900F3E3 + D100FEFFE100FEFFE100FEFFE100F0E4C700CDA6A000CC9A9900CC9A9900EADB + D400FFF8EE00FFFDF800FEF9F300F5EADF00CDA6A000E2BDB300FDF6C600FDF6 + C600FDF6C600DAB4A00000000000000000000000000000000000000000000000 + 00002EACD8009AE3FF0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE007BDD + FE0037B4E000E4FDFE0025A63D0058E0870058E0870018992E00FCFEFE00F9FF + FE00BEE6F500059ACD0000000000000000000000000000000000059ACD007BDD + FE007BDDFE00B4817600BEE6F500F0FEFE00E4FDFE00D4FDFF00C2FDFF00C2FD + FF00ADFBFE00ADFBFE00C2FDFF0025A63D0047CE710018992E002A8C4300C7EF + F100D4FDFF002EACD80000000000000000000000000000000000E6E5E4000F83 + 190016B4320025BB4F0030C3690018992E00C0D5C200FEFFE100FEFED300FEFE + D300606060008F8F8F0030303000927D7000A18C8200CB9A8200FED7AB00ECEA + EA004571FA008F8E8D00C8ABAB00B9B9B9000000000000000000CC9A9900EFE4 + D800FEFFE100FEEBD700D5B3AF00CC9A9900BFBFBF00C7EFF100C0C0C000CC9A + 9900CC9A9900CDA6A000D5B3AF00CDA6A000CC9A9900F0E4C700FEFED300FEFE + D300FEFED300DAB4A00000000000000000000000000000000000000000000000 + 000037B4E00099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD + FE008DF3FE0037B4E00025A63D0058E0870058E0870018992E00BEE6F500BEE6 + F500FCFEFB00059ACD0000000000000000000000000000000000059ACD007BDD + FE007BDDFE00BA8E8500C7DDE1009BC3D80037B4E00041B3DA0037B4E0002EAC + D800F0FEFE00C2FDFF00C2FDFF0025A63D000F83190068A762009AE3FF00E4FD + FE00E4FDFE002EACD800DFEFF900000000000000000000000000ECEAEA000571 + 0A0016B4320025BB4F0025BB4F0018992E00C0D5C200FFFDF800FEFFE100FEFF + E100F0E4C700C6B8AB004040400059504700FED7AB00FDE8B700FDE8B700FFF8 + EE00FDE8B70086858500B9AAA600B9B9B9000000000000000000CC9A9900EADB + D400E2BDB300CC9A9900C6B8AB00E6E5E400F0FEFE00F9FFFE00E4FDFE00C2FD + FF00C7DDE100BEC6DC00C4C4C400AFCFDE00C8ABAB00CC9A9900FEEBD700FEFE + D300FEFED300DAB4A00000000000000000000000000000000000000000000000 + 000037B4E00099FDFE0099FDFE0099FDFE0099FDFE0099FDFE00ADFBFE0099FD + FE0099FDFE0099FDFE0025A63D0047CE710058E0870018992E002EACD80037B4 + E00037B4E000ACDBF400000000000000000000000000000000001DA6D50084E9 + FE0084E9FE00CB9A8200F8F3EC00FEF4E900FEF0E200FEEBD700FEEBD700C0D5 + C2002EACD800F0FEFE00F0FEFE0005710A00A1C79E00D4FDFF009AE3FF00F0FE + FE00F0FEFE00F0FEFE00059ACD0000000000000000000000000000000000448A + 3E000F83190016B4320025BB4F0018992E00A1C79E00FFFDF800FEFFE100FEFF + E100FEFFE100FEFED300E3D3AA0043424200826A5B00FED7AB00FDF6C600FEEB + D700C7D195008F8F8F0099969700D1D1D1000000000000000000CC9A9900CC9A + 9900CDA6A000BEC6DC00F9FFFE00FFFFFF00F9FFFE00F9FFFE00F0FEFE00F0FE + FE00E4FDFE00E4FDFE00E4FDFE00E4FDFE00D1EFF900C8ABAB00CC9A9900F0E4 + C700FEFFE100DAB4A00000000000000000000000000000000000000000000000 + 000037B4E000D4FDFF0099FDFE0099FDFE0099FDFE00C2FDFF00ACDBF40037B4 + E0002EACD80051B4940025A63D0047CE710058E0870018992E00000000000000 + 00000000000000000000000000000000000000000000000000001DA6D5008DF3 + FE008DF3FE00CB9A8200F8F3EC00FEF9F300FEF4E900FEF0E200FEEBD700FEEB + D700C0D5C2002EACD8002EACD8002EACD800DFEFF900FCFEFE00E4FDFE00FEFE + FE00FEFEFE00FCFEFE00059ACD0000000000000000000000000000000000AFAF + AF0005710A000EA31B0016B4320016B43200448A3E00FFFFFF00FCFEFB00FEF9 + F300FEFFE100FEFED300FEFED300E3D3AA0040404000826A5B00FED7AB00FDE8 + B700B1A097009996970086858500000000000000000000000000CC9A9900CC9A + 9900CC9A9900BEC6DC00FFFEFF00FFFEFF00FFFFFF00FFFFFF00F9FFFE00F9FF + FE00F0FEFE00E4FDFE00F0FEFE00E4FDFE00E4FDFE00D1EFF900B9AAA600CDA6 + A000FEEBD700D5B3AF0000000000000000000000000000000000000000000000 + 00002EACD8009EDEEE00E4FDFE00E4FDFE00D4FDFF00BEE6F5007ECBE600FEFF + FC00FFFEFF0005710A0025BB4F0030C3690047CE710018992E00000000000000 + 00000000000000000000000000000000000000000000000000001DA6D50099FD + FE0099FDFE00DCA88700F8F8F800FEFAF700FFF8EE00FEF4E900FEF0E200FEEB + D700FEEBD700FEE4CA00FED6C900B481760071EBF900059ACD00059ACD00059A + CD00059ACD00059ACD00059ACD0000000000000000000000000000000000E6E5 + E400668E67000F8319000EA31B0016B432000F8319008DBC8B00FFFEFF00FFFF + FF00FFFFFE00FFFDF800FEFED300FEFED300E3D3AA0040404000E3B18E00E3D3 + AA008C89880099969700A8A8A800000000000000000000000000F6EFE700CC9A + 9900C8ABAB0090D1F100FFFFFE00FFFEFF00FFFFFF00FEFEFE00FFFEFF00FFFC + FF00FFFFFF00F0FEFE00F0FEFE00E4FDFE00BEE6F500ACDBF4007ECBE600AB8F + A300CDA6A000CDA6A00000000000000000000000000000000000000000000000 + 0000F0F6FD0037B4E00041B3DA0037B4E00037B4E00005710A0005710A00448A + 3E00448A3E000F83190030C3690047CE710047CE710018992E00000000000000 + 00000000000000000000000000000000000000000000000000001DA6D50099FD + FE0099FDFE00E3B18E00F8FBFB00FFFEFE00FEF9F300FFF8EE00FEF4E900FEF0 + E200FEEBD700FEEBD700FEE4CA00B481760099FDFE0099FDFE002EACD8000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000B9B9B900668E67000F8319000EA31B0016B432000F831900448A3E00A1C7 + 9E00DDEADD00D1EFF900FFFDF8008DBC8B00C7D19500EDD9B700E3D3AA008C89 + 8800A8A8A8008F8E8D00EFEFEF0000000000000000000000000000000000F8F0 + F000CC9A9900A9ACD400FEFFFC00FFFFFF00FFFCFF00D0E3EC00ACDBF40090D1 + F10090D1F10098A9EF0085AFCB00AB8FA300AB8FA300CC9A9900B7A2B600B7A2 + B600CC9A9900CC9A990000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000049AA49000F83190016B4 + 320016B4320025BB4F0025BB4F0030C3690030C3690025BB4F000F8319000F83 + 190005710A0068A76200000000000000000000000000000000001DA6D500D4FD + FF0099FDFE00EDBD9200FFFCFF00FFFFFE00FFFFFF00FEFAF700FFF8EE00FEF4 + E900FEF0E200FECFC200FCA3A200B4817600C2FDFF00ADFBFE002EACD8000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000FAF3F200B5B3B3008DBC8B000F8319000F83190016B4320025BB4F001899 + 2E000F83190005710A0005710A00267D2700E3D3AA00EAC8A10099969700AFAF + AF0099969700CFCFCF0000000000000000000000000000000000000000000000 + 0000F8F0F000A9ACD40098A9EF0070D2FD0070D2FD0070D2FD009AE3FF0090D1 + F10090D1F10090D1F10090D1F10090D1F10098A9EF0098A9EF00A9ACD400B7A2 + B600CC9A9900CC9A990000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000EFF9F00005710A000EA3 + 1B0016B4320016B4320025BB4F0025BB4F0030C3690030C3690025BB4F000F83 + 19008DBC8B0000000000000000000000000000000000000000001DA6D500DFEF + F900E4FDFE00EDBD9200FEFEFE00FFFFFE00FEFEFE00FFFFFF00FEF9F300FEF9 + F300B4817600B4817600B4817600B48176002EACD8002EACD8002EACD8000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000EFEFEF00B9B9B900DDEADD007DA97200267D270005710A000F83 + 190005710A00448A3E007DA97200DAB4A000B1A09700A8A8A800B9B9B9009F9F + 9F00CFCFCF000000000000000000000000000000000000000000000000000000 + 00000000000000000000CDA6A000BFBFBF008DF3FE009AE3FF009AE3FF009AE3 + FF009AE3FF0090D1F10090D1F10090D1F10090D1F10090D1F10098A9EF00B7A2 + B600CC9A9900F1E3E20000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000AFD0B0000571 + 0A000EA31B0016B4320016B4320025BB4F0025BB4F0018992E00267D2700D0E2 + CE000000000000000000000000000000000000000000000000000000000041B3 + DA0041B3DA00EDBD9200FFFEFF00FFFFFF00FFFFFF00FFFFFF00FEFFFC00FEF9 + F300B4817600FECC9A00DCA88700ECDACC000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000EFEFEF00B9B9B900DAD9D900FBFBFC00E6E5E400C7C7 + C700BFBFBF00B9B9B900B5B3B300C4C4C400CFCFCF00C0C0C0009F9F9F00DAD9 + D900000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000CDA6A000BFBFBF008DF3FE008DF3FE009AE3 + FF009AE3FF009AE3FF0090D1F10090D1F10090D1F1009BC3D800CC9A9900D5B3 + AF00000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000000049AA + 49000F8319000EA31B0016B4320018992E000F8319008DBC8B00000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000EDBD9200FEF0E200FEF0E200F5EADF00EFE4D800EFE4D800EADB + D400B4817600E3B18E00EADBD400000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000DAD9D900B9B9B900C0C0C000DAD9 + D900DAD9D900D1D1D100CCCACA00B9B9B900A8A8A800C4C4C400000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000CDA6A000BFBFBF0099FDFE008DF3 + FE009AE3FF009AE3FF009AE3FF0090D1F100B7A2B600CC9A9900F1E3E2000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000F3F3 + F30005710A000EA31B0005710A00448A3E00E1F1E10000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 + 8700DCA88700EFE4D80000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000F3F3F300DFDF + DF00CCCACA00CCCACA00DAD9D900ECEAEA000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000CDA6A000B5B3B3009EDE + EE008DF3FE009AE3FF009BC3D800CC9A9900D5B3AF0000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000AFD0B00005710A00A1C79E00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000D6BCBB00CC9A + 9900CC9A9900CC9A9900CC9A9900F1E3E2000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000009F9F9F0080808000C8AB + AB00000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000D0E3EC0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000D0E3EC00F3F3 + F30000000000000000000000000000000000000000000000000000000000A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 7400A47874008C5D5C0000000000000000000000000000000000000000000000 + 0000000000000000000000000000C8ABAB00CB9A8200BA8E8500B4817600C8AB + AB00E4D1CD00FAF3F20000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000A8A8A8005D71AB005D71AB008D82 + A600C8ABAB000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000066A6C3000C7CAC000C7CAC00D0E3EC000000000000000000000000000000 + 000000000000000000000000000000000000F0F6FD00458EB60066A6C3000C7C + AC0000000000000000000000000000000000000000000000000000000000A478 + 7400E2BDB300F3B9B500F3B9B500F3B9B500F3B9B500F3B9B500F3B9B500F3B9 + B500F3B9B500F3B9B500F3B9B500E2BDB300F3B9B500D5B3AF00F3B9B500F3B9 + B500E3B18E008C5D5C0000000000000000000000000000000000000000000000 + 00000000000000000000BF989500DCA88700EDBD9200F3E3D100F0E4C700E5CD + BE00DAB4A000C8ABAB00CC9A9900D6BCBB00E2D6E200FAF3F200000000000000 + 00000000000000000000000000000000000085AFCB004BAFFF003A80E0005D71 + AB008D82A600CDA6A00000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000C7CAC0084E9FE0004B8EA000C7CAC00AFCFDE0000000000000000000000 + 0000000000000000000000000000D0E3EC002783AC009BC3D800D4FDFF002EAC + D800C0D9E600000000000000000000000000000000000000000000000000A478 + 7400DED2CA00FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD + BB00FEDDBB00FED7AB00FEDDBB00FED7AB00FED7AB00FED7AB00F3D1A300FECC + 9A00FECFC2008C5D5C0000000000000000000000000000000000000000000000 + 0000ECEAEA00D6A89400E3B18E00EDBD9200EAC8A100EFE4D800F3E3D100F0E4 + C700ECDACC00EDD9B700B4817600BA8E8500BA8E8500BA8E8500BF989500C8AB + AB00DBC9C600F8F0F0000000000000000000ACDBF40061C6FE004BAFFF003A80 + E0005D71AB008D82A600C8ABAB00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000458EB600D0E3EC0010D1FE0004B8EA000C7CAC009BC3D800000000000000 + 00000000000000000000D0E3EC002783AC00ACDBF4008DF3FE003FE0FF000C7C + AC0000000000000000000000000000000000000000000000000000000000A478 + 7400DED7D300FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEDD + BB00FDE8B700FDE8B700FED7AB00FED7AB00FED7AB00FED7AB00FED7AB00F3D1 + A300FECFC2008C5D5C000000000000000000000000000000000000000000F6EF + E700D6A89400EDBD9200EDBD9200EDBD9200EAC8A100FEEBD700EFE4D800F3E3 + D100F0E4C700ECDACC00A4787400A4787400B4817600BF989500E3D3AA00DAB4 + A000D6A89400A18C8200BF9895000000000000000000ACDBF40061C6FE004BAF + FF003A80E0005D71AB008D82A600C8ABAB000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000AFCFDE0066A6C30084E9FE0010D1FE0010D1FE00059ACD0066A6C3000000 + 0000000000009BC3D8002783AC00C7EFF10063E5FD0010D1FE001DA6D50066A6 + C30000000000000000000000000000000000000000000000000000000000B481 + 7600DED7D300FEEBD700FEEBD700FEEBD700029A0300FEE4CA00FEE4CA00C7D1 + 95007DA972008DBC8B00E3D3AA00FEDDBB00FED7AB00FED7AB00FED7AB00FED7 + AB00FECFC2008C5D5C0000000000000000000000000000000000EADBD400D6A8 + 9400F3D1A300FECC9A00EAC8A100EDBD9200EAC8A100F5EADF00EFE4D800EFE4 + D800F3E3D100ECDACC00986B6600986B6600986B6600BA8E8500E3D3AA00E3D3 + AA00E3D3AA00EAC8A100BA8E850000000000000000000000000090D1F10061C6 + FE004BAFFF003A80E0005D71AB008D82A600C8ABAB0000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000002783AC00D1EFF90010D1FE0010D1FE003FE0FF002EACD8002783 + AC0085AFCB00458EB600DFEFF90084E9FE0010D1FE0010D1FE000C7CAC00DFEF + F90000000000000000000000000000000000000000000000000000000000B481 + 7600DAD9D900FEF0E200FEEBD700FEEBD700029A03007ACE7F0049AA4900029A + 0300029A0300029A0300029A030068A76200FDE8B700FED7AB00FED7AB00FED7 + AB00FECFC2008C5D5C00000000000000000000000000F1E3E200DAB4A000FED7 + AB00F3D1A300F3D1A300FECC9A00EDBD9200F3D1A300F6EFE700F5EADF00F5EA + DF00F3E3D100F3E3D100986B6600986B6600986B6600A18C8200EDD9B700E3D3 + AA00E3D3AA00E3D3AA00BA8E850000000000000000000000000000000000ACDB + F40061C6FE004BAFFF003A80E0005D71AB008D82A600D5B3AF00000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000009BC3D80085AFCB0063E5FD0010D1FE0010D1FE003FE0FF004BBD + E00066A6C300F0FEFE008DF3FE003FE0FF0010D1FE0004B8EA002783AC000000 + 000000000000000000000000000000000000000000000000000000000000B481 + 7600DFDFDF00FEF0E200FEF0E200FEEBD700029A0300029A0300029A0300029A + 0300029A0300029A0300029A0300029A030068A76200FED7AB00FED7AB00FED7 + AB00FECFC2008C5D5C00000000000000000000000000DAB4A000E3D3AA00EDD9 + B700F3D1A300F3D1A300F3D1A300EAC8A100F3D1A300F6EFE700F6EFE700F5EA + DF00EFE4D800F3E3D100986B6600986B6600986B6600B4817600EDD9B700EDD9 + B700EDD9B700E3D3AA00BA8E8500000000000000000000000000000000000000 + 000090D1F10061C6FE004BAFFF003A80E0005D71AB00AB8FA300000000000000 + 0000F8F0F000DED7D300DED2CA00EADBD400EFE4D800F1E3E200F8F0F0000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000F3F3F3002783AC00D1EFF90010D1FE0010D1FE003FE0FF0063E5 + FD0099FDFE00ADFBFE008DF3FE0063E5FD003FE0FF00059ACD009BC3D8000000 + 000000000000000000000000000000000000000000000000000000000000B481 + 7600E1E0E000FEF4E900FEF0E200FEF0E200029A0300029A0300029A0300029A + 03000EA31B007ACE7F0068A76200029A0300029A0300C7D19500FED7AB00FEDD + BB00FECFC2008C5D5C00000000000000000000000000DAB4A000F3D1A300FED7 + AB00FED7AB00F3D1A300F3D1A300FECC9A00EDD9B700F8F3EC00FEF4E900F5EA + DF00F5EADF00EFE4D800986B6600986B6600986B6600B4817600EDD9B700EDD9 + B700E3D3AA00E3D3AA00BA8E8500000000000000000000000000000000000000 + 000000000000ACDBF4005DB2FC004BAFFF00458EB60099969700D1D1D100E4D1 + CD00BF989500BA8E8500BF989500CDA6A000C8ABAB00CDA6A000D5B3AF00E4D1 + CD00000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000066A6C300ACDBF4003FE0FF0010D1FE0010D1FE0063E5 + FD0071EBF90099FDFE0099FDFE0071EBF9003FE0FF000C7CAC00F2FAF5000000 + 000000000000000000000000000000000000000000000000000000000000BA8E + 8500E6E5E400FEF4E900FEF4E900FEF0E200029A0300029A0300029A0300029A + 03008DBC8B00FEE4CA00FEE4CA00EDD9B70049AA490049AA4900FEDDBB00FEDD + BB00FED6C9008C5D5C00000000000000000000000000DAB4A000E3D3AA00FDE8 + B700FED7AB00F3D1A300EDBD9200EDBD9200F3D1A300FEF9F300F8F3EC00F6EF + E700F5EADF00F5EADF00986B6600986B6600986B6600B4817600ECDACC00EDD9 + B700EDD9B700EDD9B700BA8E8500000000000000000000000000000000000000 + 00000000000000000000ACDBF40090D1F100CFCFCF00A4968E00A18C8200CB9A + 8200EAC8A100FDE8B700FDF6C600FEFED300FEFED300FDF6C600E5CDBE00CDA6 + A000D5B3AF000000000000000000000000000000000000000000000000000000 + 000000000000000000009BC3D800458EB6009AE3FF0010D1FE0010D1FE003FE0 + FF0071EBF9008DF3FE0099FDFE008DF3FE004BBDE000458EB600000000000000 + 000000000000000000000000000000000000000000000000000000000000BA8E + 8500E6E5E400FFF8EE00FEF4E900FEF0E200029A0300029A0300029A0300029A + 0300029A0300029A0300FEE4CA00FEE4CA00EDD9B70025A63D00FED7AB00FEDD + BB00FECFC200986B6600000000000000000000000000DAB4A000EDD9B700FDE8 + B700EAC8A10085AFCB004493ED004493ED00CABCB700FDFAFA00F9F6F500F8F3 + EC00F6EFE700F5EADF00E4D1CD00D8C5B500CDA6A000BF989500F0E4C700ECDA + CC00EDD9B700EDD9B700BA8E8500000000000000000000000000000000000000 + 0000000000000000000000000000F0F6FD00E6E5E400BF989500CB9A8200EDD9 + B700FDF6C600FEFED300FEFED300FEFED300FEFFE100FEFFE100FFFDF800F6EF + E700C8ABAB00C8ABAB0000000000000000000000000000000000000000000000 + 00000000000085AFCB000C7CAC0004B8EA0010D1FE0010D1FE0010D1FE003FE0 + FF003FE0FF008DF3FE0099FDFE0099FDFE0063E5FD001DA6D500458EB600DFEF + F90000000000000000000000000000000000000000000000000000000000BA8E + 8500ECEAEA00FFF8EE00FEF4E900FEF4E900FEF4E900FEF4E900FEF0E200FEF0 + E200FEEBD700FEE4CA00FEEBD700FEE4CA00FEE4CA00FEE4CA00EDD9B700FEDD + BB00FECFC200986B6600000000000000000000000000DAB4A000F3D1A300EAC8 + A10085AFCB004BAFFF00399CFE00399CFE004493ED00D1EFF900FEFAF700F9F6 + F500F8F3EC00F6EFE700F6EFE700F5EADF00EFE4D800F3E3D100F3E3D100F0E4 + C700ECDACC00EDD9B700BA8E8500000000000000000000000000000000000000 + 000000000000000000000000000000000000EADBD400CC9A9900FED7AB00FDF6 + C600FDF6C600FDF6C600FEFED300FEFFE100FEFFE100FFFDF800FFFFFF00FEFE + FE00F6EFE700BF989500DFDFDF0000000000000000000000000000000000C0D9 + E6002783AC00059ACD0010D1FE003FE0FF003FE0FF0010D1FE0010D1FE0010D1 + FE003FE0FF0071EBF90099FDFE0099FDFE0071EBF90063E5FD0004B8EA000C7C + AC0085AFCB00000000000000000000000000000000000000000000000000CB9A + 8200ECEAEA00FDFAFA00FEF9F3007ACE7F00FEF0E200FEF0E200FEF0E200FEF0 + E200FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD + BB00FED6C900986B6600000000000000000000000000DAB4A000C6B8AB0061C6 + FE0061C6FE004BAFFF004BAFFF00399CFE00399CFE004493ED00DFEFF900FDFA + FA00FEF9F300F8F3EC00F6EFE700F5EADF00F5EADF00EFE4D800F3E3D100F3E3 + D100ECDACC00ECDACC00BF989500F1E3E2000000000000000000000000000000 + 000000000000000000000000000000000000E2BDB300DAB4A000FDF6C600FDE8 + B700FDE8B700FEFED300FEFED300FEFFE100FEF9F300FFFDF800FEFEFE00FFFD + F800FEFFE100E5CDBE00BF9895000000000000000000F3F3F30066A6C3000C7C + AC0004B8EA0063E5FD0071EBF90063E5FD003FE0FF0010D1FE0010D1FE0010D1 + FE003FE0FF0063E5FD008DF3FE0099FDFE008DF3FE0071EBF9003FE0FF0010D1 + FE00059ACD000C7CAC00C0D9E60000000000000000000000000000000000CB9A + 8200EFEFEF00FFFDF800FEF9F3007ACE7F00BBE5B900FEF4E900FEF4E900FEF0 + E200029A0300029A0300029A0300029A0300029A0300FEE4CA00FEE4CA00FEE4 + CA00FED6C900986B6600000000000000000000000000C6B8AB0061C6FE0061C6 + FE0061C6FE0061C6FE004BAFFF004BAFFF00399CFE00399CFE004493ED00DFEF + F900FDFAFA00FEF9F300F8F3EC00F6EFE700F5EADF00F5EADF00EFE4D800F3E3 + D100EADBD400BF989500BF989500F1E3E2000000000000000000000000000000 + 0000000000000000000000000000ECEAEA00CDA6A000EDD9B700FDF6C600FED7 + AB00FDE8B700FEFED300FEFED300FEFFE100FFFDF800FFFDF800FFFDF800FEF9 + F300FEFFE100F0E4C700B4817600ECEAEA009BC3D8000C7CAC0004B8EA0010D1 + FE008DF3FE0099FDFE008DF3FE0071EBF9003FE0FF0010D1FE0010D1FE0010D1 + FE0010D1FE003FE0FF0071EBF90099FDFE0099FDFE008DF3FE003FE0FF0010D1 + FE0010D1FE0010D1FE000C7CAC0085AFCB00000000000000000000000000CB9A + 8200EFEFEF00F9FFFE00FEFAF700D0E2CE000EA31B00BBE5B900FEF4E900FEF0 + E200FEEBD70025A63D00029A0300029A0300029A0300FEEBD700FEE4CA00FEE4 + CA00FECFC200986B66000000000000000000ACDBF4004BAFFF005DB2FC0061C6 + FE0061C6FE0061C6FE0061C6FE005DB2FC004BAFFF00399CFE00399CFE005DB2 + FC00F0F6FD00F9F6F500F9F6F500F8F3EC00F6EFE700F5EADF00F5EADF00EFE4 + D800AB8FA3008D82A600DED7D300000000000000000000000000000000000000 + 0000000000000000000000000000F1E3E200CDA6A000FDF6C600FDE8B700FED7 + AB00FDE8B700FDF6C600FEFED300FEFFE100FEFFE100FFF8EE00FFF8EE00FEFF + E100FEFED300FDF6C600BF989500F1E3E2002783AC003FE0FF0063E5FD00ADFB + FE00C2FDFF00C2FDFF00D4FDFF00D4FDFF00D4FDFF00FCFEFE003FE0FF0010D1 + FE0010D1FE003FE0FF0063E5FD00E4FDFE00E4FDFE00D4FDFF00ADFBFE008DF3 + FE0063E5FD003FE0FF0063E5FD002783AC00000000000000000000000000CB9A + 8200F3F3F300FFFFFF00FFFEFF00FFFDF80049AA4900029A03007ACE7F00D8EB + CC00D8EBCC0049AA4900029A0300029A0300029A0300FEEBD700FEEBD700FEE4 + CA00FECFC200A47874000000000000000000DFEFF9004BAFFF004BAFFF005DB2 + FC0061C6FE0061C6FE0061C6FE0061C6FE005DB2FC004BAFFF00399CFE00399C + FE005DB2FC00F0F6FD00FEF9F300F9F6F500F6EFE700F6EFE700F5EADF008D82 + A6008D82A600D1CDE40000000000000000000000000000000000000000000000 + 0000000000000000000000000000F1E3E200CDA6A000FDF6C600FDE8B700FED7 + AB00FDE8B700FDF6C600FEFED300FEFED300FEFFE100FEFFE100FEFFE100FEFE + D300FEFED300FEFED300BF989500F1E3E200458EB60066A6C30085AFCB0066A6 + C30066A6C300458EB600458EB6002783AC002783AC0066A6C3007BDDFE0010D1 + FE0010D1FE003FE0FF0037B4E0002783AC002783AC002783AC00458EB60066A6 + C30066A6C30085AFCB0085AFCB002783AC00000000000000000000000000DCA8 + 8700F3F3F300FFFFFF00FEFEFE00FFFEFF00D8EBCC00029A0300029A0300029A + 0300029A0300029A0300029A0300029A0300029A0300FEEBD700FEEBD700FEEB + D700F3B9B500A4787400000000000000000000000000DFEFF9004BAFFF004BAF + FF005DB2FC0061C6FE0061C6FE0061C6FE0061C6FE005DB2FC004BAFFF004BAF + FF00399CFE004BAFFF00F0F6FD00FEF9F300F9F6F500F8F0F00085AFCB005D71 + AB00BEC6DC000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000F1E3E200CDA6A000FDF6C600FDF6C600FDE8 + B700FDE8B700FDE8B700FDF6C600FEFED300FEFED300FEFED300FEFED300FEFE + D300FEFED300FDE8B700BA8E8500E6E5E400F0F6FD009BC3D800C0D9E600C0D9 + E600C0D9E600F2FAF500000000000000000000000000458EB600ADFBFE0010D1 + FE0010D1FE0010D1FE00059ACD00C7DDE100000000000000000000000000C0D9 + E600C0D9E600C0D9E6009BC3D800DFEFF900000000000000000000000000DCA8 + 8700F3F3F300FFFFFF00FFFEFF00FFFFFF00FFFDF80091DEA900029A0300029A + 0300029A0300029A0300029A03000EA31B00029A0300FEEBD700F3B9B500FCA3 + A200FCA3A200A478740000000000000000000000000000000000DFEFF9004BAF + FF004BAFFF004BAFFF0061C6FE0061C6FE0061C6FE0061C6FE005DB2FC004BAF + FF004BAFFF00399CFE005DB2FC00F0F6FD00F0FEFE0085AFCB005D71AB00BEC6 + DC00000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000F8F0F000DAB4A000EDD9B700FEFFE100FEE4 + CA00FDE8B700FED7AB00FDE8B700FDE8B700FDF6C600FDF6C600FDF6C600FDF6 + C600FEFED300FED7AB00B4817600F8F0F0000000000000000000000000000000 + 00000000000000000000000000000000000000000000458EB600D1EFF90010D1 + FE0010D1FE0010D1FE000C7CAC00000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000DCA8 + 8700F8F8F800FFFFFF00FEFEFE00FFFFFF00FFFEFF00FEFEFE00DDEADD007ACE + 7F0049AA490049AA49007ACE7F00FEEBD700029A0300E2BDB300D6A89400DCA8 + 8700CB9A8200A47874000000000000000000000000000000000000000000D1EF + F9004BAFFF004BAFFF004BAFFF0061C6FE0061C6FE0061C6FE0061C6FE0061C6 + FE004BAFFF004BAFFF00399CFE005DB2FC0098A9EF003A80E000C0D9E6000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000D6BCBB00E2BDB300FFF8EE00FEFF + FC00FEF9F300FEE4CA00FDE8B700FDE8B700FDE8B700FDE8B700FDE8B700FDF6 + C600FDF6C600D6A89400C8ABAB00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000085AFCB00AFCFDE003FE0 + FF0010D1FE0004B8EA00458EB600000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000DCA8 + 8700FAFAFA00FFFFFF00FCFEFE00FFFFFF00FFFFFF00FFFEFE00FEFAF700FEFA + F700FFF8EE00FFF8EE00FEF4E900FEF4E900FEF4E900B4817600B4817600B481 + 7600B4817600A478740000000000000000000000000000000000000000000000 + 0000D1EFF9005DB2FC004BAFFF004BAFFF0061C6FE0061C6FE0070D2FD0061C6 + FE0061C6FE004BAFFF004BAFFF00399CFE00399CFE006682F100000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000ECEAEA00CDA6A000ECDACC00FFFF + FF00FFFFFF00FEE4CA00FDE8B700FED7AB00F3D1A300FED7AB00FDF6C600FDF6 + C600EDBD9200B481760000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000C7DDE10066A6C30084E9 + FE0010D1FE0004B8EA0085AFCB00000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000E3B1 + 8E00FFFDF800FFFEFF00FFFFFF00FFFFFF00FFFEFF00FFFFFE00FFFEFF00FFFF + FF00FFFDF800FDFAFA00FEF9F300FEF9F300FFF8EE00B4817600FCB04C00DCA8 + 8700CB9A8200E4D1CD0000000000000000000000000000000000000000000000 + 000000000000000000005DB2FC004BAFFF004BAFFF005DB2FC0061C6FE0061C6 + FE0061C6FE0061C6FE004BAFFF004493ED003A80E0006682F100000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000DED7D300CDA6A000EADB + D400FCFEFE00FFF8EE00FDF6C600FDF6C600FDF6C600FEFED300FDE8B700EDBD + 9200BA8E8500DFDFDF0000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000002783AC00C2FD + FF0010D1FE00059ACD00C0D9E600000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000E3B1 + 8E00FEFEFE00FFFEFE00FFFFFE00FFFFFF00FFFFFF00FEFEFE00FFFFFE00FFFF + FE00FFFEFF00FFFDF800FFFDF800FEF9F300FEF9F300B4817600DCA88700CB9A + 8200ECDACC000000000000000000000000000000000000000000000000000000 + 00000000000000000000F0F6FD004BAFFF004BAFFF004BAFFF005DB2FC0070D2 + FD009AE3FF00D1EFF9007C7FCC0002019A000732DE006682F100000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000EADBD400BA8E + 8500D6A89400EDD9B700FDE8B700FDF6C600FDE8B700E3D3AA00D6A89400BF98 + 9500F1E3E2000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000458EB600DFEF + F9003FE0FF000C7CAC0000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000E3B1 + 8E00FFFFFE00FCFEFB00FFFCFF00F8FBFB00FAFAFA00FAFAFA00F9F6F500F9F6 + F500F3F3F300EFEFEF00EFEFEF00ECEAEA00ECEAEA00B4817600DCA88700EADB + D400000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000F0FEFE00ACDBF400BEE6F500F0F6FD000000 + 000000000000000000007C7FCC0002019A00295AF7006682F100000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000FAF3 + F200CABCB700BA8E8500B4817600BA8E8500BF989500CC9A9900D6BCBB000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000066A6C300AFCF + DE0060D4F000458EB60000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 + 8700DCA88700DCA88700DCA88700DCA88700DCA88700B4817600F3E3D1000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000BEC6DC008D82A6008D82A600D1CDE400000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000002783 + AC000C7CAC00AFCFDE0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0E2CE00C0D5C200C7C7 + C700C0D5C200C0D5C200EFEFEF00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000EFF9F000C0D5C200C7C7 + C700C0D5C200C0D5C200DDEADD00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000E1F1E1007DA97200267D27000450070005710A000571 + 0A0005710A000450070004500700255E2800A8A8A80000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000A1C79E00267D27000450070005710A000571 + 0A0005710A000450070004500700255E2800668E6700DDEADD00000000000000 + 000000000000000000000000000000000000000000000000000000000000A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 7400A47874008C5D5C000000000000000000CC670100CC670100CC670100CC67 + 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 + 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 + 0100CC670100CC670100CC670100000000000000000000000000000000000000 + 0000000000008DBC8B0005710A000F8319000EA31B0016B4320009B2190009B2 + 190009B2190009B21900029A03000F83190004500700255E2800D1D1D1000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000D0E2CE000F83190005710A0018992E0009B2190009B2190009B2 + 190009B2190009B2190009B21900029A030005710A0004500700879185000000 + 000000000000000000000000000000000000000000000000000000000000A478 + 7400E2BDB300F3B9B500F3B9B500F3B9B500F3B9B500F3B9B500F3B9B500F3B9 + B500F3B9B500F3B9B500F3B9B500E2BDB300F3B9B500D5B3AF00F3B9B500F3B9 + B500E3B18E008C5D5C000000000000000000CC670100FFFFFE00FFFFFF00FEFA + F700FEF9F300FFF8EE00FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEEB + D700FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7 + AB00FED7AB00FED7AB00CC67010000000000000000000000000000000000EFF9 + F00049AA490005710A0016B4320016B4320016B4320016B4320016B4320009B2 + 190009B2190009B2190009B2190009B2190009B2190005710A0004500700A8A8 + A800000000000000000000000000000000000000000000000000000000000000 + 0000A1C79E0005710A0018992E0016B4320016B4320016B4320016B4320009B2 + 190009B2190009B2190009B2190009B2190009B21900029A030004500700668E + 670000000000000000000000000000000000000000000000000000000000A478 + 7400DED2CA00FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD + BB00FEDDBB00FEDDBB00FEDDBB00FED0B700FED7AB00FED7AB00F3D1A300FECC + 9A00FECFC2008C5D5C000000000000000000CC670100FFFFFE00FFFFFF00FFFF + FF00FEF9F300FEF4E900FEF0E200FEF0E200FEF0E200FEEBD700FEE4CA00FEE4 + CA00FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FED7AB00FED7AB00FED7 + AB00FED7AB00FED0B700CC6701000000000000000000000000000000000068A7 + 62000F83190025BB4F0025BB4F0016B4320016B4320016B4320016B4320016B4 + 320016B4320009B2190009B2190009B2190009B2190009B21900029A03000450 + 0700B9B9B900000000000000000000000000000000000000000000000000A1C7 + 9E000F83190016B4320025BB4F0025BB4F0016B4320016B4320016B4320016B4 + 320016B4320009B2190009B2190009B2190009B2190009B21900029A03000450 + 0700668E6700000000000000000000000000000000000000000000000000A478 + 7400DAD9D900FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD + BB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00F3D1 + A300FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFFFE00FFFF + FF00999697009996970099969700FEF4E900FEF4E900FEF0E200999697009996 + 970099969700FEE4CA00FEE4CA00FEE4CA00999697009996970099969700FED7 + AB00FEDDBB00FED7AB00CC6701000000000000000000000000008DBC8B000F83 + 190025BB4F0025BB4F0025BB4F0025BB4F0025BB4F0016B4320030C36900EFF9 + F000EFF9F00047CE710009B2190009B2190009B2190009B2190009B21900029A + 030004500700DDEADD0000000000000000000000000000000000E1F1E1000F83 + 190025A63D0025BB4F0025BB4F0025BB4F0025BB4F0016B4320047CE7100F2FA + F500F2FAF50039C3390016B4320009B2190009B2190009B2190009B219000EA3 + 1B00045007009F9F9F000000000000000000000000000000000000000000B481 + 7600DED7D300FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4 + CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7 + AB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFFFF00FFFF + FE00FFFFFF00FFFFFF00FFFDF800FEF9F300FFF8EE00FFF8EE00F6EFE700F6EF + E700F3E3D100FEE4CA00FEEBD700FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD + BB00FEDDBB00FEDDBB00CC6701000000000000000000DDEADD000F83190025BB + 4F0030C3690025BB4F0025BB4F0025BB4F0025BB4F0047CE7100EFF9F000FFFF + FE00FEFEFE0091DEA90016B4320016B4320009B2190009B2190009B2190009B2 + 190005710A00668E67000000000000000000000000000000000049AA49001899 + 2E0030C3690030C3690025BB4F0025BB4F0025BB4F0025BB4F0091DEA900FEFE + FE00FFFFFF00F2FAF50030C3690016B4320009B2190009B2190009B2190009B2 + 19000EA31B0004500700F3F3F30000000000000000000000000000000000B481 + 7600DAD9D900FEF0E200FEEBD700FEEBD700FEEBD700FEEBD700FEE4CA00FEE4 + CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7 + AB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFEFF00FFFF + FF004571FA004571FA004571FA00FFFCFB00FEFAF700F6EFE700A23F0800A23F + 0800A23F0800FEEBD700FEEBD700FEE4CA00059ACD00059ACD00059ACD00FEDD + BB00FEDDBB00FEDDBB00CC67010000000000000000007ACE7F0018992E0030C3 + 690030C3690030C3690030C3690030C3690047CE7100F2FAF500FFFFFF00FCFE + FE00EFF9F00025BB4F0016B4320016B4320016B4320016B4320009B2190009B2 + 190009B2190004500700DDEADD000000000000000000D9F3E2000F83190030C3 + 690030C3690030C3690030C3690030C3690025BB4F0025BB4F0047CE7100EFF9 + F000FFFFFF00FCFEFE00F8F8F80039C3390016B4320016B4320009B2190009B2 + 190009B2190005710A007DA9720000000000000000000000000000000000B481 + 7600DFDFDF00FEF0E200FEF0E200FEEBD700FEEBD700A9ACD400EADBD400FEE4 + CA00FEE4CA00FEDDBB00CCC0CC00FEDDBB00FEDDBB00FED7AB00FEDDBB00FED7 + AB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFFFE00FFFF + FF004571FA006682F1004571FA00FFFFFF00FEFAF700FEF9F300A23F0800A23F + 0800A23F0800F3E3D100FEEBD700FEEBD700059ACD00059ACD00059ACD00FEDD + BB00FEDDBB00FEDDBB00CC670100000000000000000018992E0025BB4F0030C3 + 690030C3690030C3690030C3690047CE7100F8FBFB00FFFFFF00FEFFFC00F2FA + F50047CE710025BB4F0016B4320016B4320016B4320016B4320016B4320009B2 + 190009B2190005710A007DA9720000000000000000007ACE7F0018992E0030C3 + 690030C3690030C3690030C3690030C3690030C3690025BB4F0025BB4F0047CE + 7100F2FAF500FFFEFF00FFFFFF00EFF9F00030C3690016B4320016B4320009B2 + 190009B219000EA31B00255E280000000000000000000000000000000000B481 + 7600E1E0E000FEF4E900FEF0E200FEF0E20098A9EF000335FB00295AF700EADB + D400FEE4CA0098A9EF000335FB006682F100FED0B700FEDDBB00FEDDBB00FED7 + AB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFEFF00FFFF + FF004571FA004571FA004571FA00FFFFFF00FEFEFE00F8F8F800A23F0800A23F + 0800A23F0800FEF0E200FEF0E200FEEBD700059ACD00059ACD00059ACD00FEE4 + CA00FEE4CA00FEE4CA00CC67010000000000E1F1E100029A030030C3690030C3 + 690030C3690030C3690047CE7100F2FAF500FFFFFF00FFFFFF00F8FBFB0047CE + 710025BB4F0025BB4F0025BB4F0016B4320016B4320016B4320016B4320016B4 + 320016B432000F831900448A3E00000000000000000049AA490025BB4F0030C3 + 690030C3690030C3690030C3690030C3690030C3690030C3690030C3690030C3 + 690047CE7100F2FAF500FCFEFE00FFFFFE00F2FAF50030C3690016B4320016B4 + 320016B4320009B2190004500700DDEADD00000000000000000000000000BA8E + 8500E6E5E400FEF4E900FEF4E900E2D6E2000335FB000335FB000335FB00295A + F70098A9EF000335FB000335FB000335FB00A9ACD400FEDDBB00FEDDBB00FEDD + BB00FECFC2008C5D5C000000000000000000CC670100FFFFFF00FFFFFF00FFFF + FE00FFFEFF00FCFEFE00FEFFFC00FFFFFF00FFFEFF00FFFDF800FEF9F300FFF8 + EE00FEF4E900FEF0E200FEF0E200FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4 + CA00FEE4CA00FEE4CA00CC67010000000000BBE5B9000EA31B0030C3690030C3 + 690030C3690058E08700F9F6F500FCFEFE00FFFFFE00FFFFFE00AFE6BF0091DE + A90091DEA90091DEA90091DEA90091DEA90091DEA90091DEA90047CE710016B4 + 320016B432000EA31B0005710A0000000000000000000EA31B0025BB4F0030C3 + 690030C3690058E08700AFE6BF0091DEA90091DEA90091DEA90091DEA90091DE + A90091DEA900AFE6BF00FFFFFF00FFFEFF00FFFFFF00F2FAF50030C3690016B4 + 320016B4320016B4320005710A00C0D5C200000000000000000000000000BA8E + 8500E6E5E400FFF8EE00FEF4E900FEF4E90098A9EF000335FB000335FB000335 + FB000335FB000335FB000335FB006682F100FEDDBB00FEE4CA00FEDDBB00FEDD + BB00FED6C900986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF + FF00B5B3B300B5B3B300B5B3B300FFFFFE00FFFFFE00FFFFFF00B5B3B300B5B3 + B300AFAFAF00FFF8EE00FEF4E900FEF0E200AFAFAF00AFAFAF00B9AAA600FEE4 + CA00FEE4CA00FEE4CA00CC67010000000000BBE5B9000EA31B0030C3690030C3 + 690047CE7100F2FAF500FFFFFF00FFFFFE00FCFEFE00FFFFFF00FFFFFF00FFFE + FF00FFFFFF00FFFEFF00FCFEFE00FFFFFF00FFFEFF00FEFFFC0091DEA90016B4 + 320016B432000EA31B0005710A000000000000000000029A030030C3690030C3 + 690030C3690091DEA900FFFFFF00FFFEFF00FEFEFE00FFFFFF00FFFFFF00FEFE + FE00FFFFFF00FFFEFF00FFFFFF00FFFEFF00FFFFFF00FEFEFE00F2FAF50030C3 + 690025BB4F0016B4320005710A00C0D5C200000000000000000000000000BA8E + 8500E6E5E400FEF9F300FFF8EE00FEF4E900FEF4E900BEC6DC000335FB000335 + FB000335FB000335FB006682F100FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD + BB00FECFC200986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF + FF00FEFEFE00FFFFFF00FFFFFE00FFFEFF00FFFFFF00FFFFFE00FFFFFF00FFFF + FF00FEF9F300FFF8EE00FFF8EE00FFF8EE00FEF4E900FEF4E900FEEBD700FEEB + D700FEEBD700FEEBD700CC67010000000000BBE5B9000EA31B0030C3690030C3 + 690058E08700F0F6FD00FFFFFF00FCFEFE00FFFEFF00FFFEFF00FFFEFF00FFFF + FF00FEFEFE00FFFEFF00F9FFFE00F9FFFE00FFFFFF00FFFFFF0091DEA90025BB + 4F0016B4320016B4320005710A000000000000000000029A030030C3690030C3 + 690030C3690091DEA900FBFBFC00FCFEFE00FFFFFF00F9FFFE00FCFEFE00FFFF + FF00FFFFFF00FFFFFF00FCFEFE00FFFEFF00FCFEFE00FFFFFF00D9F3E20025BB + 4F0016B4320016B432000F831900C0D5C200000000000000000000000000CB9A + 8200ECEAEA00FEFAF700FEF9F300FFF8EE00FFF8EE0098A9EF000335FB000335 + FB000335FB000335FB00295AF700EADBD400FEE4CA00FEE4CA00FEE4CA00FEDD + BB00FED6C900986B66000000000000000000CC670100FFFFFF00FFFFFF00FCFE + FE00CC9A9900CC9A9900CC9A9900FFFEFF00FEFFFC00FFFFFF00E27E0300E27E + 0300E27E0300FEF9F300FEFAF700FEF4E900029A0300029A0300029A0300FEEB + D700FEEBD700FEEBD700CC67010000000000BBE5B90009B2190030C3690030C3 + 690030C3690047CE7100F3F3F300FFFEFF00FFFFFF00FCFEFE00D9F3E20091DE + A90091DEA90091DEA900AFE6BF0091DEA90091DEA90091DEA90047CE710025BB + 4F0025BB4F0025A63D002A8C4300000000000000000016B4320030C3690030C3 + 690030C3690058E08700AFE6BF0091DEA90091DEA900AFE6BF0091DEA90091DE + A90091DEA900D9F3E200FFFFFF00FFFEFF00FFFFFF00D9F3E20030C3690025BB + 4F0025BB4F0025BB4F0005710A00C0D5C200000000000000000000000000CB9A + 8200EFEFEF00FFFDF800FEFAF700FEF9F30098A9EF000335FB000335FB000335 + FB000335FB000335FB000335FB00295AF700EADBD400FEE4CA00FEE4CA00FEE4 + CA00FED6C900986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF + FF00CC9A9900CC9A9900CC9A9900FFFFFF00FFFEFF00FCFEFE00E27E0300E27E + 0300E27E0300FBFBFC00FEFAF700FFF8EE00029A0300029A0300029A0300FEF0 + E200FEF0E200FEF0E200CC67010000000000EFF9F000029A030047CE710047CE + 710030C3690030C3690058E08700F0F6FD00FFFEFF00FFFFFF00FFFFFF0091DE + A90030C3690030C3690030C3690030C3690030C3690030C3690025BB4F0025BB + 4F0025BB4F0018992E0049AA4900000000000000000039C3390039C3390047CE + 710030C3690030C3690030C3690030C3690030C3690030C3690030C3690030C3 + 690091DEA900F9FFFE00FFFEFF00FEFEFE00D9F3E20030C3690030C3690025BB + 4F0025BB4F0025BB4F0005710A0000000000000000000000000000000000CB9A + 8200EFEFEF00FFFEFE00FDFAFA00DFEFF9000335FB000335FB000335FB006682 + F100D1CDE4000335FB000335FB000335FB0098A9EF00FEE4CA00FEE4CA00FEE4 + CA00FECFC200986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF + FE00CC9A9900CC9A9900CC9A9900FFFFFE00FFFFFF00FFFFFF00E27E0300E27E + 0300E27E0300FEFFFC00FFFEFF00FEFAF700029A0300029A0300029A0300FEF0 + E200FEF0E200FEF0E200CC670100000000000000000039C3390047CE71007ACE + 7F0047CE710030C3690030C3690047CE7100FCFEFB00FCFEFE00FFFFFF00FCFE + FE0091DEA90030C3690030C3690030C3690030C3690030C3690030C3690030C3 + 690025BB4F000F831900A1C79E00000000000000000091DEA90016B432007ACE + 7F0047CE710047CE710030C3690030C3690030C3690030C3690030C3690091DE + A900FFFFFE00FFFFFF00FCFEFE00D9F3E20030C3690030C3690030C3690030C3 + 690025BB4F0018992E0049AA490000000000000000000000000000000000CB9A + 8200F3F3F300FFFFFE00FEFEFE00FEFEFE0098A9EF000335FB006682F100FEF4 + E900FEF4E900D1CDE4000335FB006682F100FEEBD700FEEBD700FEEBD700FEE4 + CA00FECFC200986B66000000000000000000CC670100FFFFFF00FFFFFF00FFFF + FF00FEFEFE00FFFEFE00FFFFFF00FFFEFF00FFFFFE00FFFEFF00FFFEFF00FFFF + FE00FFFEFF00FFFFFF00FFFFFF00FDFAFA00FEFAF700FFF8EE00FEF4E900FEF0 + E200FEF0E200FEF0E200CC670100000000000000000091DEA90009B2190091DE + A9007ACE7F0058E0870047CE710030C3690051B49400F8FBFB00FFFCFF00FFFE + FF00FFFFFF0047CE710030C3690030C3690030C3690030C3690030C3690030C3 + 690025BB4F000F831900EFF9F0000000000000000000EFF9F00009B219007ACE + 7F0091DEA90058E0870047CE710030C3690030C3690030C3690058E08700FBFB + FC00FEFEFE00FFFEFF00D9F3E20047CE710030C3690030C3690030C3690030C3 + 690030C369000F831900AFD0B00000000000000000000000000000000000DCA8 + 8700F3F3F300FCFEFE00FFFEFF00FFFCFB00FEFAF700CED7FA00FFF8EE00FAF3 + F200FEF4E900FEF4E900E2D6E200FEF0E200FEEBD700FEEBD700FEEBD700FEEB + D700F3B9B500A47874000000000000000000CC670100FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FEFEFE00FFFFFF00FFFEFF00FFFFFF00FFFFFE00FFFF + FF00FFFFFF00FFFEFF00FEFFFC00FEFAF700FEF9F300FFF8EE00FEF4E900FEF0 + E200FEF0E200FEF0E200CC67010000000000000000000000000039C3390047CE + 710091DEA90091DEA9007ACE7F0058E0870030C3690058E08700F0FEFE00FFFF + FF00FBFBFC0058E0870030C3690030C3690030C3690030C3690030C3690030C3 + 69000F831900AFD0B000000000000000000000000000000000007ACE7F0016B4 + 320091DEA90091DEA90058E087007ACE7F0047CE710030C369007ACE7F00FFFE + FF00FEFEFE00D9F3E20047CE710030C3690030C3690030C3690030C3690030C3 + 690025A63D0049AA49000000000000000000000000000000000000000000DCA8 + 8700F9F6F500FFFFFF00FFFFFF00FFFFFF00FEFEFE00FFFDF800FEFAF700FFF8 + EE00FFF8EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700F3B9B500FCA3 + A200FCA3A200A47874000000000000000000CC670100E27E0300E27E0300E27E + 0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E + 0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E0300E27E + 0300E27E0300E27E0300CC670100000000000000000000000000D8EBCC0009B2 + 190091DEA90091DEA90091DEA9007ACE7F0058E0870047CE710051B49400AFE6 + BF00AFE6BF0030C3690030C3690030C3690030C3690030C3690030C369001899 + 2E0049AA490000000000000000000000000000000000000000000000000039C3 + 390039C33900AFE6BF0091DEA90091DEA90058E0870047CE710047CE710091DE + A900AFE6BF0047CE710030C3690030C3690030C3690030C3690030C3690025BB + 4F000F831900D9F3E2000000000000000000000000000000000000000000DCA8 + 8700F8F8F800FFFEFF00FFFFFF00FFFEFF00FFFFFF00FFFEFF00FFFDF800FEFA + F700FFF8EE00FFF8EE00FEF4E900FEF4E900FEF4E900E2BDB300D6A89400DCA8 + 8700CB9A8200A47874000000000000000000D3936400CC670100CC670100CC67 + 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 + 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 + 0100CC670100CC670100CC6701000000000000000000000000000000000091DE + A90009B2190091DEA900BBE5B90091DEA90091DEA9007ACE7F0047CE710047CE + 710030C3690030C3690030C3690030C3690030C3690030C369000EA31B0025A6 + 3D00EFF9F000000000000000000000000000000000000000000000000000EFF9 + F00039C3390039C33900BBE5B90091DEA90091DEA9007ACE7F0058E0870051B4 + 940030C3690030C3690030C3690030C3690030C3690030C3690025BB4F00029A + 0300A1C79E00000000000000000000000000000000000000000000000000E3B1 + 8E00FAFAFA00FFFFFF00FFFFFF00FFFEFE00FFFFFF00FFFFFF00FFFEFE00FFFD + F800FEFAF700FEF9F300FFF8EE00FEF4E900FEF4E900B4817600B4817600B481 + 7600B4817600A47874000000000000000000F3E3D100C56E2600CC670100CC67 + 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 + 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 + 0100CC670100CC670100EAC8A100000000000000000000000000000000000000 + 0000BBE5B90009B2190047CE7100BBE5B900AFE6BF0091DEA90091DEA90058E0 + 870047CE710030C3690030C3690030C3690025BB4F000EA31B0025A63D00EFF9 + F000000000000000000000000000000000000000000000000000000000000000 + 0000EFF9F00039C3390039C3390091DEA900BBE5B90091DEA90091DEA90058E0 + 870058E0870030C3690030C3690030C3690030C3690016B432000EA31B00AFD0 + B00000000000000000000000000000000000000000000000000000000000E3B1 + 8E00FBFBFC00FFFFFF00FFFEFF00FFFFFE00FFFEFF00FFFFFE00FFFFFF00FFFF + FF00FFFDF800FFFDF800FEFAF700FFF8EE00FFF8EE00B4817600FCB04C00DCA8 + 8700CB9A8200E4D1CD0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000E1F1E10047CE710009B2190039C339007ACE7F007ACE7F007ACE + 7F007ACE7F0030C3690025BB4F000EA31B000EA31B007ACE7F00000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000007ACE7F0009B2190039C339007ACE7F007ACE7F007ACE + 7F007ACE7F0047CE710025BB4F0016B43200029A030047CE7100E1F1E1000000 + 000000000000000000000000000000000000000000000000000000000000E3B1 + 8E00FEFEFE00FFFFFF00FFFFFE00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FEFE + FE00FFFFFF00FFFDF800FDFAFA00FEF9F300FEF9F300B4817600DCA88700CB9A + 8200EADBD4000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000D9F3E2007ACE7F0039C3390009B2190009B2 + 190009B2190039C3390039C3390091DEA900EFF9F00000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000EFF9F00091DEA90039C3390039C3390009B2 + 190009B2190009B2190039C3390091DEA900E1F1E10000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000E3B1 + 8E00FCFEFE00FCFEFE00FEFEFE00FBFBFC00FAFAFA00F8F8F800F8F8F800F3F3 + F300F3F3F300F3F3F300EFEFEF00ECEAEA00F6EFE700B4817600DCA88700EADB + D400000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 + 8700DCA88700DCA88700DCA88700DCA88700DCA88700B4817600F3E3D1000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000FAF3F200D6A89400D6A89400F1E3E20000000000000000000000 + 0000000000000000000000000000FAF3F200DAB4A000D6A89400FAF3F2000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000A4787400A4787400A4787400A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 7400A47874008C5D5C0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000F3F3F300A23F0800A23F0800AF4B0200A23F0800DAB4A000000000000000 + 00000000000000000000EADBD400A23F0800AF4B0200AF4B0200A23F0800EADB + D400000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000A4787400A4787400A4787400A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 7400A4787400A47874008C5D5C00000000000000000000000000000000000000 + 000000000000000000000000000000000000A4787400D8C5B500FECFC200FECF + C200FECFC200FECFC200FECFC200FECFC200FECFC200FECFC200FECFC200FECF + C200FECFC2008C5D5C0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000D3936400AF4B0200AF4B0200A23F0800AF4B0200A23F0800EADBD4000000 + 000000000000F5EADF00A23F0800AF4B0200A23F0800AF4B0200BD580100BD79 + 5800000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000B4817600DBC9C600FECFC200FECF + C200FECFC200FECFC200FECFC200FECFC200FECFC200FECFC200FECFC200FECF + C200FECFC200FECFC2008C5D5C00000000000000000000000000000000007ECB + E60041B3DA0041B3DA0041B3DA0041B3DA00A4787400DBC9C600F6EFE700F6EF + E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF + E700FEDDBB008C5D5C00000000000000000000000000059ACD00059ACD00D1EF + F900000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000B3664100BD580100CB9A8200F8F3EC00BD795800AF4B0200BD7958000000 + 000000000000B4817600AF4B0200BD79580000000000BD795800BD580100AB56 + 2900000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000B4817600D8C5B500F6EFE700F6EF + E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF + E700F6EFE700FEDDBB008C5D5C000000000000000000000000007ECBE6001DA6 + D50060D4F00071EBF90071EBF90071EBF900B4817600E5CDBE00F6EFE700FEDD + BB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FED7AB00F6EF + E700FEDDBB00986B6600000000000000000000000000059ACD0064C1E1004BBD + E000059ACD0041B3DA007ECBE600BEE6F500F0F6FD0000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000B3664100AF4B0200E2BDB30000000000F1E3E200AF4B0200AF4B02000000 + 000000000000AB562900AF4B02000000000000000000E2BDB300AF4B0200AB56 + 2900000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000B4817600E5CDBE00F6EFE700F6EF + E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF + E700F6EFE700FEDDBB008C5D5C0000000000000000000000000041B3DA008DF3 + FE0070D2FD0070D2FD0070D2FD0070D2FD00B4817600DED2CA00F6EFE700FECC + 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00F6EF + E700FEDDBB008C5D5C00000000000000000000000000059ACD004BBDE000D1EF + F90070D2FD0056C6F00037B4E0001DA6D500059ACD002EACD80064C1E100BEE6 + F500DFEFF9000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000BD795800BD580100BD79580000000000F8F3EC00AF4B0200AF4B02000000 + 000000000000AF4B0200AF4B02000000000000000000BD795800CC670100BD79 + 5800000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000BA8E8500E5CDBE00F6EFE700FEE4 + CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7 + AB00F6EFE700FEDDBB008C5D5C0000000000000000000000000041B3DA008DF3 + FE0070D2FD0070D2FD0070D2FD0070D2FD00BA8E8500DED2CA00F6EFE700F6EF + E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF + E700FEDDBB00986B6600000000000000000000000000059ACD00059ACD00F0F6 + FD007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE0056C6F0004BBDE0001DA6 + D500059ACD001DA6D50064C1E10090D1F100D1EFF90000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000E5CDBE00AF4B0200AF4B0200BD795800BD795800BD580100AF4B0200F1E3 + E20000000000A23F0800BD580100BD795800BD795800BD580100AF4B0200DAB4 + A000000000000000000000000000000000000000000000000000A4787400A478 + 7400A4787400A4787400A4787400A4787400BA8E8500DED2CA00F6EFE700FECC + 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC + 9A00F6EFE700FEDDBB008C5D5C0000000000000000000000000041B3DA008DF3 + FE007BDDFE007BDDFE007BDDFE007BDDFE00BA8E8500ECDACC00F6EFE700FEEB + D700FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00F6EF + E700FEDDBB00986B6600000000000000000000000000059ACD0056C6F0009EDE + EE009AE3FF007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE007BDDFE007BDD + FE0070D2FD003A80E00037B4E00037B4E000059ACD001DA6D500ACDBF4000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000BD795800AF4B0200CC670100CC670100CC670100AF4B0200DAB4 + A000E5CDBE00AF4B0200CC670100BD580100CC670100BD580100BD7958000000 + 0000000000000000000000000000000000000000000000000000A4787400FED6 + C900FED6C900FED6C900FED6C900FED6C900BA8E8500E4D1CD00F6EFE700F6EF + E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700FEF0E200F6EF + E700F6EFE700FEDDBB00986B660000000000000000000000000041B3DA0099FD + FE007BDDFE007BDDFE007BDDFE007BDDFE00CB9A8200EADBD400F6EFE700FECC + 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00F6EF + E700FEDDBB00986B6600000000000000000000000000059ACD0070D2FD004BBD + E000D4FDFF0084E9FE0084E9FE0084E9FE0084E9FE0084E9FE0071EBF9007BDD + FE00295AF7000335FB004493ED0084E9FE0084E9FE0056C6F00037B4E0000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000CB9A8200A23F0800A23F0800A23F0800A23F0800BD79 + 5800B4817600A23F0800A23F0800A23F0800A23F0800BA8E8500FAF3F2000000 + 0000000000000000000000000000000000000000000000000000A4787400F6EF + E700F6EFE700F6EFE700F6EFE700F6EFE700CB9A8200ECDACC00F6EFE700FED7 + AB00FED7AB00FED7AB00FED7AB00FED7AB00FED7AB00FED7AB00F3D1A300F3D1 + A300F6EFE700FEDDBB00986B660000000000000000000000000041B3DA0099FD + FE0084E9FE0084E9FE0084E9FE0084E9FE00CB9A8200EADBD400F6EFE700F6EF + E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF + E700FEDDBB00986B6600000000000000000000000000059ACD0090D1F1002EAC + D800F9FFFE0084E9FE008DF3FE008DF3FE008DF3FE0084E9FE008DF3FE004571 + FA000335FB000335FB00295AF7008DF3FE008DF3FE0061C6FE0060D4F000BEE6 + F500000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000B4817600AB562900826A + 5B00826A5B00B3664100BD795800000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000A4787400F6EF + E700F6EFE700F6EFE700F6EFE700F8F3EC00CB9A8200EADBD400F6EFE700FECC + 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC + 9A00F6EFE700FEDDBB00986B660000000000000000000000000041B3DA00ADFB + FE0084E9FE0084E9FE0084E9FE0084E9FE00CB9A8200EFE4D800F9F6F500FEDD + BB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00F6EF + E700FEDDBB00A4787400000000000000000000000000059ACD0070D2FD0037B4 + E000ACDBF400ADFBFE008DF3FE008DF3FE008DF3FE0099FDFE004571FA000335 + FB000335FB000335FB000335FB007BDDFE008DF3FE0061C6FE0099FDFE004BBD + E000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000F6EFE700826A5B00927D + 7000B1A09700826A5B00DED7D300000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000B4817600F6EF + E700FEDDBB00FEDDBB00FEDDBB00FED7AB00CB9A8200EFE4D800F6EFE700F6EF + E700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EFE700F6EF + E700F6EFE700FEDDBB00A478740000000000000000000000000041B3DA00ADFB + FE008DF3FE0084E9FE008DF3FE0084E9FE00DCA88700EFE4D800F9F6F500FECC + 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00F6EF + E700FEDDBB00A4787400000000000000000000000000059ACD009AE3FF0056C6 + F00064C1E100D4FDFF0099FDFE0099FDFE0099FDFE004571FA000335FB000335 + FB005DB2FC00295AF7000335FB005DB2FC0099FDFE0070D2FD00C2FDFF004BBD + E000DFEFF9000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000C3C2C200826A5B00F1E3 + E200C4C4C400826A5B00A4968E00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000B4817600F6EF + E700FECC9A00FECC9A00FECC9A00FECC9A00DCA88700EFE4D800F9F6F500FEDD + BB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDD + BB00F6EFE700FEDDBB00A478740000000000000000000000000041B3DA00ADFB + FE008DF3FE008DF3FE008DF3FE008DF3FE00DCA88700F5EADF00FFFEFE00FFFF + FF00FFFCFB00FEFAF700FFF8EE00FEF4E900FEF4E900FEF0E200FEEBD700FEE4 + CA00FED0B700A4787400000000000000000000000000059ACD009AE3FF0061C6 + FE0037B4E000F0FEFE0099FDFE00ADFBFE00399CFE000335FB000335FB005DB2 + FC0099FDFE004BAFFF000335FB004571FA0099FDFE0061C6FE00C2FDFF00ADFB + FE0064C1E1000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000ECEAEA00826A5B00CABCB700B1A0 + 9700826A5B00B9AAA600826A5B00DED7D3000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000B4817600F6EF + E700F6EFE700F6EFE700F6EFE700F6EFE700DCA88700EFE4D800F9F6F500FECC + 9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC9A00FECC + 9A00F6EFE700FEDDBB00A478740000000000000000000000000041B3DA00C2FD + FF008DF3FE0099FDFE008DF3FE0099FDFE00E3B18E00F5EADF00FFFFFF00FEFE + FE00FFFFFF00FFFCFF00FEF9F300FEF9F300FFF8EE00FEF0E200F3E3D100F3B9 + B500FCA3A200B48176000000000000000000000000001DA6D5009AE3FF0070D2 + FD004BBDE0009EDEEE00F0FEFE00D4FDFF0090D1F100295AF7005DB2FC00C2FD + FF00ADFBFE009AE3FF000335FB000335FB00ADFBFE0061C6FE00D4FDFF00D4FD + FF004BBDE000F0FEFE0000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000A4968E00B9AAA600B9B9B900826A + 5B00927D7000B9AAA600C8ABAB00927D70000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000BA8E8500F8F3 + EC00FEDDBB00FEDDBB00FEDDBB00FEDDBB00DCA88700F5EADF00FFFFFF00FFFF + FE00FFFFFF00FDFAFA00FEFAF700FFF8EE00FEF4E900FEF4E900FEF0E200FEF0 + E200F6EFE700FEDDBB00A478740000000000000000000000000041B3DA00C2FD + FF0099FDFE0099FDFE0099FDFE0099FDFE00E3B18E00FEF0E200FFFEFF00FFFE + FF00FFFFFE00FFFFFF00FFFCFB00FEFAF700FFF8EE00FFF8EE00B4817600B481 + 7600B4817600B48176000000000000000000000000001DA6D5009AE3FF007BDD + FE007BDDFE004BBDE0004BBDE0004BBDE0004BBDE000ACDBF400D1EFF900F0FE + FE00C2FDFF00C2FDFF00295AF7000335FB005DB2FC0061C6FE00E4FDFE00E4FD + FE00BEE6F5007ECBE60000000000000000000000000000000000000000000000 + 0000000000000000000000000000ECEAEA00826A5B00CCCACA00A4968E00C6B8 + AB00DED7D300927D7000C0C0C000927D7000DED7D30000000000000000000000 + 0000000000000000000000000000000000000000000000000000BA8E8500F8F3 + EC00FECC9A00FECC9A00FECC9A00FECC9A00E3B18E00F5EADF00FFFEFF00FFFE + FF00FFFFFE00FFFEFF00FEFAF700FEF9F300FFF8EE00FEF4E900FEF4E900FEE4 + CA00F3B9B500FCA3A200B481760000000000000000000000000041B3DA00D4FD + FF0099FDFE0099FDFE0099FDFE0099FDFE00EDBD9200FEF0E200FFFFFE00FFFE + FF00FFFFFF00FFFFFE00FFFFFF00FCFEFE00FEFAF700FFF8EE00B4817600FCC4 + 7C00DCA88700E2BDB3000000000000000000000000001DA6D5009AE3FF0084E9 + FE0084E9FE0084E9FE0084E9FE0084E9FE007BDDFE0060D4F00037B4E00064C1 + E100F8FBFB00E4FDFE0098A9EF000335FB00295AF70070D2FD00F0FEFE00F0FE + FE00F0FEFE004BBDE00000000000000000000000000000000000000000000000 + 0000000000000000000000000000B9AAA600B9AAA600C7C7C700927D70000000 + 000000000000A18C8200B1A09700B9AAA600B1A0970000000000000000000000 + 0000000000000000000000000000000000000000000000000000CB9A8200F9F6 + F500FEF9F300FEF9F300FEF4E900FEF4E900E3B18E00F5EADF00FFFFFF00FCFE + FE00FFFFFF00FFFEFF00FEFFFC00FFFCFB00FEF9F300FFF8EE00FFF8EE00B481 + 7600B4817600B4817600B481760000000000000000000000000041B3DA00D4FD + FF0099FDFE00ADFBFE0099FDFE00ADFBFE00EDBD9200FEF4E900FFFFFE00FEFE + FE00FBFBFC00FBFBFC00FAFAFA00FAFAFA00F9F6F500F8F3EC00B4817600E3B1 + 8E00D8C5B500000000000000000000000000000000001DA6D500ADFBFE008DF3 + FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE008DF3FE0060D4 + F0004BBDE0009EDEEE00ACDBF4000335FB000335FB00ACDBF400FFFFFE00FFFE + FF00FFFCFB00BEE6F5004BBDE000000000000000000000000000000000000000 + 0000000000000000000000000000927D7000F3F3F300927D7000DED7D3000000 + 000000000000ECEAEA00927D7000E1E0E000826A5B0000000000000000000000 + 0000000000000000000000000000000000000000000000000000CB9A8200FAFA + FA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00EDBD9200FEF0E200FFFEFF00FFFF + FF00FFFFFE00FCFEFE00FFFFFF00FEFEFE00FFFDF800FEF9F300FEFAF700B481 + 7600FCC47C00CB9A8200E2BDB30000000000000000000000000041B3DA00D4FD + FF00ADFBFE00ADFBFE00ADFBFE00ADFBFE00EDBD9200DCA88700DCA88700DCA8 + 8700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700B4817600E5CD + BE0000000000000000000000000000000000000000001DA6D500ADFBFE0099FD + FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD + FE0099FDFE0071EBF90060D4F0004571FA000335FB00295AF7004BBDE0004BBD + E0004BBDE0004BBDE0004BBDE000000000000000000000000000000000000000 + 00000000000000000000E1E0E000A18C8200C6B8AB00B1A09700000000000000 + 00000000000000000000C6B8AB00B1A09700B9AAA600C3C2C200000000000000 + 0000000000000000000000000000000000000000000000000000DCA88700FBFB + FC00FECC9A00FECC9A00FECC9A00FECC9A00EDBD9200FEF0E200FCFEFB00FCFE + FB00F8FBFB00F9F6F500F9F6F500F3F3F300F3F3F300EFEFEF00EFEFEF00B481 + 7600E3B18E00D8C5B5000000000000000000000000000000000041B3DA00E4FD + FE00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FD + FF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF00C2FDFF0090D1F100059A + CD0000000000000000000000000000000000000000001DA6D500ADFBFE0099FD + FE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FDFE0099FD + FE0099FDFE0099FDFE0099FDFE0070D2FD000335FB000335FB00000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000B1A09700B1A09700826A5B0000000000000000000000 + 0000000000000000000000000000927D7000B1A09700A18C8200000000000000 + 0000000000000000000000000000000000000000000000000000DCA88700FBFB + FC00FFFEFF00FFFFFF00FFFDF800FEF9F300EDBD9200DCA88700DCA88700DCA8 + 8700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700B481 + 7600E5CDBE00000000000000000000000000000000000000000041B3DA00E4FD + FE00D4FDFF00C2FDFF009EDEEE009EDEEE009EDEEE009AE3FF009EDEEE009AE3 + FF009EDEEE009AE3FF009EDEEE009EDEEE00D4FDFF00D4FDFF0090D1F100059A + CD0000000000000000000000000000000000000000001DA6D500E4FDFE0099FD + FE0099FDFE0099FDFE0099FDFE0099FDFE00F0FEFE00BEE6F500BEE6F500E4FD + FE00D4FDFF00C2FDFF00ADFBFE00ADFBFE00295AF7000335FB0098A9EF00F0F6 + FD00000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000927D7000826A5B00DED2CA0000000000000000000000 + 0000000000000000000000000000E1E0E000826A5B00826A5B00000000000000 + 0000000000000000000000000000000000000000000000000000E3B18E00FBFB + FC00FFFFFF00FFFFFE00FEFEFE00FFFDF800FEFAF700FFF8EE00FEF4E900FEF0 + E200FEEBD700D6A89400D6A89400B48176000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000041B3DA00F0FE + FE00D4FDFF009BC3D800A18C8200A18C8200A18C8200A18C8200A18C8200A18C + 8200A18C8200A18C8200A18C8200A18C820090D1F100D4FDFF0090D1F100059A + CD0000000000000000000000000000000000000000002EACD80090D1F100D4FD + FF00D4FDFF00C2FDFF00ADFBFE00E4FDFE0064C1E1001DA6D5001DA6D5001DA6 + D5001DA6D5001DA6D5001DA6D5001DA6D5001DA6D5000335FB004571FA000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000FAF3F200826A5B00927D70000000000000000000000000000000 + 000000000000000000000000000000000000B1A097006F5B4D00DED7D3000000 + 0000000000000000000000000000000000000000000000000000E3B18E00FFFE + FE00FFFFFE00FFFFFF00FFFFFF00FFFFFF00FFFCFB00FEFAF700FFF8EE00FFF8 + EE00B4817600B4817600B4817600B48176000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000041B3DA00F0FE + FE00E4FDFE00B1A09700DED7D300DED7D300DED7D300DED7D300DED7D300DED7 + D300DED7D300DED7D300DED7D3008C8988004BBDE000E4FDFE0090D1F100059A + CD000000000000000000000000000000000000000000000000001DA6D5007ECB + E6007ECBE6009EDEEE00BEE6F500ACDBF4001DA6D50000000000000000000000 + 00000000000000000000000000000000000000000000CED7FA00DFEFF9000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000DED7D3006F5B4D00ECEAEA000000000000000000000000000000 + 00000000000000000000000000000000000000000000826A5B00CABCB7000000 + 0000000000000000000000000000000000000000000000000000EDBD9200FFFE + FE00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFEFE00FFFDF800FEFAF700FFF8 + EE00B4817600FCC47C00DCA88700E2BDB3000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000007ECBE6002EAC + D80099FDFE009BC3D800A4968E00C6B8AB00FFFFFF00FFFFFF00FCFEFE00FFFE + FF00FFFFFE00DED7D300A4968E008791850041B3DA007ECBE600059ACD00DFEF + F900000000000000000000000000000000000000000000000000000000001DA6 + D5001DA6D5001DA6D5001DA6D5001DA6D5000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000ECEAEA00DBC9C600000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000DED7D300DED7D3000000 + 0000000000000000000000000000000000000000000000000000EDBD9200FCFE + FE00FFFCFF00FEFEFE00FCFEFB00FAFAFA00F8F8F800F8F8F800F9F6F500F8F3 + EC00B4817600E3B18E00D8C5B500000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000000000000000000090D1 + F10041B3DA0041B3DA0041B3DA00A18C8200CABCB700CABCB700CABCB700CABC + B700CABCB700B9AAA60066A6C30041B3DA0041B3DA004BBDE000BEE6F5000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000EDBD9200DCA8 + 8700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 + 8700B4817600E5CDBE0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000A18C8200A18C8200A18C8200A18C8200A18C + 8200A18C8200A18C8200DBC9C600000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000CED7FA00295AF70098A9EF00000000000000000000000000000000000000 + 000000000000000000000000000000000000EADBD400D3936400000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000FAF3F200A23F0800DAB4A00000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 7400A4787400A4787400A4787400A4787400A4787400A4787400A4787400A478 + 74008C5D5C000000000000000000000000000000000000000000D0E3EC00D1CD + E400ECEAEA000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000CED7 + FA000732DE000732DE000732DE00000000000000000000000000000000000000 + 0000000000000000000000000000EADBD400A23F0800A23F0800D39364000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000AB562900BD580100A23F0800DAB4A000000000000000 + 000000000000000000000000000000000000000000000000000000000000A478 + 7400ECDACC00FED6C900FED6C900FED6C900FED6C900FED6C900FED6C900FED6 + C900FED0B700FECFC200FECFC200FED0B700FED0B700FED0B700FED0B700FED0 + B7008C5D5C0000000000000000000000000000000000F0F6FD000732DE000732 + DE000732DE00C7DDE10000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000D1CDE4000732 + DE000732DE000732DE00CED7FA00000000000000000000000000000000000000 + 00000000000000000000EADBD400AF4B0200CC670100CC670100A23F0800DAB4 + A000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000BD795800BD580100CC670100CC670100A23F0800DAB4A0000000 + 000000000000000000000000000000000000000000000000000000000000A478 + 7400F3E3D100FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDDBB00FEDD + BB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FECC9A00FED0 + B7008C5D5C0000000000000000000000000000000000D1CDE4000732DE000732 + DE000732DE000732DE00CED7FA00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000098A9EF000732DE000732 + DE000732DE00CED7FA0000000000000000000000000000000000000000000000 + 000000000000EADBD400A23F0800BD580100CC670100CC670100AF4B0200D6A8 + 9400000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000DAB4A000A23F0800CC670100CC670100CC670100A23F0800E5CD + BE0000000000000000000000000000000000000000000000000000000000A478 + 7400EFE4D800FEEBD700FEEBD700FEE4CA00FEE4CA00FEE4CA00FEE4CA00FEDD + BB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED7AB00FED0 + B7008C5D5C0000000000000000000000000000000000F0F6FD000732DE000732 + DE000732DE000732DE000732DE00BEC6DC000000000000000000000000000000 + 00000000000000000000000000000000000098A9EF000732DE000732DE000732 + DE00CED7FA000000000000000000000000000000000000000000000000000000 + 0000F8F0F000AB562900BD580100CC670100CC670100AF4B0200DAB4A0000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000DAB4A000A23F0800CC670100CC670100CC670100A23F + 0800FAF3F200000000000000000000000000000000000000000000000000A478 + 7400F1E3E200FEEBD700FEEBD700FEEBD700FEE4CA00FEE4CA00FED6C900FEDD + BB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FED7AB00FED0 + B7008C5D5C000000000000000000000000000000000000000000F3F3F300295A + F7000732DE000732DE000732DE000732DE0098A9EF0000000000000000000000 + 00000000000000000000000000006682F1000732DE000732DE000732DE00CED7 + FA00000000000000000000000000000000000000000000000000000000000000 + 0000D3936400AF4B0200CC670100CC670100AF4B0200BD795800000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000DAB4A000A23F0800CC670100CC670100BD58 + 0100B3664100000000000000000000000000000000000000000000000000B481 + 7600F5EADF00FEF0E200FEEBD700FEEBD700FEE4CA00F3E3D100CCC0CC0098A9 + EF00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED7AB00FECF + C2008C5D5C000000000000000000000000000000000000000000000000000000 + 00006682F1000732DE000732DE000732DE000732DE0098A9EF00000000000000 + 000000000000000000006682F1000732DE000732DE000732DE00CED7FA000000 + 000000000000000000000000000000000000000000000000000000000000E5CD + BE00A23F0800CC670100CC670100AF4B0200BD79580000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000CB9A8200AF4B0200CC670100CC67 + 0100A23F0800E2BDB3000000000000000000000000000000000000000000B481 + 7600F5EADF00FEF0E200FEF0E200FEEBD700EFE4D8006682F1000335FB000335 + FB00CFC4D600FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED7AB00FED0 + B7008C5D5C000000000000000000000000000000000000000000000000000000 + 00000000000098A9EF000732DE000732DE000732DE000732DE0098A9EF000000 + 0000F3F3F300295AF7000732DE000732DE000732DE00CED7FA00000000000000 + 000000000000000000000000000000000000000000000000000000000000B366 + 4100BD580100CC670100BD580100AB562900F8F0F00000000000ECDACC00A23F + 0800AF4B0200AF4B0200AF4B0200AF4B0200AF4B0200AF4B0200AF4B0200AF4B + 0200AF4B0200AF4B0200B3664100000000000000000000000000B3664100BD58 + 0100BD580100BD580100BD580100BD580100BD580100BD580100BD580100BD58 + 0100BD580100A23F0800E2BDB3000000000000000000B3664100BD580100CC67 + 0100CC670100AB5629000000000000000000000000000000000000000000B481 + 7600F6EFE700FEF4E900FEF0E20098A9EF000335FB000335FB000335FB000335 + FB00295AF700FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FED0 + B700986B66000000000000000000000000000000000000000000000000000000 + 00000000000000000000CED7FA00295AF7000732DE000732DE000732DE0098A9 + EF00295AF7000732DE000732DE000732DE00CED7FA0000000000000000000000 + 0000000000000000000000000000000000000000000000000000E5CDBE00AF4B + 0200CC670100CC670100A23F0800E5CDBE000000000000000000CB9A8200BD58 + 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 + 0100CC670100CC670100B3664100000000000000000000000000B3664100CC67 + 0100CC670100CC670100CC670100CC670100CC670100CC670100CC670100CC67 + 0100CC670100BD580100CB9A82000000000000000000F5EADF00A23F0800CC67 + 0100CC670100AF4B0200DAB4A00000000000000000000000000000000000B481 + 7600F8F3EC00FEF4E90098A9EF000335FB000335FB000335FB0098A9EF000335 + FB000335FB00A9ACD400FEE4CA00FEDDBB00FEDDBB00FEDDBB00FEDDBB00FECF + C200986B66000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000F0F6FD004571FA000732DE000335FB000732 + DE000732DE000732DE000732DE00CED7FA000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000BD795800CC67 + 0100E27E0300BD580100BD795800000000000000000000000000D6A89400AF4B + 0200BD580100BD580100BD580100BD580100BD580100CC670100CC670100CC67 + 0100CC670100CC670100B3664100000000000000000000000000B3664100E27E + 0300E27E0300CC670100CC670100BD580100AF4B0200AF4B0200AF4B0200AF4B + 0200AF4B0200A23F0800ECDACC00000000000000000000000000B4817600BD58 + 0100CC670100BD580100BD79580000000000000000000000000000000000BA8E + 8500F8F3EC00FFF8EE00ECEAEA000335FB006682F100F1E3E200FEEBD7006682 + F1000335FB000335FB00FED6C900FEE4CA00FEDDBB00FEDDBB00FEDDBB00FED6 + C900986B66000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000006682F1000732DE000732 + DE000335FB000732DE00CED7FA00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000AF4B0200E27E + 0300E27E0300AF4B0200F5EADF0000000000000000000000000000000000D6A8 + 9400CB9A8200CB9A8200CB9A8200D3936400A23F0800BD580100CC670100CC67 + 0100CC670100CC670100B3664100000000000000000000000000B3664100E27E + 0300E27E0300E27E0300CC670100CC670100A23F0800D6A89400E5CDBE00E5CD + BE00E5CDBE00F1E3E20000000000000000000000000000000000F5EADF00AF4B + 0200CC670100CC670100A23F080000000000000000000000000000000000BA8E + 8500FAF3F200FEF9F300FFF8EE00FEF4E900FEF4E900FEF0E200FEF0E200EFE4 + D8000335FB000335FB006682F100FEE4CA00FEE4CA00FEE4CA00FEDDBB00FED6 + C900986B66000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000BEC6DC000732DE000335FB000732 + DE000732DE000335FB006682F100000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000ECDACC00BD580100FB9B + 0F00E27E0300AB56290000000000000000000000000000000000000000000000 + 00000000000000000000F8F0F000AB562900BD580100CC670100CC670100CC67 + 0100CC670100CC670100B3664100000000000000000000000000B3664100E27E + 0300E27E0300E27E0300E27E0300E27E0300CC670100A23F0800EADBD4000000 + 000000000000000000000000000000000000000000000000000000000000AB56 + 2900CC670100CC670100AF4B0200ECDACC00000000000000000000000000CB9A + 8200F9F6F500FFFCFB00FEF9F300FFF8EE00FEF4E900FEF4E900FEF0E200FEF0 + E20098A9EF000335FB000335FB00CFC4D600FEE4CA00FEE4CA00FEE4CA00FED6 + C900986B66000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000098A9EF000732DE000335FB000732DE000335 + FB00295AF7000335FB000732DE006682F1000000000000000000000000000000 + 00000000000000000000000000000000000000000000E5CDBE00CC670100FB9B + 0F00E27E0300BD79580000000000000000000000000000000000000000000000 + 000000000000FAF3F200AB562900BD580100CC670100CC670100BD580100AF4B + 0200CC670100CC670100B3664100000000000000000000000000B3664100FB9B + 0F00E27E0300AF4B0200AF4B0200E27E0300E27E0300CC670100A23F0800EADB + D40000000000000000000000000000000000000000000000000000000000B366 + 4100CC670100CC670100AF4B0200E5CDBE00000000000000000000000000CB9A + 8200FDFAFA00FFFDF800FEFAF700FEF9F300FFF8EE00FEF4E900FEF4E900FEF0 + E200F5EADF004571FA000335FB00295AF700FEEBD700FEE4CA00FEE4CA00FED6 + C900A47874000000000000000000000000000000000000000000000000000000 + 0000000000000000000098A9EF000335FB000335FB000732DE000335FB00CED7 + FA00F0F6FD006682F1000335FB000335FB006682F10000000000000000000000 + 00000000000000000000000000000000000000000000E5CDBE00CC670100FB9B + 0F00FB9B0F00B366410000000000000000000000000000000000000000000000 + 0000FAF3F200AB562900BD580100CC670100CC670100BD580100AB562900AF4B + 0200CC670100CC670100B3664100000000000000000000000000B3664100FB9B + 0F00FB9B0F00AF4B0200DAB4A000AF4B0200E27E0300E27E0300CC670100A23F + 0800E5CDBE00000000000000000000000000000000000000000000000000AB56 + 2900CC670100CC670100AF4B0200F1E3E200000000000000000000000000CB9A + 8200FAFAFA00FFFFFF00FFFCFB00FEFAF700FEF9F300FFF8EE00FEF4E900FEF4 + E900FEF0E200D1CDE4000335FB000335FB00CFC4D600FEE4CA00FEE4CA00FED6 + C900A47874000000000000000000000000000000000000000000000000000000 + 0000000000006682F1000335FB000732DE000335FB000335FB00CED7FA000000 + 0000000000000000000098A9EF000335FB000335FB006682F100000000000000 + 00000000000000000000000000000000000000000000F8F3EC00AF4B0200FCB0 + 4C00FB9B0F00AF4B0200F1E3E20000000000000000000000000000000000E5CD + BE00A23F0800BD580100CC670100CC670100BD580100BD795800E5CDBE00AF4B + 0200CC670100CC670100B3664100000000000000000000000000B3664100FB9B + 0F00FB9B0F00AF4B020000000000DAB4A000BD580100E27E0300E27E0300E27E + 0300A23F0800CB9A820000000000000000000000000000000000DAB4A000AF4B + 0200CC670100CC670100A23F080000000000000000000000000000000000DCA8 + 8700F8FBFB00FFFFFF00FFFFFF00FFFDF800FEFAF700FEF9F300FFF8EE00FEF4 + E900FEF0E200FEF0E20098A9EF00E2D6E200FEEBD700FEEBD700FEE4CA00FECF + C200A47874000000000000000000000000000000000000000000000000000000 + 00006682F1000335FB000335FB000335FB000335FB00CED7FA00000000000000 + 0000000000000000000000000000CED7FA00295AF7000335FB006682F1000000 + 0000000000000000000000000000000000000000000000000000AB562900FB9B + 0F00FCB04C00E27E0300AB562900F1E3E20000000000F5EADF00BD795800AF4B + 0200E27E0300E27E0300CC670100BD580100BD79580000000000E5CDBE00AF4B + 0200CC670100CC670100B3664100000000000000000000000000B3664100FCB0 + 4C00FB9B0F00AF4B02000000000000000000DAB4A000AF4B0200E27E0300E27E + 0300E27E0300BD580100AB562900D6A89400E5CDBE00CB9A8200A23F0800CC67 + 0100CC670100BD580100BD79580000000000000000000000000000000000DCA8 + 8700FBFBFC00FCFEFE00FFFFFE00FFFFFF00FFFCFB00FEFAF700FEF9F300FFF8 + EE00FEF4E900FEF4E900FEF0E200FEF0E200FEEBD700FED6C900FECFC200F3B9 + B500A47874000000000000000000000000000000000000000000F0F6FD00295A + F7000335FB000335FB000335FB000335FB00CED7FA0000000000000000000000 + 000000000000000000000000000000000000F0F6FD004571FA000335FB004571 + FA00000000000000000000000000000000000000000000000000DAB4A000C56E + 2600FCB04C00FCB04C00E27E0300BD580100AF4B0200BD580100CC670100E27E + 0300E27E0300E27E0300AF4B0200BD7958000000000000000000E5CDBE00AF4B + 0200CC670100CC670100B3664100000000000000000000000000B3664100FCB0 + 4C00FCB04C00AF4B0200000000000000000000000000DAB4A000AF4B0200E27E + 0300E27E0300E27E0300E27E0300BD580100BD580100BD580100CC670100CC67 + 0100CC670100A23F0800F5EADF0000000000000000000000000000000000DCA8 + 8700FCFEFE00FFFEFF00FFFEFF00FFFFFF00FFFEFE00FFFCFB00FEF9F300FFF8 + EE00FFF8EE00FEF4E900FEF4E900FEF0E200FED6C900F3B9B500FCA3A200FCA3 + A200B481760000000000000000000000000000000000F0F6FD00295AF7000335 + FB000335FB000335FB000335FB00CED7FA000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000098A9EF000335 + FB00CED7FA00000000000000000000000000000000000000000000000000B366 + 4100D3936400FCB04C00FCB04C00FCB04C00FB9B0F00FB9B0F00FB9B0F00FB9B + 0F00E27E0300A23F0800D6A89400000000000000000000000000E5CDBE00AF4B + 0200CC670100CC670100B3664100000000000000000000000000B3664100FCB0 + 4C00FCC47C00AF4B020000000000000000000000000000000000ECDACC00AB56 + 2900CC670100E27E0300E27E0300E27E0300E27E0300E27E0300CC670100CC67 + 0100A23F0800DAB4A0000000000000000000000000000000000000000000E3B1 + 8E00FCFEFB00FFFFFE00FFFEFF00FEFEFE00FFFFFE00FEFEFE00FFFDF800FEFA + F700FFF8EE00FFF8EE00FEF4E900B4817600B4817600B4817600B4817600B481 + 7600B4817600000000000000000000000000000000006682F1000335FB000335 + FB000335FB000335FB00CED7FA00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000F8F3 + EC00BD795800C56E2600FCB04C00FCB04C00FCB04C00FB9B0F00E27E0300BD58 + 0100AB562900ECDACC000000000000000000000000000000000000000000AB56 + 2900AF4B0200AF4B0200E2BDB300000000000000000000000000E5CDBE00AB56 + 2900AF4B0200CB9A82000000000000000000000000000000000000000000FAF3 + F200CB9A8200AF4B0200CC670100CC670100E27E0300CC670100BD580100A23F + 0800DAB4A000000000000000000000000000000000000000000000000000E3B1 + 8E00FFFEFE00FFFFFE00FFFFFF00FFFEFF00FEFEFE00FFFEFF00FFFEFF00FDFA + FA00FEFAF700FFF8EE00FFF8EE00B4817600E3B18E00FCB04C00FB9B0F00D393 + 6400CB9A8200000000000000000000000000000000006682F1000335FB000335 + FB000335FB00CED7FA0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000DAB4A000BD795800AF4B0200AB562900A23F0800BD795800E2BD + B30000000000000000000000000000000000000000000000000000000000F8F0 + F000DAB4A000ECDACC0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000F8F3EC00DAB4A000D3936400B3664100BD795800DAB4A000F8F3 + EC0000000000000000000000000000000000000000000000000000000000EDBD + 9200FFFCFF00FFFFFF00FFFFFF00FFFEFF00FFFFFE00FFFFFF00FFFFFF00FFFF + FE00FEFAF700FEFAF700FFF8EE00B4817600EAC8A100FCC47C00CB9A8200DCA8 + 8700F6EFE70000000000000000000000000000000000CED7FA004571FA004571 + FA00CED7FA000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200FFFFFE00FFFEFF00FFFFFF00FEFEFE00FFFFFF00FEFEFE00FFFFFF00FFFF + FE00FEFEFE00FEFAF700FEFAF700B4817600EAC8A100E3B18E00DCA88700F6EF + E700000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200FEF0E200FEF0E200F5EADF00F5EADF00EFE4D800EFE4D800EADBD400EADB + D400ECDACC00E4D1CD00DED2CA00B4817600DCA88700D6A89400F6EFE7000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000000000000EDBD + 9200DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA88700DCA8 + 8700DCA88700DCA88700DCA88700B4817600DCA88700F6EFE700000000000000 + 000000000000000000000000000000000000424D3E000000000000003E000000 + 2800000060000000A80000000100010000000000E00700000000000000000000 + 000000000000000000000000FFFFFF00FF003FFFFFFFFFFFFF000000FC000FE0 + 0007E00007000000F00003E00007C00007000000E00000E00007C00007000000 + C00000E00007C00007000000800001E00007E00007000000800001E00007E000 + 07000000800001E00007E00007000000800001E00007E00007000000800001E0 + 0007E00007000000800001E00007E00007000000C00003E00007E00007000000 + E00003E00007E00007000000F80007E00007E00007000000F8001FE00007E000 + 07000000FC007FE00007E00007000000FC00FFE00007E00007000000F800FFE0 + 0007E00007000000F800FFE00007E00007000000F801FFE00007E00007000000 + F001FFE0000FE0000F000000F001FFE0001FE0001F000000E003FFE0003FE000 + 3F000000E003FFE0007FE0007F000000FFFFFFFFFFFFFFFFFFFFFFFFE7FFFFFF + FFFFC3FFFFC00003E07FFFF81FFFC03FFF800003E007FFF800FFC003FF800003 + 80007FF8000FC0007F80000380000F80000FC0007F800003800007800007C000 + 3F800003800007800007C0003F800003800007800003C0001F80000380000380 + 0003C0001F800003800003800001C0000F800003800001800001C0000F800003 + 800001800001C00007800003800000800001C0000780000380000080001FC000 + 0780000380000080001FC0000380000380001F80001FC0780180000380001F80 + 00FFE0FF0F800003E0001F8000FFFFFE0F800003E0007F8000FFFFFE0F800003 + FC00FF80FFFFFFF81F800003FC01FFC0FFFFFF803F800003FFFFFFFFFFFFFFC0 + FF800003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF807FC0 + FFFFF0FFFFC3FEFFFE001FC00FFFF007FFC03E7FFC000FC000FFF0007FC0001F + F80007C0000FF0001FC0000FF00003C00003F0001FC00007E00001C00003F000 + 0FC00003C00001C00003F0000FC00001C00000C00003F00007C00001800000C0 + 0003F00007C00003000000C00003F00007C00003000000C00003F00003C00003 + C00000C00003F00003C00001C00000C00003F00003C00001E00000C00003F000 + 3FC00001E00001C00003F0003FC00001E00001C00003F0003FC0001FF00001E0 + 0003FF8003C0001FF00003F00003FF8007C0001FF80007FC0003FFC00FE000FF + FC000FFE000FFFE03FF801FFFF003FFF001FFFE07FF803FFFFC0FFFF807FFFF1 + FFFFFFFFFFFFFFFFC0FFFFFFFFFFFFFFFFFFFFFFFFFF8FFFFFFBFFCFE00003FE + 03FF07FFFFF0FF0FE00003FC003F03FFFFF07E07E00003F0000301FFFFF03C0F + E00003E0000180FFFFF0180FE00003C00001C07FFFF8000FE00003800001E03F + FFF8001FE00003800001F0301FF8001FE00003800001F8000FFC001FE0000380 + 0001FC0007FC003FE00003800001FE0003F8000FE00003800001FF0001E00007 + E00003800000FF0001800001E00003800000FE0000000000E00003000001FE00 + 00000000E00003000003FE0000000000E00003800007FE00000380E0E00003C0 + 000FFE0000FF81FFE00003E0001FFF0001FF81FFE00003F0003FFF0003FF81FF + E00003FC003FFF8003FFC1FFE00007FC003FFFC007FFC3FFE0000FFE1C3FFFE0 + 1FFFC3FFE0001FFFFC3FFFFFFFFFE3FFFFFFFFFF81FFFF81FFFFFFFFFFFFFFFC + 007FFE003FE00003000001F8001FF8001FE00003000001E0000FF0000FE00003 + 000001E00007E00007E00003000001C00003C00003E00003000001800003C000 + 01E00003000001800001800001E00003000001800001800001E0000300000100 + 0001800000E00003000001000001800000E00003000001000001800000E00003 + 000001000001800000E00003000001000001800000E000030000010000018000 + 01E00003000001800001800001E00003000001800001800001E00003000001C0 + 0003C00003E00003000001C00007E00003E00003000001E00007E00007E00003 + 000001F0000FF0000FE00003FFFFFFF8003FFC001FE00007FFFFFFFE007FFE00 + 7FE0000FFFFFFFFFFFFFFFFFFFE0001FF87E1FFFFFFFFF0003FFFFFFF03C0FFF + 0001FF0003FFFFFFF0180FFF0001E000038FFFFFF0188FFF0001C00003807FFF + F1198FFF0001C000038007FFF1198FFF0001C0000380007FF0080FC00001C000 + 0380001FF8001FC00001C0000380001FFC001FC00001C0000380000FFF81FFC0 + 0001C0000380000FFF81FFC00001C00003800007FF81FFC00001C00003800007 + FF00FFC00001C00003800003FF00FFC00001C00003800003FE007FC00001C000 + 03800003FE187FC00001C00007800001FE187FC00001C0000F800001FC3C3FC0 + 0003C0000F80003FFC7E3FC00007C0000F80000FFC7E3FC000FFC0000F80001F + F8FF1FC000FFC0000FC07F9FF8FF9FC000FFC0000FE0FFFFF9FF9FC001FFE000 + 1FFFFFFFFFFFFFC003FFFE01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1FF + 3FFFFFFC7FE00007C7FFE1FE1FFFFFFC3FE0000783FFC1FC0FFFFFF81FE00007 + 81FF83F80FFFFFF80FE0000780FF07F01FFFFFFC07E00007C07E0FF03FFFFFFE + 07E00007F03C1FE07FFFFFFF03E00007F8103FE04001C00183E00007FC007FC0 + C001C00181E00007FE00FFC1C001C001C1E00007FF81FFC1E001C003C1E00007 + FF01FF83FC01C01FE0E00007FE00FF83F801C00FE0E00007FC007F83F001C007 + E0E00007F81C3F81E001C203C1E00007F03E1FC08041C30001E00007C07F0FC0 + 00C1C38001E0000780FFC7E001C1C3C003E0000781FFFFE003E1C3E007E00007 + 83FFFFF80FE3FFF80FE0000787FFFFFFFFFFFFFFFFE0000FFFFFFFFFFFFFFFFF + FFE0001FFFFFFFFFFFFFFFFFFFE0003F00000000000000000000000000000000 + 000000000000} + end +end diff --git a/Demos/Interfaces/myevents.pas b/Demos/Interfaces/myevents.pas index 2efdd91b4..595e2ee77 100644 --- a/Demos/Interfaces/myevents.pas +++ b/Demos/Interfaces/myevents.pas @@ -103,9 +103,11 @@ procedure TEventPresenter.setup(aVST: TVirtualStringTree; anImageList: TImageLis //set up columns col := fVST.header.Columns.Add; col.Text := 'Star Event'; + col.Hint := 'Number of Stars'; col.Width := 120; col := fVST.header.Columns.Add; col.Text := 'Date'; + col.Text := 'The date of the event'; col.Width := 100; col := fVST.header.Columns.Add; col.Text := 'Charity Event Name'; From 654b9d7657a8e8e10df404d978d1c9ad870d720c Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 31 Mar 2020 09:49:01 +0100 Subject: [PATCH 055/681] Fixed copy and paste error. --- Demos/Interfaces/myevents.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Demos/Interfaces/myevents.pas b/Demos/Interfaces/myevents.pas index 595e2ee77..7ad78b489 100644 --- a/Demos/Interfaces/myevents.pas +++ b/Demos/Interfaces/myevents.pas @@ -107,7 +107,7 @@ procedure TEventPresenter.setup(aVST: TVirtualStringTree; anImageList: TImageLis col.Width := 120; col := fVST.header.Columns.Add; col.Text := 'Date'; - col.Text := 'The date of the event'; + col.Hint := 'The date of the event'; col.Width := 100; col := fVST.header.Columns.Add; col.Text := 'Charity Event Name'; From b926b6006a205c91d20e4ab46afd3ac10ffa2422 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 31 Mar 2020 09:53:42 +0100 Subject: [PATCH 056/681] Added Hint text to left button. --- Demos/Interfaces/modelviewform.dfm | 1 + 1 file changed, 1 insertion(+) diff --git a/Demos/Interfaces/modelviewform.dfm b/Demos/Interfaces/modelviewform.dfm index ebf924390..94d489570 100644 --- a/Demos/Interfaces/modelviewform.dfm +++ b/Demos/Interfaces/modelviewform.dfm @@ -38,6 +38,7 @@ object FormModelView: TFormModelView Top = 480 Width = 201 Height = 25 + Hint = 'Removes all items that do not have a star' Caption = 'Display Star Events Only' TabOrder = 0 OnClick = btDisplayStarsClick From 0a419f138ef5e615bb919e6e9a5d3db937c785cb Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 7 Apr 2020 20:37:04 +0100 Subject: [PATCH 057/681] Fix for issue #959: Deleting a column using BeginUpdate and EndUpdate will result in an error --- Source/VirtualTrees.pas | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 4f4d8f1df..515a46107 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -8290,12 +8290,21 @@ procedure TVirtualTreeColumns.InitializePositionArray; //---------------------------------------------------------------------------------------------------------------------- procedure TVirtualTreeColumns.Notify(Item: TCollectionItem; Action: System.Classes.TCollectionNotification); - +var + I: Integer; begin if Action in [cnExtracting, cnDeleting] then + begin + // Adjust all positions larger than the deleted column's position. Fixes #959 + for I := 0 to Count - 1 do begin + if Items[I].Position > TVirtualTreeColumn(Item).Position then + Items[I].Position := Items[I].Position - 1; + end;//for I + with Header.Treeview do if not (csLoading in ComponentState) and (FFocusedColumn = Item.Index) then FFocusedColumn := NoColumn; + end;// if cnDeleting end; //---------------------------------------------------------------------------------------------------------------------- From 1aa415dc91dd610945d2800b28761dac258d72ce Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Thu, 30 Apr 2020 14:56:26 +1000 Subject: [PATCH 058/681] Implemented #964 EmptyListMessage should show when no visible nodes. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 515a46107..78ee01d4a 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -30884,7 +30884,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe NodeBitmap.Free; end;//try..finally - if (ChildCount[nil] = 0) and (FEmptyListMessage <> '') then + if (FEmptyListMessage <> '') and ((ChildCount[nil] = 0) or (GetFirstVisible = nil)) then begin // output a message if no items are to display Canvas.Font := Self.Font; From 08d9213d5dd34fac069e43fd036d246abd7ff9ee Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 4 May 2020 22:02:29 +0200 Subject: [PATCH 059/681] Fixed issue #966: Tabs in EmptyListMessage property are now drawn correctly. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 78ee01d4a..0b96a8494 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -30894,7 +30894,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe R.Right := R.Left + Width - 2; R.Bottom := Height -2; TargetCanvas.Font.Color := clGrayText; - TargetCanvas.TextRect(R, FEmptyListMessage, [tfNoClip, tfLeft, tfWordBreak]); + TargetCanvas.TextRect(R, FEmptyListMessage, [tfNoClip, tfLeft, tfWordBreak, tfExpandTabs]); end; DoAfterPaint(TargetCanvas); From f614d528441708423c22f77a579e067049d395c9 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 4 May 2020 22:06:24 +0200 Subject: [PATCH 060/681] Issue #967: Added packages for RAD Studio 10.4 derived from 10.3 packages. --- .../RAD Studio 10.4/VirtualTreeView.groupproj | 48 ++++++ Packages/RAD Studio 10.4/VirtualTreesD.dpk | 42 +++++ Packages/RAD Studio 10.4/VirtualTreesD.dproj | 132 +++++++++++++++ Packages/RAD Studio 10.4/VirtualTreesD.res | Bin 0 -> 96 bytes Packages/RAD Studio 10.4/VirtualTreesR.dpk | 51 ++++++ Packages/RAD Studio 10.4/VirtualTreesR.dproj | 151 ++++++++++++++++++ Packages/RAD Studio 10.4/VirtualTreesR.res | Bin 0 -> 96 bytes 7 files changed, 424 insertions(+) create mode 100644 Packages/RAD Studio 10.4/VirtualTreeView.groupproj create mode 100644 Packages/RAD Studio 10.4/VirtualTreesD.dpk create mode 100644 Packages/RAD Studio 10.4/VirtualTreesD.dproj create mode 100644 Packages/RAD Studio 10.4/VirtualTreesD.res create mode 100644 Packages/RAD Studio 10.4/VirtualTreesR.dpk create mode 100644 Packages/RAD Studio 10.4/VirtualTreesR.dproj create mode 100644 Packages/RAD Studio 10.4/VirtualTreesR.res diff --git a/Packages/RAD Studio 10.4/VirtualTreeView.groupproj b/Packages/RAD Studio 10.4/VirtualTreeView.groupproj new file mode 100644 index 000000000..b0f33e510 --- /dev/null +++ b/Packages/RAD Studio 10.4/VirtualTreeView.groupproj @@ -0,0 +1,48 @@ + + + {CC6A9541-DD5C-4BCD-8914-016D8D2EAB3B} + + + + + + + VirtualTreesR.dproj + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Packages/RAD Studio 10.4/VirtualTreesD.dpk b/Packages/RAD Studio 10.4/VirtualTreesD.dpk new file mode 100644 index 000000000..eb9f5e095 --- /dev/null +++ b/Packages/RAD Studio 10.4/VirtualTreesD.dpk @@ -0,0 +1,42 @@ +package VirtualTreesD; + +{$R *.res} +{$R '..\..\Design\VirtualTrees.dcr'} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO ON} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'VirtualTreeView Controls'} +{$LIBSUFFIX '26'} +{$DESIGNONLY} +{$IMPLICITBUILD OFF} + +requires + DesignIDE, + VirtualTreesR; + +contains + VirtualTreesReg in '..\..\Design\VirtualTreesReg.pas'; + +end. + diff --git a/Packages/RAD Studio 10.4/VirtualTreesD.dproj b/Packages/RAD Studio 10.4/VirtualTreesD.dproj new file mode 100644 index 000000000..97d945a1e --- /dev/null +++ b/Packages/RAD Studio 10.4/VirtualTreesD.dproj @@ -0,0 +1,132 @@ + + + {A34BA07B-19B6-4C21-9DEE-65FCA52D00AB} + VirtualTreesD.dpk + True + Release + Package + VCL + DCC32 + 19.0 + Win32 + 1 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + VirtualTreesD + ..\..\Source + .\$(Platform)\$(Config) + true + VirtualTreeView Controls + All + 26 + true + ..\..\source;.\$(Platform)\$(Config);$(DCC_UnitSearchPath) + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + 1053 + false + true + 00400000 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + false + true + false + false + false + + + $(BDS)\BIN\Bds.exe + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + vcl;VirtualTreesR;$(DCC_UsePackage) + + + RELEASE;$(DCC_Define) + + + DEBUG;$(DCC_Define) + false + true + + + + MainSource + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + VirtualTreesD.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1053 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + True + + + 12 + + + + diff --git a/Packages/RAD Studio 10.4/VirtualTreesD.res b/Packages/RAD Studio 10.4/VirtualTreesD.res new file mode 100644 index 0000000000000000000000000000000000000000..743599575b02e97248bade49ed2e3eabafe25a0a GIT binary patch literal 96 zcmZQzU|>)H;{X347|28cOhBFu5dZ(r#Sp;Y!{Epe!r;c>&k)4m3uHM0X?F%!AS)QE O%YcEC1!e#vkO2UW7YiT& literal 0 HcmV?d00001 diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dpk b/Packages/RAD Studio 10.4/VirtualTreesR.dpk new file mode 100644 index 000000000..d27dfdafa --- /dev/null +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dpk @@ -0,0 +1,51 @@ +package VirtualTreesR; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS ON} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$LIBSUFFIX '26'} +{$RUNONLY} +{$IMPLICITBUILD OFF} + +requires + vcl, + vclx; + +contains + VirtualTrees in '..\..\Source\VirtualTrees.pas', + VirtualTrees.HeaderPopup in '..\..\Source\VirtualTrees.HeaderPopup.pas', + VirtualTrees.AccessibilityFactory in '..\..\Source\VirtualTrees.AccessibilityFactory.pas', + VirtualTrees.Accessibility in '..\..\Source\VirtualTrees.Accessibility.pas', + VirtualTrees.StyleHooks in '..\..\Source\VirtualTrees.StyleHooks.pas', + VirtualTrees.Classes in '..\..\Source\VirtualTrees.Classes.pas', + VirtualTrees.WorkerThread in '..\..\Source\VirtualTrees.WorkerThread.pas', + VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', + VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', + VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas'; + +end. + + diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dproj b/Packages/RAD Studio 10.4/VirtualTreesR.dproj new file mode 100644 index 000000000..b646e57bb --- /dev/null +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dproj @@ -0,0 +1,151 @@ + + + {B62F3689-96E1-47D5-9FB2-2A2718281FDB} + VirtualTreesR.dpk + True + Release + Package + VCL + DCC32 + 19.0 + Win32 + 3 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + All + VirtualTreesR + .\$(Platform)\$(Config) + ..\..\Source + true + 26 + true + ..\..\source;.\$(Platform)\$(Config);$(DCC_UnitSearchPath) + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + 1053 + false + true + 00400000 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + false + true + false + false + false + + + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + DEBUG;$(DCC_Define) + false + true + + + true + + + + MainSource + + + + + + + + + + + + + + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + Delphi.Personality.12 + Package + + + + VirtualTreesR.dpk + + + True + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1053 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + True + True + + + 12 + + + + diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.res b/Packages/RAD Studio 10.4/VirtualTreesR.res new file mode 100644 index 0000000000000000000000000000000000000000..93e7e944f20ed0f0e50532c1b7ef56bb8d255d33 GIT binary patch literal 96 zcmZQzU|>)H;{X347|28cOhBFu5dZ(r#Sp;Y!{Epe!r;c>&k)4m3uHM0X?F%!AS)QE O%YcEC1!e# Date: Tue, 5 May 2020 21:01:32 +0200 Subject: [PATCH 061/681] Fixed issue #968: Switching between edited nodes with TAB key is broken --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 0b96a8494..964731b55 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -32984,8 +32984,8 @@ procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown); // check NextNode, otherwise we got AV if NextNode <> nil then begin - // Continue editing next node - ClearSelection; + // Continue editing next node + Tree.ClearSelection(); Tree.Selected[NextNode] := True; if Tree.CanEdit(Tree.FocusedNode, Tree.FocusedColumn) then Tree.DoEdit; From 7b1e166d605ee46dfcaf626716d0149b2ffb0775 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 9 May 2020 11:29:03 +0200 Subject: [PATCH 062/681] TVclStyleScrollBarsHook: UpdateScroll() is protected in base class in RAD Studio 10.4 --- Source/VirtualTrees.StyleHooks.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index 97cf20d5d..41b727e82 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -93,7 +93,7 @@ TScrollWindow = class(TWinControl) procedure MouseLeave; override; procedure PaintScroll; override; function PointInTreeHeader(const P: TPoint): Boolean; - procedure UpdateScroll; + procedure UpdateScroll;{$if CompilerVersion >= 34}override;{$ifend} public constructor Create(AControl: TWinControl); override; destructor Destroy; override; From a609bf90fb279424cf3f5d4fa6b266e7f98e307b Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 10 May 2020 10:48:02 +0200 Subject: [PATCH 063/681] LibSuffix '27' as LibSuffix Auto does not yet work correctly. --- Packages/RAD Studio 10.4/VirtualTreesD.dpk | 2 +- Packages/RAD Studio 10.4/VirtualTreesD.dproj | 2 +- Packages/RAD Studio 10.4/VirtualTreesR.dpk | 2 +- Packages/RAD Studio 10.4/VirtualTreesR.dproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Packages/RAD Studio 10.4/VirtualTreesD.dpk b/Packages/RAD Studio 10.4/VirtualTreesD.dpk index eb9f5e095..92f96d9a1 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesD.dpk +++ b/Packages/RAD Studio 10.4/VirtualTreesD.dpk @@ -27,7 +27,7 @@ package VirtualTreesD; {$DEFINE RELEASE} {$ENDIF IMPLICITBUILDING} {$DESCRIPTION 'VirtualTreeView Controls'} -{$LIBSUFFIX '26'} +{$LIBSUFFIX '27'} {$DESIGNONLY} {$IMPLICITBUILD OFF} diff --git a/Packages/RAD Studio 10.4/VirtualTreesD.dproj b/Packages/RAD Studio 10.4/VirtualTreesD.dproj index 97d945a1e..a571731f8 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesD.dproj +++ b/Packages/RAD Studio 10.4/VirtualTreesD.dproj @@ -36,7 +36,7 @@ true VirtualTreeView Controls All - 26 + 27 true ..\..\source;.\$(Platform)\$(Config);$(DCC_UnitSearchPath) System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dpk b/Packages/RAD Studio 10.4/VirtualTreesR.dpk index d27dfdafa..1bf4fd9af 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dpk @@ -25,7 +25,7 @@ package VirtualTreesR; {$IMAGEBASE $400000} {$DEFINE RELEASE} {$ENDIF IMPLICITBUILDING} -{$LIBSUFFIX '26'} +{$LIBSUFFIX '27'} {$RUNONLY} {$IMPLICITBUILD OFF} diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dproj b/Packages/RAD Studio 10.4/VirtualTreesR.dproj index b646e57bb..fd94a4367 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dproj @@ -41,7 +41,7 @@ .\$(Platform)\$(Config) ..\..\Source true - 26 + 27 true ..\..\source;.\$(Platform)\$(Config);$(DCC_UnitSearchPath) System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) From 78c5913d7e4e4455c123bc64e58a6028bf67d1d3 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 10 May 2020 11:08:22 +0200 Subject: [PATCH 064/681] Preparation for issue #969: Allow to customize StaticText alignment --- Source/VirtualTrees.pas | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 964731b55..f94dd43d2 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -3372,6 +3372,7 @@ TVSTGetCellTextEventArgs = record Column: TColumnIndex; CellText: string; StaticText: string; + StaticTextAlignment: TAlignment; ExportType: TVTExportType; constructor Create(pNode: PVirtualNode; pColumn: TColumnIndex; pExportType: TVTExportType = TVTExportType.etNone); end; @@ -3415,7 +3416,7 @@ TCustomVirtualStringTree = class(TBaseVirtualTree) FPreviouslySelected: TStringList; procedure InitializeTextProperties(var PaintInfo: TVTPaintInfo); // [IPK] - private to protected procedure PaintNormalText(var PaintInfo: TVTPaintInfo; TextOutFlags: Integer; Text: string); virtual; // [IPK] - private to protected - procedure PaintStaticText(const PaintInfo: TVTPaintInfo; TextOutFlags: Integer; const Text: string); virtual; // [IPK] - private to protected + procedure PaintStaticText(const PaintInfo: TVTPaintInfo; pStaticTextAlignment: TAlignment; const Text: string); virtual; // [IPK] - private to protected procedure AdjustPaintCellRect(var PaintInfo: TVTPaintInfo; var NextNonEmpty: TColumnIndex); override; function CanExportNode(Node: PVirtualNode): Boolean; function CalculateStaticTextWidth(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; const Text: string): Integer; virtual; @@ -33613,8 +33614,7 @@ procedure TCustomVirtualStringTree.PaintNormalText(var PaintInfo: TVTPaintInfo; //---------------------------------------------------------------------------------------------------------------------- -procedure TCustomVirtualStringTree.PaintStaticText(const PaintInfo: TVTPaintInfo; TextOutFlags: Integer; - const Text: string); +procedure TCustomVirtualStringTree.PaintStaticText(const PaintInfo: TVTPaintInfo; pStaticTextAlignment: TAlignment; const Text: string); // This method retrives and draws the static text bound to a particular node. @@ -33654,7 +33654,7 @@ procedure TCustomVirtualStringTree.PaintStaticText(const PaintInfo: TVTPaintInfo Canvas.Font.Color := FColors.DisabledColor; R := ContentRect; - if Alignment = taRightJustify then begin + if pStaticTextAlignment = taRightJustify then begin Dec(R.Right, NodeWidth + FTextMargin); DrawFormat := DrawFormat or DT_RIGHT end @@ -34055,6 +34055,7 @@ procedure TCustomVirtualStringTree.DoPaintNode(var PaintInfo: TVTPaintInfo); lEventArgs := TVSTGetCellTextEventArgs.Create(PaintInfo.Node, PaintInfo.Column); lEventArgs.CellText := FDefaultText; + lEventArgs.StaticTextAlignment := PaintInfo.Alignment; DoGetText(lEventArgs); // Paint the normal text first... @@ -34063,7 +34064,7 @@ procedure TCustomVirtualStringTree.DoPaintNode(var PaintInfo: TVTPaintInfo); // ... and afterwards the static text if not centered and the node is not multiline enabled. if (Alignment <> taCenter) and not (vsMultiline in PaintInfo.Node.States) and (toShowStaticText in TreeOptions.FStringOptions) and not lEventArgs.StaticText.IsEmpty then - PaintStaticText(PaintInfo, TextOutFlags, lEventArgs.StaticText); + PaintStaticText(PaintInfo, lEventArgs.StaticTextAlignment, lEventArgs.StaticText); finally RestoreFontChangeEvent(PaintInfo.Canvas); end; From 2c431706ec7efa764019b2bb8b49b545cbfde7b6 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 10 May 2020 11:17:58 +0200 Subject: [PATCH 065/681] We now use the OnGetCellText event instead of OnGetText as demonstration for issue #969 --- Demos/Advanced/GeneralAbilitiesDemo.dfm | 2 +- Demos/Advanced/GeneralAbilitiesDemo.pas | 28 +++++++++++-------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Demos/Advanced/GeneralAbilitiesDemo.dfm b/Demos/Advanced/GeneralAbilitiesDemo.dfm index 9cb5d04f3..ba8211dca 100644 --- a/Demos/Advanced/GeneralAbilitiesDemo.dfm +++ b/Demos/Advanced/GeneralAbilitiesDemo.dfm @@ -66,7 +66,7 @@ object GeneralForm: TGeneralForm OnDragOver = VST2DragOver OnFocusChanging = VST2FocusChanging OnFreeNode = VST2FreeNode - OnGetText = VST2GetText + OnGetCellText = VST2GetCellText OnPaintText = VST2PaintText OnGetImageIndexEx = VST2GetImageIndexEx OnGetNodeDataSize = VST2GetNodeDataSize diff --git a/Demos/Advanced/GeneralAbilitiesDemo.pas b/Demos/Advanced/GeneralAbilitiesDemo.pas index aa07b400d..cec1c0a05 100644 --- a/Demos/Advanced/GeneralAbilitiesDemo.pas +++ b/Demos/Advanced/GeneralAbilitiesDemo.pas @@ -57,8 +57,8 @@ TGeneralForm = class(TForm) var InitialStates: TVirtualNodeInitStates); procedure VST2InitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode; var ChildCount: Cardinal); procedure VST2NewText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; NewText: string); - procedure VST2GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; - var CellText: string); + procedure VST2GetCellText(Sender: TCustomVirtualStringTree; + var E: TVSTGetCellTextEventArgs); procedure VST2PaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); procedure VST2GetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer); @@ -183,8 +183,7 @@ procedure TGeneralForm.VST2PaintText(Sender: TBaseVirtualTree; const TargetCanva //---------------------------------------------------------------------------------------------------------------------- -procedure TGeneralForm.VST2GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; - TextType: TVSTTextType; var CellText: string); +procedure TGeneralForm.VST2GetCellText(Sender: TCustomVirtualStringTree; var E: TVSTGetCellTextEventArgs); // Returns the text as it is stored in the nodes data record. @@ -192,21 +191,18 @@ procedure TGeneralForm.VST2GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Data: PNodeData2; begin - Data := Sender.GetNodeData(Node); - CellText := ''; - case Column of + Data := Sender.GetNodeData(E.Node); + case E.Column of 0: // main column (has two different captions) - case TextType of - ttNormal: - CellText := Data.Caption; - ttStatic: - CellText := Data.StaticText; + begin + E.CellText := Data.Caption; + E.StaticText := Data.StaticText; + //E.StaticTextAlignment := TAlignment.taRightJustify; end; - 1: // no text in the image column - ; 2: - if TextType = ttNormal then - CellText := Data.ForeignText; + E.CellText := Data.ForeignText; + else + E.CellText := ''; end; end; From 2a6c613aec64fb0ce7a8c305e57f3b7e66f4531e Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 10 May 2020 13:29:52 +0200 Subject: [PATCH 066/681] Fixed issue #969: PaintStaticText() now respects new parameter pStaticTextAlignment. --- Source/VirtualTrees.pas | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index f94dd43d2..75741c70f 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -33655,11 +33655,16 @@ procedure TCustomVirtualStringTree.PaintStaticText(const PaintInfo: TVTPaintInfo R := ContentRect; if pStaticTextAlignment = taRightJustify then begin - Dec(R.Right, NodeWidth + FTextMargin); - DrawFormat := DrawFormat or DT_RIGHT + DrawFormat := DrawFormat or DT_RIGHT; + Dec(R.Right, FTextMargin); + if PaintInfo.Alignment = taRightJustify then + Dec(R.Right, NodeWidth); // room for node text end - else - Inc(R.Left, NodeWidth + FTextMargin); + else begin + Inc(R.Left, FTextMargin); + if PaintInfo.Alignment = taRightJustify then + Inc(R.Left, NodeWidth); // room for node text + end; if Canvas.TextFlags and ETO_OPAQUE = 0 then SetBkMode(Canvas.Handle, TRANSPARENT) From 56e4ceabf405b4b26bdd073dfa305a8a612b27c2 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 10 May 2020 13:31:30 +0200 Subject: [PATCH 067/681] This for now demonstares issue #969: Allow to customize StaticText alignment --- Demos/Advanced/GeneralAbilitiesDemo.pas | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Demos/Advanced/GeneralAbilitiesDemo.pas b/Demos/Advanced/GeneralAbilitiesDemo.pas index cec1c0a05..e0cc0536d 100644 --- a/Demos/Advanced/GeneralAbilitiesDemo.pas +++ b/Demos/Advanced/GeneralAbilitiesDemo.pas @@ -197,9 +197,10 @@ procedure TGeneralForm.VST2GetCellText(Sender: TCustomVirtualStringTree; var E: begin E.CellText := Data.Caption; E.StaticText := Data.StaticText; - //E.StaticTextAlignment := TAlignment.taRightJustify; + if Sender.GetNodeLevel(E.Node) > 0 then + E.StaticTextAlignment := TAlignment.taRightJustify; end; - 2: + 1,2: E.CellText := Data.ForeignText; else E.CellText := ''; @@ -232,7 +233,7 @@ procedure TGeneralForm.VST2InitNode(Sender: TBaseVirtualTree; ParentNode, Node: end; Caption := Format('Level %d, Index %d', [Level, Node.Index]); - if Level in [0, 3] then + if Level in [0, 2, 3] then StaticText := '(static text)'; ForeignText := ''; From 1615e84f9bfd1130200f24460c99afdda6a5334f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 15 May 2020 22:10:06 +0200 Subject: [PATCH 068/681] Fixed isuue #970: Tristate checkbox init without toAutoTristateTracking --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 75741c70f..ffd67f428 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -22855,7 +22855,7 @@ procedure TBaseVirtualTree.InitNode(Node: PVirtualNode); // Fix: Any parent check state must be propagated here. // Because the CheckType is normally set in DoInitNode // by the App. - if Node.CheckType in [ctTriStateCheckBox] then + if (Node.CheckType = ctTriStateCheckBox) and (toAutoTristateTracking in FOptions.FAutoOptions) then begin ParentCheckState := Self.GetCheckState(Node.Parent); SelfCheckState := Self.GetCheckState(Node); From 92335a6e9046db4749085f41a5f3a4e5393c2049 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 27 May 2020 22:12:14 +0200 Subject: [PATCH 069/681] VTVersion = '7.4.0' --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ffd67f428..1fc011308 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -86,7 +86,7 @@ interface {$ENDIF} const - VTVersion = '7.3.0'; + VTVersion = '7.4.0'; const VTTreeStreamVersion = 3; From fb15258a321186116169c9191051308d32b9efad Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 27 May 2020 22:16:04 +0200 Subject: [PATCH 070/681] cosnt VTVersion is now deprecated. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 1fc011308..ca6336000 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -86,7 +86,7 @@ interface {$ENDIF} const - VTVersion = '7.4.0'; + VTVersion = '7.4.0' deprecated 'This const is going to be removed in a future version'; const VTTreeStreamVersion = 3; From 30df5a885d131a73271012bf164ecb007f9c3868 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 29 May 2020 15:59:21 +0100 Subject: [PATCH 071/681] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f122cb4d..d8fb6f2ce 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Virtual Treeview is a Delphi treeview control built from ground up. Many years o I don't use C++ Builder and my experience with it is very limited. This makes it difficult to take care about bugs that are reported in C++ Builder and to maintain the C++ Builder packages. I would be great if someone would volunteer to do this. Please contact me at joachim.marder+CPP@gmail.com. ### Downloads -**V7.3** official release for **RAD Studio XE3 to 10.3 Rio**: [JAM Software](https://www.jam-software.com/virtual-treeview/VirtualTreeView.zip) ([Changes](https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/12?closed=1)) +**V7.4** official release for **RAD Studio XE3 to 10.4 Rio**: [JAM Software](https://www.jam-software.com/virtual-treeview/VirtualTreeView.zip) ([Changes](https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/13?closed=1)) An experimental **FireMonkey** port can be found here: [livius2/Virtual-TreeView](https://github.com/livius2/Virtual-TreeView) From 36f644c943a3de93c0276a09a3f10838d62a6305 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 29 May 2020 15:59:48 +0100 Subject: [PATCH 072/681] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8fb6f2ce..4d301f9b6 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Virtual Treeview is a Delphi treeview control built from ground up. Many years o I don't use C++ Builder and my experience with it is very limited. This makes it difficult to take care about bugs that are reported in C++ Builder and to maintain the C++ Builder packages. I would be great if someone would volunteer to do this. Please contact me at joachim.marder+CPP@gmail.com. ### Downloads -**V7.4** official release for **RAD Studio XE3 to 10.4 Rio**: [JAM Software](https://www.jam-software.com/virtual-treeview/VirtualTreeView.zip) ([Changes](https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/13?closed=1)) +**V7.4** official release for **RAD Studio XE3 to 10.4 Rio**: [JAM Software](https://www.jam-software.com/virtual-treeview/VirtualTreeView.zip) ([Changes](https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/16?closed=1)) An experimental **FireMonkey** port can be found here: [livius2/Virtual-TreeView](https://github.com/livius2/Virtual-TreeView) From afb9db0fc7791b17a7e3089b71f3641a7ebac039 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 3 Jun 2020 18:34:01 +0200 Subject: [PATCH 073/681] Fixed wrong hint font size on secondary monitor. --- Source/VirtualTrees.pas | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ca6336000..f0f8c2d87 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -5472,6 +5472,7 @@ procedure TVirtualTreeHintWindow.Paint(); if (Node = nil) or (Tree.FHintMode <> hmToolTip) then begin Canvas.Font := Screen.HintFont; + Canvas.Font.Height := Tree.ScaledPixels(Canvas.Font.Height); Y := 2; end else @@ -5602,7 +5603,10 @@ function TVirtualTreeHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: str ChangeBidiModeAlignment(Alignment); if (Node = nil) or (Tree.FHintMode <> hmToolTip) then - Canvas.Font := Screen.HintFont + begin + Canvas.Font := Screen.HintFont; + Canvas.Font.Height := Tree.ScaledPixels(Canvas.Font.Height); + end else begin Canvas.Font := Tree.Font; From 4c3910e936240d607819129b9fcba6df96433126 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 3 Jun 2020 22:04:17 +0200 Subject: [PATCH 074/681] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d301f9b6..40da2263e 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ For installation instruction see the "INSTALL.TXT" file in the ZIP. [Delphinus]( Please do not contact developers or JAM Software for technical support. Please try to get support from the community e.g. at [Stack Overflow](http://stackoverflow.com/search?q=%22virtual+treeview%22), [Delphi Pages](http://www.delphipages.com/), [Delphi Praxis](http://www.delphipraxis.net/141465-virtual-treeview-tutorials-mit-beispielen.html) or [Embarcadero forums](https://forums.embarcadero.com/). Please do not use the issue tracker for getting support, only for reporting true bugs (see below). ### Reporting Bugs -First of all, please make sure you are using the **latest official version**. When **[reporting a bug](https://github.com/Virtual-TreeView/Virtual-TreeView/issues)** please include a **sample** project that allows us to quickly reproduce the bug. This can also be one of the demo projects that come with Virtual Treeview, modified to show the bug. If only small changes are required, a description is sufficient how a demo projects needs to be changed in order to replicate the bug. If you already have a solution, please supply a patch file or make a pull request. If you used a previous version that did not have the bug, please include this version number in your report. +First of all, please make sure you are using the **latest official version**. When **[reporting a bug](https://github.com/Virtual-TreeView/Virtual-TreeView/issues)** please attach a **sample** project as ZIP-file that allows us to quickly reproduce the bug. This can also be one of the demo projects that come with Virtual Treeview, modified to show the bug. If only small changes are required, a description is sufficient how a demo projects needs to be changed in order to replicate the bug. If you already have a solution, please supply a patch file or make a pull request. If you used a previous version that did not have the bug, please include this version number in your report. ### Feature Requests We currently focus on reducing the number of reported bugs and getting Virtual Treeview stable. Feature Requests will most likely not processed at the moment. We are only going to process enhancement requests if the new feature is of general interest and a source code patch based on the latest SVN revision is attached to the report. Please mark feature requests with the flag "Enhancement". From d6174f1d796485597f6c4543fb329ac88c0bbe6d Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 4 Jun 2020 23:03:51 +0200 Subject: [PATCH 075/681] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 40da2263e..eec3bb083 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,9 @@ For installation instruction see the "INSTALL.TXT" file in the ZIP. [Delphinus]( Please do not contact developers or JAM Software for technical support. Please try to get support from the community e.g. at [Stack Overflow](http://stackoverflow.com/search?q=%22virtual+treeview%22), [Delphi Pages](http://www.delphipages.com/), [Delphi Praxis](http://www.delphipraxis.net/141465-virtual-treeview-tutorials-mit-beispielen.html) or [Embarcadero forums](https://forums.embarcadero.com/). Please do not use the issue tracker for getting support, only for reporting true bugs (see below). ### Reporting Bugs -First of all, please make sure you are using the **latest official version**. When **[reporting a bug](https://github.com/Virtual-TreeView/Virtual-TreeView/issues)** please attach a **sample** project as ZIP-file that allows us to quickly reproduce the bug. This can also be one of the demo projects that come with Virtual Treeview, modified to show the bug. If only small changes are required, a description is sufficient how a demo projects needs to be changed in order to replicate the bug. If you already have a solution, please supply a patch file or make a pull request. If you used a previous version that did not have the bug, please include this version number in your report. +First of all, please make sure you are using the **latest official version**. When **[reporting a bug](https://github.com/Virtual-TreeView/Virtual-TreeView/issues)** please attach a **sample** project as ZIP-file that allows us to quickly reproduce the bug. This can also be one of the demo projects that come with Virtual Treeview, modified to show the bug. If only small changes are required, a description is sufficient how a demo projects needs to be changed in order to replicate the bug. Please follow [best practices for good bug reports](https://www.softwaretestinghelp.com/how-to-write-good-bug-report/). + +If you already have a solution, please supply a patch file or make a pull request. If you used a previous version that did not have the bug, please include this version number in your report. ### Feature Requests We currently focus on reducing the number of reported bugs and getting Virtual Treeview stable. Feature Requests will most likely not processed at the moment. We are only going to process enhancement requests if the new feature is of general interest and a source code patch based on the latest SVN revision is attached to the report. Please mark feature requests with the flag "Enhancement". From 3a8fd0784ba51755560e660e10ba33e95111e1f2 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 9 Jun 2020 20:56:11 +0200 Subject: [PATCH 076/681] Fixed issue #975: RAD Studio 10.4 stores default VirtualTree's default properties in .dfm file --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index f0f8c2d87..78b9e0a3d 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -11842,7 +11842,7 @@ function TVTColors.GetColor(const Index: TVTColorEnum): TColor; begin // Only try to fetch the color via StyleServices if theses are enabled // Return default/user defined color otherwise - if FOwner.VclStyleEnabled and not StyleServices.IsSystemStyle then + if FOwner.VclStyleEnabled then begin // If the ElementDetails are not defined, fall back to the SystemColor case Index of @@ -25597,7 +25597,7 @@ procedure TBaseVirtualTree.VclStyleChanged; // Updates the member FVclStyleEnabled, should be called initially and when the VCL style changes begin - FVclStyleEnabled := StyleServices.Enabled and not StyleServices.IsSystemStyle; + FVclStyleEnabled := StyleServices.Enabled and not StyleServices.IsSystemStyle and not (csDesigning in ComponentState); end; //---------------------------------------------------------------------------------------------------------------------- From 8af175725c65fe107069de18d4075175b3524294 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 17 Jun 2020 22:12:43 +0200 Subject: [PATCH 077/681] Fixed issue #977: Access Violation when trying to edit a node in a collapsed node via Shortcut --- Source/VirtualTrees.pas | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 78b9e0a3d..b1fe5a840 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -19827,13 +19827,12 @@ procedure TBaseVirtualTree.DoEdit; if Assigned(FFocusedNode) and not (vsDisabled in FFocusedNode.States) and not (toReadOnly in FOptions.FMiscOptions) and (FEditLink = nil) then begin + ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, not (toDisableAutoscrollOnEdit in FOptions.AutoOptions)); FEditLink := DoCreateEditor(FFocusedNode, FEditColumn); if Assigned(FEditLink) then begin DoStateChange([tsEditing], [tsDrawSelecting, tsDrawSelPending, tsToggleFocusedSelection, tsOLEDragPending, tsOLEDragging, tsClearPending, tsDrawSelPending, tsScrollPending, tsScrolling]); - ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, - not (toDisableAutoscrollOnEdit in FOptions.AutoOptions)); if FEditLink.PrepareEdit(Self, FFocusedNode, FEditColumn) then begin UpdateEditBounds; From 8a3735aae4f41b1a31c929abdda5ca406dd03089 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 18 Jun 2020 18:50:58 +0200 Subject: [PATCH 078/681] Fixed issue #976: Access Violation when changing Windows-Theme while editing --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index b1fe5a840..4ef50284c 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -32842,7 +32842,7 @@ procedure TVTEdit.WMDestroy(var Message: TWMDestroy); begin // If editing stopped by other means than accept or cancel then we have to do default processing for // pending changes. - if Assigned(FLink) and not FLink.FStopping then + if Assigned(FLink) and not FLink.FStopping and not (csRecreating in Self.ControlState) then begin with FLink, FTree do begin From fcad421bf7cb487bb4c48c8d956a5ef41be94654 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 19 Jun 2020 20:53:44 +0200 Subject: [PATCH 079/681] Fiuxed issue #978: Problem with recent static text alignment change --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 4ef50284c..6821cc059 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -33665,7 +33665,7 @@ procedure TCustomVirtualStringTree.PaintStaticText(const PaintInfo: TVTPaintInfo end else begin Inc(R.Left, FTextMargin); - if PaintInfo.Alignment = taRightJustify then + if PaintInfo.Alignment = taLeftJustify then Inc(R.Left, NodeWidth); // room for node text end; From 7cb31a69c078b6fceb70998a85714c83123d1541 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 19 Jun 2020 22:42:14 +0200 Subject: [PATCH 080/681] Preparation for #946: Update MainColumn property in case the current MainColumn gets invisible. --- Source/VirtualTrees.pas | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 6821cc059..bcb606d95 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -6862,6 +6862,9 @@ procedure TVirtualTreeColumn.SetOptions(Value: TVTColumnOptions); if coAutoSpring in ToBeSet then FSpringRest := 0; + if coVisible in ToBeCleared then + Owner.Header.UpdateMainColumn(); // Fixes issue #946 + if ((coFixed in ToBeSet) or (coFixed in ToBeCleared)) and (coVisible in FOptions) then Owner.Header.RescaleHeader; @@ -11029,15 +11032,20 @@ procedure TVTHeader.RescaleHeader; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.UpdateMainColumn; +procedure TVTHeader.UpdateMainColumn(); // Called once the load process of the owner tree is done. begin if FMainColumn < 0 then - FMainColumn := 0; + MainColumn := 0; if FMainColumn > FColumns.Count - 1 then - FMainColumn := FColumns.Count - 1; + MainColumn := FColumns.Count - 1; + if (FMainColumn >= 0) and not (coVisible in Self.Columns[FMainColumn].Options) then + begin + // Issue #946: Choose new MainColumn if current one ist not visible + MainColumn := Self.Columns.GetFirstVisibleColumn(); + end end; //---------------------------------------------------------------------------------------------------------------------- From 44847de840165e91230eab31ccda5262c9db2063 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 22 Jun 2020 20:11:10 +0200 Subject: [PATCH 081/681] Introduced TBaseVirtualTree.TrySetFocus() to fix issue #979. --- Source/VirtualTrees.pas | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index bcb606d95..8c27b2b2d 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -2760,6 +2760,7 @@ TBaseVirtualTree = class(TCustomControl) procedure StructureChange(Node: PVirtualNode; Reason: TChangeReason); virtual; function SuggestDropEffect(Source: TObject; Shift: TShiftState; Pt: TPoint; AllowedEffects: Integer): Integer; virtual; procedure ToggleSelection(StartNode, EndNode: PVirtualNode); virtual; + procedure TrySetFocus(); procedure UnselectNodes(StartNode, EndNode: PVirtualNode); virtual; procedure UpdateColumnCheckState(Col: TVirtualTreeColumn); procedure UpdateDesigner; virtual; @@ -19431,7 +19432,7 @@ function TBaseVirtualTree.DoCancelEdit(): Boolean; FOnEditCancelled(Self, FEditColumn); if not (csDestroying in ComponentState) then FEditLink := nil; - SetFocus(); + TrySetFocus(); end; end; @@ -19888,7 +19889,7 @@ function TBaseVirtualTree.DoEndEdit: Boolean; FOnEdited(Self, FFocusedNode, FEditColumn); end; DoStateChange([], [tsEditPending]); - SetFocus(); + TrySetFocus(); end; //---------------------------------------------------------------------------------------------------------------------- @@ -25059,7 +25060,7 @@ procedure TBaseVirtualTree.StartWheelPanning(Position: TPoint); ShowWindow(FPanningWindow, SW_SHOWNOACTIVATE); // Setup the panscroll timer and capture all mouse input. - SetFocus; + TrySetFocus(); SetCapture(Handle); SetTimer(Handle, ScrollTimer, 20, nil); end; @@ -25260,6 +25261,19 @@ procedure TBaseVirtualTree.ToggleSelection(StartNode, EndNode: PVirtualNode); end; end; +procedure TBaseVirtualTree.TrySetFocus(); +begin + if Visible and CanFocus then + begin + try + Self.SetFocus(); + except + on EInvalidOperation do + Exit; + end; + end;//if +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TBaseVirtualTree.UnselectNodes(StartNode, EndNode: PVirtualNode); @@ -25645,8 +25659,8 @@ procedure TBaseVirtualTree.WndProc(var Message: TMessage); if not Handled then begin - if (Message.Msg in [WM_NCLBUTTONDOWN, WM_NCRBUTTONDOWN, WM_NCMBUTTONDOWN]) and not Focused and CanFocus then - SetFocus; + if (Message.Msg in [WM_NCLBUTTONDOWN, WM_NCRBUTTONDOWN, WM_NCMBUTTONDOWN]) and not Focused then + TrySetFocus; inherited; end; end; @@ -32928,7 +32942,7 @@ procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown); end; case EditOptions of - toDefaultEdit: Tree.SetFocus; + toDefaultEdit: Tree.TrySetFocus; toVerticalEdit: if NextNode <> nil then begin @@ -33602,7 +33616,7 @@ procedure TCustomVirtualStringTree.PaintNormalText(var PaintInfo: TVTPaintInfo; if BidiMode <> bdLeftToRight then DrawFormat := DrawFormat or DT_RTLREADING; // Check if the text must be shortend. - if (Column > NoColumn) and ((NodeWidth - 2 * FTextMargin) > R.Right - R.Left) then + if (Column > NoColumn) and ((NodeWidth - 2 * FTextMargin) > R.Width) then begin Text := DoShortenString(Canvas, Node, Column, Text, R.Right - R.Left, TripleWidth); if Alignment = taRightJustify then From c635313d47076f5307039132dd213f97aa4599a1 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 28 Jul 2020 13:44:31 +0200 Subject: [PATCH 082/681] Fixed issue #981 by adding public property TVTHeader.RestoreSelectionColumnIndex --- Demos/Advanced/Advanced.dproj | 3 ++- Source/VirtualTrees.pas | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Demos/Advanced/Advanced.dproj b/Demos/Advanced/Advanced.dproj index b29276705..d58c96f90 100644 --- a/Demos/Advanced/Advanced.dproj +++ b/Demos/Advanced/Advanced.dproj @@ -7,7 +7,7 @@ 1 Application VCL - 18.5 + 19.0 Win32 @@ -77,6 +77,7 @@ CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) true PerMonitorV2 + Debug diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 8c27b2b2d..d1faaf6a3 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1310,8 +1310,8 @@ TVTHeader = class(TPersistent) FSortColumn: TColumnIndex; FSortDirection: TSortDirection; FDragImage: TVTDragImage; // drag image management during header drag - FLastWidth: TDimension; // Used to adjust spring columns. This is the width of all visible columns, - // not the header rectangle. + FLastWidth: TDimension; // Used to adjust spring columns. This is the width of all visible columns, not the header rectangle. + FRestoreSelectionColumnIndex: Integer; // The column that is used to implement the coRestoreSelection option function GetMainColumn: TColumnIndex; function GetUseColumns: Boolean; function IsFontStored: Boolean; @@ -1330,6 +1330,7 @@ TVTHeader = class(TPersistent) procedure SetSortColumn(Value: TColumnIndex); procedure SetSortDirection(const Value: TSortDirection); procedure SetStyle(Value: TVTHeaderStyle); + function GetRestoreSelectionColumnIndex: Integer; protected FStates: THeaderStates; // Used to keep track of internal states the header can enter. FDragStart: TPoint; // initial mouse drag position @@ -1389,6 +1390,7 @@ TVTHeader = class(TPersistent) procedure SaveToStream(const Stream: TStream); virtual; property DragImage: TVTDragImage read FDragImage; + property RestoreSelectionColumnIndex: Integer read GetRestoreSelectionColumnIndex write fRestoreSelectionColumnIndex default NoColumn; property States: THeaderStates read FStates; property Treeview: TBaseVirtualTree read FOwner; property UseColumns: Boolean read GetUseColumns; @@ -10293,6 +10295,14 @@ function TVTHeader.GetOwner: TPersistent; Result := FOwner; end; +function TVTHeader.GetRestoreSelectionColumnIndex: Integer; +begin + if fRestoreSelectionColumnIndex >= 0 then + Result := fRestoreSelectionColumnIndex + else + Result := MainColumn; +end; + //---------------------------------------------------------------------------------------------------------------------- function TVTHeader.GetShiftState: TShiftState; @@ -33810,7 +33820,7 @@ function TCustomVirtualStringTree.AddChild(Parent: PVirtualNode; UserData: Point if (toRestoreSelection in TreeOptions.SelectionOptions) and Assigned(FPreviouslySelected) and Assigned(OnGetText) then begin // See if this was the previously selected node and restore it in this case - Self.OnGetText(Self, Result, 0, ttNormal, NewNodeText); + Self.OnGetText(Self, Result, Header.RestoreSelectionColumnIndex, ttNormal, NewNodeText); if FPreviouslySelected.IndexOf(NewNodeText) >= 0 then begin // Select this node and make sure that the parent node is expanded From 5799a77f74fd58d77e1d68e8d6c0654ecfb3adde Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 28 Jul 2020 13:47:10 +0200 Subject: [PATCH 083/681] Enhanced commnt for toRestoreSelection due to issue #981 --- Source/VirtualTrees.pas | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index d1faaf6a3..4d350082c 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -510,8 +510,9 @@ TCheckStateHelper = record helper for TCheckState // selection rectangle. toAlwaysSelectNode, // If this flag is set to true, the tree view tries to always have a node selected. // This behavior is closer to the Windows TreeView and useful in Windows Explorer style applications. - toRestoreSelection, // Set to true if upon refill the previously selected nodes should be selected again. - // The nodes will be identified by its caption only. + toRestoreSelection, // Set to true if upon refill the previously selected nodes should be selected again. + // The nodes will be identified by its caption (text in MainColumn) + // You may use TVTHeader.RestoreSelectiuonColumnIndex to define an other column that should be used for indentification. toSyncCheckboxesWithSelection // If checkboxes are shown, they follow the change in selections. When checkboxes are // changed, the selections follow them and vice-versa. // **Only supported for ctCheckBox type checkboxes. From 3490a6db03e8fa3a8308e87225d69253d04bfe49 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 5 Aug 2020 20:53:55 +0100 Subject: [PATCH 084/681] Fixed issue #982 --- Source/VirtualTrees.pas | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 4d350082c..d9b999ace 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -28638,11 +28638,14 @@ function TBaseVirtualTree.GetNodeData(pNode: PVirtualNode): T; // Returns the associated data converted to the class given in the generic part of the function. +var + P: Pointer; begin - if Assigned(pNode) then - Result := T(Self.GetNodeData(pNode)^) + P := Self.GetNodeData(pNode); + if Assigned(P) then + Exit(T(P^)) else - Result := Default(T); + Exit(Default(T)); end; //---------------------------------------------------------------------------------------------------------------------- From 613d9988e4d2d46635781dca3411a98ccda68d08 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 27 Aug 2020 08:22:59 +0200 Subject: [PATCH 085/681] Prevent occasional exceptiuon seen in TVTEdit.CreateParams() --- Source/VirtualTrees.pas | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index d9b999ace..a27d32394 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -32795,6 +32795,8 @@ constructor TVTEdit.Create(Link: TStringEditLink); begin inherited Create(nil); + if not Assigned(Link) then + raise EArgumentException.Create('Paramter Link must not be nil.'); ShowHint := False; ParentShowHint := False; // This assignment increases the reference count for the interface. @@ -33082,6 +33084,8 @@ procedure TVTEdit.CreateParams(var Params: TCreateParams); begin inherited; + if not Assigned(FLink.FNode) then + exit; // Prevent AV exceptions occasionally seen in code below // Only with multiline style we can use the text formatting rectangle. // This does not harm formatting as single line control, if we don't use word wrapping. From 7bfbbfe91af3216874e9462403742115e30d5360 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 6 Sep 2020 22:03:54 +0200 Subject: [PATCH 086/681] Work on issue #984: For RAD Studio 10.4 and higher StyleServices.DrawElement() we now sepcify the optional dpi parameter. --- Source/VirtualTrees.StyleHooks.pas | 18 +++++++++--------- Source/VirtualTrees.pas | 23 +++++++++++------------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index 41b727e82..9e02395ac 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -195,23 +195,23 @@ procedure TVclStyleScrollBarsHook.DrawHorzScrollBar(DC: HDC); R.Left := HorzUpButtonRect.Right; R.Right := HorzDownButtonRect.Left; Details := StyleServices.GetElementDetails(tsUpperTrackHorzNormal); - StyleServices.DrawElement(B.Canvas.Handle, Details, R); + StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); if FHorzScrollWnd.Enabled then Details := StyleServices.GetElementDetails(HorzSliderState); - StyleServices.DrawElement(B.Canvas.Handle, Details, HorzSliderRect); + StyleServices.DrawElement(B.Canvas.Handle, Details, HorzSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); if FHorzScrollWnd.Enabled then Details := StyleServices.GetElementDetails(HorzUpState) else Details := StyleServices.GetElementDetails(tsArrowBtnLeftDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, HorzUpButtonRect); + StyleServices.DrawElement(B.Canvas.Handle, Details, HorzUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); if FHorzScrollWnd.Enabled then Details := StyleServices.GetElementDetails(HorzDownState) else Details := StyleServices.GetElementDetails(tsArrowBtnRightDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, HorzDownButtonRect); + StyleServices.DrawElement(B.Canvas.Handle, Details, HorzDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); R := HorzScrollRect; MoveWindowOrg(B.Canvas.Handle, R.Left, R.Top); @@ -242,28 +242,28 @@ procedure TVclStyleScrollBarsHook.DrawVertScrollBar(DC: HDC); R.Bottom := B.Height + R.Top; Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal); - StyleServices.DrawElement(B.Canvas.Handle, Details, R); + StyleServices.DrawElement(B.Canvas.Handle, Details, R {$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); R.Top := VertUpButtonRect.Bottom; R.Bottom := VertDownButtonRect.Top; Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal); - StyleServices.DrawElement(B.Canvas.Handle, Details, R); + StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); if FVertScrollWnd.Enabled then Details := StyleServices.GetElementDetails(VertSliderState); - StyleServices.DrawElement(B.Canvas.Handle, Details, VertSliderRect); + StyleServices.DrawElement(B.Canvas.Handle, Details, VertSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); if FVertScrollWnd.Enabled then Details := StyleServices.GetElementDetails(VertUpState) else Details := StyleServices.GetElementDetails(tsArrowBtnUpDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, VertUpButtonRect); + StyleServices.DrawElement(B.Canvas.Handle, Details, VertUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); if FVertScrollWnd.Enabled then Details := StyleServices.GetElementDetails(VertDownState) else Details := StyleServices.GetElementDetails(tsArrowBtnDownDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, VertDownButtonRect); + StyleServices.DrawElement(B.Canvas.Handle, Details, VertDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); R := VertScrollRect; MoveWindowOrg(B.Canvas.Handle, R.Left, R.Top); diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index a27d32394..f1651ca7c 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -4352,7 +4352,7 @@ function CreateSystemImageSet(pControl: TWinControl): TImageList; Res := GetThemePartSize(Theme, BM.Canvas.Handle, Details.Part, Details.State, nil, TS_TRUE, lSize) = S_OK; end else - Res := StyleServices.GetElementSize(BM.Canvas.Handle, StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal), TElementSize.esActual, lSize); + Res := StyleServices.GetElementSize(BM.Canvas.Handle, StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal), TElementSize.esActual, lSize {$IF CompilerVersion >= 34}, pControl.CurrentPPI{$IFEND}); if not Res then begin lSize := TSize.Create(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK)); if lSize.cx = 0 then begin // error? (Should happen rarely only) @@ -4362,8 +4362,7 @@ function CreateSystemImageSet(pControl: TWinControl): TImageList; end;//if Result := TImageList.CreateSize(lSize.cx, lSize.cy); - with Result do - Handle := ImageList_Create(Width, Height, cFlags, 0, AllocBy); + Result.Handle := ImageList_Create(Result.Width, Result.Height, cFlags, 0, Result.AllocBy); Result.Masked := True; Result.BkColor := clWhite; @@ -5529,19 +5528,19 @@ procedure TVirtualTreeHintWindow.Paint(); (toUseExplorerTheme in Tree.TreeOptions.PaintOptions)) then begin if toUseExplorerTheme in Tree.TreeOptions.PaintOptions then // ToolTip style - StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R) + StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) else begin // Hint style LClipRect := R; InflateRect(R, 4, 4); - StyleServices.DrawElement(Handle, StyleServices.GetElementDetails(tttStandardNormal), R, @LClipRect); + StyleServices.DrawElement(Handle, StyleServices.GetElementDetails(tttStandardNormal), R, @LClipRect{$IF CompilerVersion >= 34}, FCurrentPPI{$IFEND}); R := LClipRect; StyleServices.DrawEdge(Handle, StyleServices.GetElementDetails(twWindowRoot), R, [eeRaisedOuter], [efRect]); end; end else if Tree.VclStyleEnabled then - StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R) + StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) else Rectangle(R); end; @@ -9081,7 +9080,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const if (FHeader.Treeview.VclStyleEnabled and (seClient in FHeader.FOwner.StyleElements)) then begin Details := StyleServices.GetElementDetails(thHeaderItemRightNormal); - StyleServices.DrawElement(Handle, Details, BackgroundRect, @BackgroundRect); + StyleServices.DrawElement(Handle, Details, BackgroundRect, @BackgroundRect {$IF CompilerVersion >= 34}, FHeader.Treeview.FCurrentPPI{$IFEND}); end else if tsUseThemes in FHeader.Treeview.FStates then @@ -9186,7 +9185,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const Details := StyleServices.GetElementDetails(thHeaderItemHot) else Details := StyleServices.GetElementDetails(thHeaderItemNormal); - StyleServices.DrawElement(TargetCanvas.Handle, Details, PaintRectangle, @PaintRectangle); + StyleServices.DrawElement(TargetCanvas.Handle, Details, PaintRectangle, @PaintRectangle{$IF CompilerVersion >= 34}, FHeader.TreeView.FCurrentPPI{$IFEND}); end else begin @@ -9308,7 +9307,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const else Glyph := thHeaderSortArrowSortedDown; Details := StyleServices.GetElementDetails(Glyph); - if not StyleServices.DrawElement(TargetCanvas.Handle, Details, Pos, @Pos) then + if not StyleServices.DrawElement(TargetCanvas.Handle, Details, Pos, @Pos {$IF CompilerVersion >= 34}, FHeader.TreeView.FCurrentPPI {$IFEND}) then PaintInfo.DrawSortArrow(FHeader.FSortDirection); end else @@ -13999,8 +13998,8 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); FillBitmap(FSelectedHotMinusBM); R := Rect(0,0,Size. cx,Size.cy); // tcbCategoryGlyphClosed, tcbCategoryGlyphOpened from CategoryButtons - StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R); - StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R); + StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); + StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); @@ -23926,7 +23925,7 @@ procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas; const ImageInfo: TVT lSize := TSize.Create(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK)); end;//if R := Rect(XPos, YPos, XPos + lSize.cx, YPos + lSize.cy); - StyleServices.DrawElement(Canvas.Handle, Details, R); + StyleServices.DrawElement(Canvas.Handle, Details, R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); Canvas.Refresh; // Every time you give a Canvas.Handle away to some other code you can't control you have to call Canvas.Refresh afterwards because the Canvas object and the HDC can be out of sync. end; if (Index in [ckButtonNormal..ckButtonDisabled]) then begin From 1b25e24673141aa3da1c98a4c036f5ecb0bcad22 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Sep 2020 23:02:47 +0200 Subject: [PATCH 087/681] Use property CurrentPPI instead of member FCurrentPPI. Issue #984 --- Source/VirtualTrees.StyleHooks.pas | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index 9e02395ac..9eed669a7 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -195,23 +195,23 @@ procedure TVclStyleScrollBarsHook.DrawHorzScrollBar(DC: HDC); R.Left := HorzUpButtonRect.Right; R.Right := HorzDownButtonRect.Left; Details := StyleServices.GetElementDetails(tsUpperTrackHorzNormal); - StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); + StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); if FHorzScrollWnd.Enabled then Details := StyleServices.GetElementDetails(HorzSliderState); - StyleServices.DrawElement(B.Canvas.Handle, Details, HorzSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); + StyleServices.DrawElement(B.Canvas.Handle, Details, HorzSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); if FHorzScrollWnd.Enabled then Details := StyleServices.GetElementDetails(HorzUpState) else Details := StyleServices.GetElementDetails(tsArrowBtnLeftDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, HorzUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); + StyleServices.DrawElement(B.Canvas.Handle, Details, HorzUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); if FHorzScrollWnd.Enabled then Details := StyleServices.GetElementDetails(HorzDownState) else Details := StyleServices.GetElementDetails(tsArrowBtnRightDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, HorzDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); + StyleServices.DrawElement(B.Canvas.Handle, Details, HorzDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); R := HorzScrollRect; MoveWindowOrg(B.Canvas.Handle, R.Left, R.Top); @@ -242,28 +242,28 @@ procedure TVclStyleScrollBarsHook.DrawVertScrollBar(DC: HDC); R.Bottom := B.Height + R.Top; Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal); - StyleServices.DrawElement(B.Canvas.Handle, Details, R {$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); + StyleServices.DrawElement(B.Canvas.Handle, Details, R {$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); R.Top := VertUpButtonRect.Bottom; R.Bottom := VertDownButtonRect.Top; Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal); - StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); + StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); if FVertScrollWnd.Enabled then Details := StyleServices.GetElementDetails(VertSliderState); - StyleServices.DrawElement(B.Canvas.Handle, Details, VertSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); + StyleServices.DrawElement(B.Canvas.Handle, Details, VertSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); if FVertScrollWnd.Enabled then Details := StyleServices.GetElementDetails(VertUpState) else Details := StyleServices.GetElementDetails(tsArrowBtnUpDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, VertUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); + StyleServices.DrawElement(B.Canvas.Handle, Details, VertUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); if FVertScrollWnd.Enabled then Details := StyleServices.GetElementDetails(VertDownState) else Details := StyleServices.GetElementDetails(tsArrowBtnDownDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, VertDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.FCurrentPPI{$IFEND}); + StyleServices.DrawElement(B.Canvas.Handle, Details, VertDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); R := VertScrollRect; MoveWindowOrg(B.Canvas.Handle, R.Left, R.Top); From 58a90349cb19fd4181fb37891f40fe3b4a8ea2df Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Sep 2020 23:18:41 +0200 Subject: [PATCH 088/681] Work on issue #984: For RAD Studio 10.4 and higher when calling StyleServices.GetElementSize() we now sepcify the optional dpi parameter. --- Source/VirtualTrees.pas | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index f1651ca7c..c1a3038d3 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -13988,8 +13988,11 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); begin if VclStyleEnabled and (seClient in StyleElements) then begin - Size.cx := ScaledPixels(11); - Size.cy := ScaledPixels(11); + if StyleServices.GetElementSize(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), TElementSize.esActual, Size, {$IF CompilerVersion >= 34}CurrentPPI{$IFEND}) then + Size.cx := ScaledPixels(Size.cx) // I would have expected that the returned value is dpi-sclaed, but this is not the case in rAD Studio 10.4.1. See issue #984 + else + Size.cx := ScaledPixels(11); + Size.cy := Size.cx; FillBitmap(FPlusBM); FillBitmap(FHotPlusBM); FillBitmap(FSelectedHotPlusBM); @@ -13998,8 +14001,8 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); FillBitmap(FSelectedHotMinusBM); R := Rect(0,0,Size. cx,Size.cy); // tcbCategoryGlyphClosed, tcbCategoryGlyphOpened from CategoryButtons - StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); - StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); + StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); + StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); @@ -23919,9 +23922,9 @@ procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas; const ImageInfo: TVT end else begin - if (Index in [ckButtonNormal..ckButtonDisabled]) or not StyleServices.GetElementSize(Canvas.Handle, Details, TElementSize.esActual, lSize) then begin + if (Index in [ckButtonNormal..ckButtonDisabled]) or not StyleServices.GetElementSize(Canvas.Handle, Details, TElementSize.esActual, lSize{$IF CompilerVersion >= 34}, CurrentPPI{$IFEND}) then begin // radio buttons fail in RAD Studio 10 Seattle and lower, fallback to checkbox images. See issue #615 - if not StyleServices.GetElementSize(Canvas.Handle, StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal), TElementSize.esActual, lSize) then + if not StyleServices.GetElementSize(Canvas.Handle, StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal), TElementSize.esActual, lSize{$IF CompilerVersion >= 34}, CurrentPPI{$IFEND}) then lSize := TSize.Create(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK)); end;//if R := Rect(XPos, YPos, XPos + lSize.cx, YPos + lSize.cy); From fd62296fc7f1af1e6995b3187691505da22a2410 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Sep 2020 23:53:23 +0200 Subject: [PATCH 089/681] Omit PPI paramter when calling GetElementSize() when we know that it is not respected. That way the code will not break in case the PPI parameter is respected in a future version. --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index c1a3038d3..826c6c5e5 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -13988,8 +13988,8 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); begin if VclStyleEnabled and (seClient in StyleElements) then begin - if StyleServices.GetElementSize(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), TElementSize.esActual, Size, {$IF CompilerVersion >= 34}CurrentPPI{$IFEND}) then - Size.cx := ScaledPixels(Size.cx) // I would have expected that the returned value is dpi-sclaed, but this is not the case in rAD Studio 10.4.1. See issue #984 + if StyleServices.GetElementSize(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), TElementSize.esActual, Size) then + Size.cx := ScaledPixels(Size.cx) // I would have expected that the returned value is dpi-sclaed, but this is not the case in RAD Studio 10.4.1. See issue #984 else Size.cx := ScaledPixels(11); Size.cy := Size.cx; From 2c022f7b02a531be767b45dc374dc9ad4b34f3a4 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 8 Sep 2020 11:47:39 +0200 Subject: [PATCH 090/681] Fixed compiler erros in 10.3 and below. Issue #984 --- Source/VirtualTrees.pas | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 826c6c5e5..38d886b5b 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -14001,8 +14001,8 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); FillBitmap(FSelectedHotMinusBM); R := Rect(0,0,Size. cx,Size.cy); // tcbCategoryGlyphClosed, tcbCategoryGlyphOpened from CategoryButtons - StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); - StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); + StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); + StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); @@ -23928,7 +23928,7 @@ procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas; const ImageInfo: TVT lSize := TSize.Create(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK)); end;//if R := Rect(XPos, YPos, XPos + lSize.cx, YPos + lSize.cy); - StyleServices.DrawElement(Canvas.Handle, Details, R, {$IF CompilerVersion >= 34}nil, FCurrentPPI{$IFEND}); + StyleServices.DrawElement(Canvas.Handle, Details, R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); Canvas.Refresh; // Every time you give a Canvas.Handle away to some other code you can't control you have to call Canvas.Refresh afterwards because the Canvas object and the HDC can be out of sync. end; if (Index in [ckButtonNormal..ckButtonDisabled]) then begin From b5445222c21d390a3874a9ae47ef06539332d9a6 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 8 Sep 2020 16:13:30 +0200 Subject: [PATCH 091/681] Improved assertion in GetNodeData(): In case the passed Node is nil, wedon't want the assertion. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 38d886b5b..ac3cca804 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -28624,7 +28624,7 @@ function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer; // Returns the address of the user defined data area in the node. begin - Assert(FNodeDataSize > 0, 'NodeDataSize not initialized.'); + Assert((FNodeDataSize > 0) or not Assigned(Node), 'NodeDataSize not initialized.'); if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then Result := nil else From 2cfb90fd93aff6342c9ae25e1a4683cdf570735c Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Fri, 18 Sep 2020 20:33:56 +0200 Subject: [PATCH 092/681] Fixed issue #986 by moving the call to VclStyleChanged() from the end of the constructor to the beginning of CreateWnd(). --- Source/VirtualTrees.pas | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ac3cca804..07b21731d 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -2517,6 +2517,7 @@ TBaseVirtualTree = class(TCustomControl) function GetRangeX: Cardinal; function GetDoubleBuffered: Boolean; procedure SetDoubleBuffered(const Value: Boolean); + function GetVclStyleEnabled: Boolean; inline; protected FFontChanged: Boolean; // flag for keeping informed about font changes in the off screen buffer // [IPK] - private to protected @@ -2779,7 +2780,7 @@ TBaseVirtualTree = class(TCustomControl) procedure WriteNode(Stream: TStream; Node: PVirtualNode); virtual; procedure VclStyleChanged; virtual; - property VclStyleEnabled: Boolean read FVclStyleEnabled; + property VclStyleEnabled: Boolean read GetVclStyleEnabled; property TotalInternalDataSize: Cardinal read FTotalInternalDataSize; property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify; @@ -12112,7 +12113,6 @@ constructor TBaseVirtualTree.Create(AOwner: TComponent); if not (csDesigning in ComponentState) then //Don't create worker thread in IDE, there is no use for it TWorkerThread.AddThreadReference(); - VclStyleChanged(); end; //---------------------------------------------------------------------------------------------------------------------- @@ -13452,6 +13452,11 @@ function TBaseVirtualTree.GetTotalCount: Cardinal; //---------------------------------------------------------------------------------------------------------------------- +function TBaseVirtualTree.GetVclStyleEnabled: Boolean; +begin + Exit(FVclStyleEnabled); +end; + function TBaseVirtualTree.GetVerticalAlignment(Node: PVirtualNode): Byte; begin @@ -18667,6 +18672,7 @@ procedure TBaseVirtualTree.CreateWnd; // Initializes data which depends on a valid window handle. begin + VclStyleChanged(); // Moved here due to issue #986 DoStateChange([tsWindowCreating]); inherited; DoStateChange([], [tsWindowCreating]); From 91060c2363f174833ef27521fb33ace938796ae8 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 26 Sep 2020 15:48:33 +0200 Subject: [PATCH 093/681] Fixed issue #988: In the Advanced demo the scaled property of StateForm is not set. --- Demos/Advanced/States.dfm | 1 - 1 file changed, 1 deletion(-) diff --git a/Demos/Advanced/States.dfm b/Demos/Advanced/States.dfm index 5fa972c14..27981b3d5 100644 --- a/Demos/Advanced/States.dfm +++ b/Demos/Advanced/States.dfm @@ -12,7 +12,6 @@ object StateForm: TStateForm Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False - Scaled = False PixelsPerInch = 96 TextHeight = 13 object EnableCheckBox: TCheckBox From ed32b84f50dccb02abc69f688a5b2f0b305d9581 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 26 Sep 2020 15:57:31 +0200 Subject: [PATCH 094/681] Fixed issue #989: In PrepareBitmaps buttons are unnecessarily created with Vcl.Styles. --- Source/VirtualTrees.pas | 52 +++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 07b21731d..6dfcec9aa 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -13993,30 +13993,32 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); begin if VclStyleEnabled and (seClient in StyleElements) then begin - if StyleServices.GetElementSize(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), TElementSize.esActual, Size) then - Size.cx := ScaledPixels(Size.cx) // I would have expected that the returned value is dpi-sclaed, but this is not the case in RAD Studio 10.4.1. See issue #984 - else - Size.cx := ScaledPixels(11); - Size.cy := Size.cx; - FillBitmap(FPlusBM); - FillBitmap(FHotPlusBM); - FillBitmap(FSelectedHotPlusBM); - FillBitmap(FMinusBM); - FillBitmap(FHotMinusBM); - FillBitmap(FSelectedHotMinusBM); - R := Rect(0,0,Size. cx,Size.cy); - // tcbCategoryGlyphClosed, tcbCategoryGlyphOpened from CategoryButtons - StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); - StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); - FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); - FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); - FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); - FSelectedHotPlusBM.Canvas.Draw(0, 0, FPlusBM); - if Assigned(FOnPrepareButtonImages) then - FOnPrepareButtonImages(Self, FPlusBM, FHotPlusBM, FSelectedHotPlusBM, FMinusBM, FHotMinusBM, FSelectedHotMinusBM, size); - end + if NeedButtons then begin + if StyleServices.GetElementSize(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), TElementSize.esActual, Size) then + Size.cx := ScaledPixels(Size.cx) // I would have expected that the returned value is dpi-sclaed, but this is not the case in RAD Studio 10.4.1. See issue #984 + else + Size.cx := ScaledPixels(11); + Size.cy := Size.cx; + FillBitmap(FPlusBM); + FillBitmap(FHotPlusBM); + FillBitmap(FSelectedHotPlusBM); + FillBitmap(FMinusBM); + FillBitmap(FHotMinusBM); + FillBitmap(FSelectedHotMinusBM); + R := Rect(0,0,Size. cx,Size.cy); + // tcbCategoryGlyphClosed, tcbCategoryGlyphOpened from CategoryButtons + StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); + StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); + FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); + FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); + FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); + FSelectedHotPlusBM.Canvas.Draw(0, 0, FPlusBM); + if Assigned(FOnPrepareButtonImages) then + FOnPrepareButtonImages(Self, FPlusBM, FHotPlusBM, FSelectedHotPlusBM, FMinusBM, FHotMinusBM, FSelectedHotMinusBM, size); + end;//if NeedButtons + end// if VclStyleEnabled else - begin + begin // No stlye Size.cx := 9; Size.cy := 9; if tsUseThemes in FStates then @@ -14157,8 +14159,8 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); end; if tsUseThemes in FStates then CloseThemeData(Theme); - end; - end; + end;// if NeedButtons + end;// else if NeedLines then begin From 1dda39020977d85fb83c06863947f920d2dd0a91 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 26 Sep 2020 16:32:52 +0200 Subject: [PATCH 095/681] Fixed issue #990: Make Advanced demo per monitor DPI aware. --- Demos/Advanced/Main.pas | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Demos/Advanced/Main.pas b/Demos/Advanced/Main.pas index 0499f900b..eb5359af9 100644 --- a/Demos/Advanced/Main.pas +++ b/Demos/Advanced/Main.pas @@ -184,11 +184,13 @@ procedure TMainForm.DemoButtonClick(Sender: TObject); if Assigned(NewDemoClass) then begin NewDemo := NewDemoClass.Create(Self); - NewDemo.Hide; NewDemo.BorderStyle := bsNone; - NewDemo.Parent := ContainerPanel; NewDemo.Align := alClient; + NewDemo.Parent := ContainerPanel; NewDemo.Show; + {$if CompilerVersion >= 33} + NewDemo.ScaleForPPI(FCurrentPPI); // See issue #990 + {$endif} end; end; end; From c1d230714ff6656598dcf691f2403904c81a1711 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 26 Sep 2020 16:44:25 +0200 Subject: [PATCH 096/681] Fixed issue #991: PrepareBitmaps() is called incidentally and at the wrong time in DPI-aware applications. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 6dfcec9aa..9df5f886c 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -15812,7 +15812,6 @@ procedure TBaseVirtualTree.CMFontChanged(var Message: TMessage); if not (csLoading in ComponentState) then begin - PrepareBitmaps(True, False); if HandleAllocated then begin AutoScale(False); Invalidate; @@ -18439,6 +18438,7 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; end;// if M<>D end;//if toAutoChangeScale inherited ChangeScale(M, D{$if CompilerVersion >= 31}, isDpiChange{$ifend}); + PrepareBitmaps(True, False); // It is important to do this call after calling inherited, so that the Font has been updated. AutoScale(M <> D); end; From 69df3c13f5d6fe339e76b0a933c124125042ed66 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 26 Sep 2020 21:03:46 +0200 Subject: [PATCH 097/681] Set flag toAutoScale for proper dpi scaling. --- Demos/Advanced/DrawTreeDemo.dfm | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Demos/Advanced/DrawTreeDemo.dfm b/Demos/Advanced/DrawTreeDemo.dfm index 07aee5fef..a19e169e7 100644 --- a/Demos/Advanced/DrawTreeDemo.dfm +++ b/Demos/Advanced/DrawTreeDemo.dfm @@ -67,9 +67,7 @@ object DrawTreeForm: TDrawTreeForm Header.Background = clBtnHighlight Header.Height = 22 Header.Options = [hoColumnResize, hoDblClickResize, hoDrag, hoRestrictDrag, hoShowSortGlyphs, hoVisible] - Header.ParentFont = True Header.Style = hsPlates - HintAnimation = hatNone HintMode = hmHint Images = SystemImages IncrementalSearch = isAll @@ -80,8 +78,7 @@ object DrawTreeForm: TDrawTreeForm ScrollBarOptions.VerticalIncrement = 32 ShowHint = True TabOrder = 0 - TreeOptions.AnimationOptions = [toAnimatedToggle] - TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScroll, toAutoScrollOnExpand, toAutoTristateTracking, toAutoDeleteMovedNodes] + TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScroll, toAutoScrollOnExpand, toAutoTristateTracking, toAutoDeleteMovedNodes, toAutoChangeScale] TreeOptions.PaintOptions = [toShowBackground, toShowButtons, toShowDropmark, toShowHorzGridLines, toShowRoot, toShowTreeLines, toShowVertGridLines, toThemeAware] TreeOptions.SelectionOptions = [toFullRowSelect] OnCompareNodes = VDT1CompareNodes @@ -100,18 +97,18 @@ object DrawTreeForm: TDrawTreeForm BiDiMode = bdLeftToRight Options = [coAllowClick, coEnabled, coParentColor, coResizable, coShowDropMark, coVisible] Position = 0 + Text = 'Image file name' Width = 217 - WideText = 'Image file name' end item Position = 1 + Text = 'Thumbnail' Width = 200 - WideText = 'Thumbnail' end item Position = 2 + Text = 'Properties' Width = 160 - WideText = 'Properties' end> end object TrackBar1: TTrackBar From 2af93d193eb22122a491935eb1f5a3d1902264df Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 26 Sep 2020 21:06:09 +0200 Subject: [PATCH 098/681] Set flag toAutoScale for proper dpi scaling. --- Demos/Advanced/PropertiesDemo.dfm | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Demos/Advanced/PropertiesDemo.dfm b/Demos/Advanced/PropertiesDemo.dfm index 7a40264de..1f1739d3d 100644 --- a/Demos/Advanced/PropertiesDemo.dfm +++ b/Demos/Advanced/PropertiesDemo.dfm @@ -50,14 +50,8 @@ object PropertiesForm: TPropertiesForm Colors.HotColor = clBlack DefaultNodeHeight = 20 Header.AutoSizeIndex = 1 - Header.Font.Charset = DEFAULT_CHARSET - Header.Font.Color = clWindowText - Header.Font.Height = -11 - Header.Font.Name = 'Tahoma' - Header.Font.Style = [] Header.Height = 18 Header.Options = [hoAutoResize, hoColumnResize, hoVisible, hoAutoSpring] - HintAnimation = hatFade HintMode = hmHint Images = TreeImages IncrementalSearch = isAll @@ -68,7 +62,6 @@ object PropertiesForm: TPropertiesForm ShowHint = True TabOrder = 0 TreeOptions.AnimationOptions = [toAnimatedToggle] - TreeOptions.AutoOptions = [toAutoDropExpand, toAutoTristateTracking, toAutoDeleteMovedNodes] TreeOptions.MiscOptions = [toAcceptOLEDrop, toEditable, toGridExtensions, toInitOnSave, toToggleOnDblClick, toWheelPanning] TreeOptions.SelectionOptions = [toExtendedFocus, toFullRowSelect, toCenterScrollIntoView] TreeOptions.StringOptions = [toAutoAcceptEditChange] @@ -88,14 +81,14 @@ object PropertiesForm: TPropertiesForm item Options = [coAllowClick, coDraggable, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible, coAutoSpring] Position = 0 + Text = 'Properties' Width = 203 - WideText = 'Properties' end item Options = [coAllowClick, coDraggable, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible, coAutoSpring] Position = 1 + Text = 'Values' Width = 192 - WideText = 'Values' end> end object RadioGroup1: TRadioGroup From cb2281b02137dafa3ca76358a051adec4bdede29 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 26 Sep 2020 21:09:10 +0200 Subject: [PATCH 099/681] Set flag toAutoScale for proper dpi scaling. --- Demos/Advanced/GridDemo.dfm | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Demos/Advanced/GridDemo.dfm b/Demos/Advanced/GridDemo.dfm index 34732e7a1..ae3e577da 100644 --- a/Demos/Advanced/GridDemo.dfm +++ b/Demos/Advanced/GridDemo.dfm @@ -85,15 +85,9 @@ object GridForm: TGridForm Font.Style = [] Header.AutoSizeIndex = 2 Header.Background = clBtnShadow - Header.Font.Charset = ANSI_CHARSET - Header.Font.Color = clWindowText - Header.Font.Height = -12 - Header.Font.Name = 'Microsoft Sans Serif' - Header.Font.Style = [] Header.Height = 20 Header.Options = [hoColumnResize, hoDblClickResize, hoDrag, hoShowImages, hoVisible] Header.Style = hsFlatButtons - HintAnimation = hatFade HintMode = hmTooltip ParentFont = False ParentShowHint = False @@ -102,7 +96,7 @@ object GridForm: TGridForm ScrollBarOptions.AlwaysVisible = True ShowHint = True TabOrder = 0 - TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScroll, toAutoTristateTracking] + TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScroll, toAutoScrollOnExpand, toAutoTristateTracking, toAutoChangeScale] TreeOptions.MiscOptions = [toAcceptOLEDrop, toEditable, toGridExtensions, toInitOnSave, toToggleOnDblClick, toWheelPanning] TreeOptions.PaintOptions = [toHotTrack, toShowButtons, toShowDropmark, toShowHorzGridLines, toShowVertGridLines, toUseBlendedImages] TreeOptions.SelectionOptions = [toDisableDrawSelection, toExtendedFocus, toMiddleClickSelect, toMultiSelect, toRightClickSelect, toCenterScrollIntoView] @@ -127,26 +121,26 @@ object GridForm: TGridForm item Margin = 0 Position = 1 + Text = 'Customer ID' Width = 100 - WideText = 'Customer ID' end item Margin = 0 Position = 2 + Text = 'First Name' Width = 120 - WideText = 'First Name' end item Margin = 0 Position = 3 + Text = 'Last Name' Width = 120 - WideText = 'Last Name' end item Margin = 0 Position = 4 + Text = 'Order date' Width = 100 - WideText = 'Order date' end> end object GridLineCheckBox: TCheckBox From 248ae0c3582714eae99bad4ac743ee5b016e09a0 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 26 Sep 2020 21:15:49 +0200 Subject: [PATCH 100/681] -issue #991: No need to call PrepareBitmaps() in ChnageScale() if M == D. --- Source/VirtualTrees.pas | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 9df5f886c..5695a9161 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18438,7 +18438,8 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; end;// if M<>D end;//if toAutoChangeScale inherited ChangeScale(M, D{$if CompilerVersion >= 31}, isDpiChange{$ifend}); - PrepareBitmaps(True, False); + if (M <> D) then + PrepareBitmaps(True, False); // See issue #991 // It is important to do this call after calling inherited, so that the Font has been updated. AutoScale(M <> D); end; From ca85e5a21736e456e822b420974fd53648e4f2da Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 14 Oct 2020 07:23:26 +0100 Subject: [PATCH 101/681] Fixed issue #992: Incorrect scaling of hint font --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 5695a9161..543328a24 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -5476,7 +5476,7 @@ procedure TVirtualTreeHintWindow.Paint(); if (Node = nil) or (Tree.FHintMode <> hmToolTip) then begin Canvas.Font := Screen.HintFont; - Canvas.Font.Height := Tree.ScaledPixels(Canvas.Font.Height); + Canvas.Font.Height := MulDiv(Canvas.Font.Height, Tree.ScaledPixels(96), Screen.PixelsPerInch); // See issue #992 Y := 2; end else @@ -5609,7 +5609,7 @@ function TVirtualTreeHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: str if (Node = nil) or (Tree.FHintMode <> hmToolTip) then begin Canvas.Font := Screen.HintFont; - Canvas.Font.Height := Tree.ScaledPixels(Canvas.Font.Height); + Canvas.Font.Height := MulDiv(Canvas.Font.Height, Tree.ScaledPixels(96), Screen.PixelsPerInch); // See issue #992 end else begin From 76ab7c91d2b6c57364302a8c5bc6b991ae7adac0 Mon Sep 17 00:00:00 2001 From: Sebastian Modersohn Date: Thu, 22 Oct 2020 11:18:33 +0200 Subject: [PATCH 102/681] Fixed issue #995: Button style not scaled when not using VCLStyles * call OpentThemeDataForDPI instead of OpenThemeData * default size of 9x9 must be scaled too, if tsUseThemes is not set --- Source/VirtualTrees.pas | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 543328a24..19f6fe58f 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -14019,12 +14019,12 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); end// if VclStyleEnabled else begin // No stlye - Size.cx := 9; - Size.cy := 9; + Size.cx := ScaledPixels(9); + Size.cy := ScaledPixels(9); if tsUseThemes in FStates then begin R := Rect(0, 0, 100, 100); - Theme := OpenThemeData(Handle, 'TREEVIEW'); + Theme:= OpenThemeDataForDPI(Handle, 'TREEVIEW', {$if CompilerVersion > 31}Self.FCurrentPPI{$else}Screen.PixelsPerInch{$ifend}); GetThemePartSize(Theme, FPlusBM.Canvas.Handle, TVP_GLYPH, GLPS_OPENED, @R, TS_TRUE, Size); end else From 58dec56ef3aa96b23640e4a1a96b41a0dcefe239 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 29 Oct 2020 21:05:31 +0100 Subject: [PATCH 103/681] Imporved compatibility with RAD Studio 10.2 and below. --- Source/VirtualTrees.pas | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 19f6fe58f..683fcb646 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -14024,7 +14024,11 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); if tsUseThemes in FStates then begin R := Rect(0, 0, 100, 100); - Theme:= OpenThemeDataForDPI(Handle, 'TREEVIEW', {$if CompilerVersion > 31}Self.FCurrentPPI{$else}Screen.PixelsPerInch{$ifend}); + {$if CompilerVersion >= 33} + Theme := OpenThemeDataForDPI(Handle, 'TREEVIEW', Self.FCurrentPPI); + {$else} + Theme := OpenThemeData(Handle, 'TREEVIEW'); + {$ifend} GetThemePartSize(Theme, FPlusBM.Canvas.Handle, TVP_GLYPH, GLPS_OPENED, @R, TS_TRUE, Size); end else From 3c8051c53f1e2540a470db876a79e77975103c5b Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 29 Oct 2020 21:11:36 +0100 Subject: [PATCH 104/681] Added setter to OnPrepareButtonBitmaps event so that button are reloaded. --- Source/VirtualTrees.pas | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 683fcb646..9c6afc0a9 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -2518,6 +2518,7 @@ TBaseVirtualTree = class(TCustomControl) function GetDoubleBuffered: Boolean; procedure SetDoubleBuffered(const Value: Boolean); function GetVclStyleEnabled: Boolean; inline; + procedure SetOnPrepareButtonImages(const Value: TVTPrepareButtonImagesEvent); protected FFontChanged: Boolean; // flag for keeping informed about font changes in the off screen buffer // [IPK] - private to protected @@ -2970,7 +2971,7 @@ TBaseVirtualTree = class(TCustomControl) property OnNodeMoved: TVTNodeMovedEvent read FOnNodeMoved write FOnNodeMoved; property OnNodeMoving: TVTNodeMovingEvent read FOnNodeMoving write FOnNodeMoving; property OnPaintBackground: TVTBackgroundPaintEvent read FOnPaintBackground write FOnPaintBackground; - property OnPrepareButtonBitmaps : TVTPrepareButtonImagesEvent read FOnPrepareButtonImages write FOnPrepareButtonImages; + property OnPrepareButtonBitmaps : TVTPrepareButtonImagesEvent read FOnPrepareButtonImages write SetOnPrepareButtonImages; property OnRemoveFromSelection: TVTRemoveFromSelectionEvent read FOnRemoveFromSelection write FOnRemoveFromSelection; property OnRenderOLEData: TVTRenderOLEDataEvent read FOnRenderOLEData write FOnRenderOLEData; property OnResetNode: TVTChangeEvent read FOnResetNode write FOnResetNode; @@ -15080,6 +15081,12 @@ procedure TBaseVirtualTree.SetOffsetY(const Value: Integer); DoSetOffsetXY(Point(FOffsetX, Value), DefaultScrollUpdateFlags); end; +procedure TBaseVirtualTree.SetOnPrepareButtonImages(const Value: TVTPrepareButtonImagesEvent); +begin + FOnPrepareButtonImages := Value; + PrepareBitmaps(True, False); +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TBaseVirtualTree.SetOptions(const Value: TCustomVirtualTreeOptions); From e5bea6ac9cae3de1a94b3265837c3209f242fc75 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 29 Oct 2020 23:40:13 +0100 Subject: [PATCH 105/681] Added make target for Delphi 10.3 --- MAKEFILE | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/MAKEFILE b/MAKEFILE index 9df6db19a..2ad074fbd 100644 --- a/MAKEFILE +++ b/MAKEFILE @@ -26,6 +26,14 @@ _XE8: Lib\*.pas "Package\RAD Studio XE8\$(PROJECT)R.dpk" "Package\RAD Studio XE8 $(BUILD) /property:Platform=Win64 "Packages\RAD Studio 10.2\$(PROJECT)R.dproj" $(MAKE) _samples +# build all packahes for Delphi 10.3 +10_3: Source\*.pas "Packages\RAD Studio 10.3\$(PROJECT)R.dpk" "Packages\RAD Studio 10.3\$(PROJECT)R.dproj" "Packages\RAD Studio 10.3\$(PROJECT)D.dpk" "Packages\RAD Studio 10.3\$(PROJECT)D.dproj" + SET BDS=$(STUDIO)\20.0 + $(BUILD) "Packages\RAD Studio 10.3\$(PROJECT)R.dproj" + $(BUILD) "Packages\RAD Studio 10.3\$(PROJECT)D.dproj" + $(BUILD) /property:Platform=Win64 "Packages\RAD Studio 10.3\$(PROJECT)R.dproj" + $(MAKE) _samples + "Demos\Advanced\Advanced.exe": "Demos\Advanced\*.dproj" "Demos\Advanced\*.dpr" "Demos\Advanced\*.pas" $(BUILD) "Demos\Advanced\Advanced.dproj" @@ -40,7 +48,7 @@ _XE8: Lib\*.pas "Package\RAD Studio XE8\$(PROJECT)R.dpk" "Package\RAD Studio XE8 _samples: "Demos\Advanced\Advanced.exe" "Demos\Minimal\Minimal.exe" "Demos\Objects\Objects.exe" "Demos\OLE\OLE.exe" -_continuousbuilds: clean 10_2 +_continuousbuilds: clean 10_3 _release: #This small batch file is intended to create a source code release file of the VirtualTreeView as ZIP archive From ce6a49955d8c2937d94cfaed19110648af1ee933 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 29 Oct 2020 21:05:31 +0100 Subject: [PATCH 106/681] Imporved compatibility with RAD Studio 10.2 and below. --- Source/VirtualTrees.pas | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 19f6fe58f..683fcb646 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -14024,7 +14024,11 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); if tsUseThemes in FStates then begin R := Rect(0, 0, 100, 100); - Theme:= OpenThemeDataForDPI(Handle, 'TREEVIEW', {$if CompilerVersion > 31}Self.FCurrentPPI{$else}Screen.PixelsPerInch{$ifend}); + {$if CompilerVersion >= 33} + Theme := OpenThemeDataForDPI(Handle, 'TREEVIEW', Self.FCurrentPPI); + {$else} + Theme := OpenThemeData(Handle, 'TREEVIEW'); + {$ifend} GetThemePartSize(Theme, FPlusBM.Canvas.Handle, TVP_GLYPH, GLPS_OPENED, @R, TS_TRUE, Size); end else From 47c3c01c0f3f985a961f39fa5f135dee94043ff6 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 29 Oct 2020 21:11:36 +0100 Subject: [PATCH 107/681] Added setter to OnPrepareButtonBitmaps event so that button are reloaded. --- Source/VirtualTrees.pas | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 683fcb646..9c6afc0a9 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -2518,6 +2518,7 @@ TBaseVirtualTree = class(TCustomControl) function GetDoubleBuffered: Boolean; procedure SetDoubleBuffered(const Value: Boolean); function GetVclStyleEnabled: Boolean; inline; + procedure SetOnPrepareButtonImages(const Value: TVTPrepareButtonImagesEvent); protected FFontChanged: Boolean; // flag for keeping informed about font changes in the off screen buffer // [IPK] - private to protected @@ -2970,7 +2971,7 @@ TBaseVirtualTree = class(TCustomControl) property OnNodeMoved: TVTNodeMovedEvent read FOnNodeMoved write FOnNodeMoved; property OnNodeMoving: TVTNodeMovingEvent read FOnNodeMoving write FOnNodeMoving; property OnPaintBackground: TVTBackgroundPaintEvent read FOnPaintBackground write FOnPaintBackground; - property OnPrepareButtonBitmaps : TVTPrepareButtonImagesEvent read FOnPrepareButtonImages write FOnPrepareButtonImages; + property OnPrepareButtonBitmaps : TVTPrepareButtonImagesEvent read FOnPrepareButtonImages write SetOnPrepareButtonImages; property OnRemoveFromSelection: TVTRemoveFromSelectionEvent read FOnRemoveFromSelection write FOnRemoveFromSelection; property OnRenderOLEData: TVTRenderOLEDataEvent read FOnRenderOLEData write FOnRenderOLEData; property OnResetNode: TVTChangeEvent read FOnResetNode write FOnResetNode; @@ -15080,6 +15081,12 @@ procedure TBaseVirtualTree.SetOffsetY(const Value: Integer); DoSetOffsetXY(Point(FOffsetX, Value), DefaultScrollUpdateFlags); end; +procedure TBaseVirtualTree.SetOnPrepareButtonImages(const Value: TVTPrepareButtonImagesEvent); +begin + FOnPrepareButtonImages := Value; + PrepareBitmaps(True, False); +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TBaseVirtualTree.SetOptions(const Value: TCustomVirtualTreeOptions); From 1e1d0a65116088833434b6d8ac946489e5bd97b9 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 29 Oct 2020 23:40:13 +0100 Subject: [PATCH 108/681] Added make target for Delphi 10.3 --- MAKEFILE | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/MAKEFILE b/MAKEFILE index 9df6db19a..2ad074fbd 100644 --- a/MAKEFILE +++ b/MAKEFILE @@ -26,6 +26,14 @@ _XE8: Lib\*.pas "Package\RAD Studio XE8\$(PROJECT)R.dpk" "Package\RAD Studio XE8 $(BUILD) /property:Platform=Win64 "Packages\RAD Studio 10.2\$(PROJECT)R.dproj" $(MAKE) _samples +# build all packahes for Delphi 10.3 +10_3: Source\*.pas "Packages\RAD Studio 10.3\$(PROJECT)R.dpk" "Packages\RAD Studio 10.3\$(PROJECT)R.dproj" "Packages\RAD Studio 10.3\$(PROJECT)D.dpk" "Packages\RAD Studio 10.3\$(PROJECT)D.dproj" + SET BDS=$(STUDIO)\20.0 + $(BUILD) "Packages\RAD Studio 10.3\$(PROJECT)R.dproj" + $(BUILD) "Packages\RAD Studio 10.3\$(PROJECT)D.dproj" + $(BUILD) /property:Platform=Win64 "Packages\RAD Studio 10.3\$(PROJECT)R.dproj" + $(MAKE) _samples + "Demos\Advanced\Advanced.exe": "Demos\Advanced\*.dproj" "Demos\Advanced\*.dpr" "Demos\Advanced\*.pas" $(BUILD) "Demos\Advanced\Advanced.dproj" @@ -40,7 +48,7 @@ _XE8: Lib\*.pas "Package\RAD Studio XE8\$(PROJECT)R.dpk" "Package\RAD Studio XE8 _samples: "Demos\Advanced\Advanced.exe" "Demos\Minimal\Minimal.exe" "Demos\Objects\Objects.exe" "Demos\OLE\OLE.exe" -_continuousbuilds: clean 10_2 +_continuousbuilds: clean 10_3 _release: #This small batch file is intended to create a source code release file of the VirtualTreeView as ZIP archive From 94c7cd42e0f82ae014a3eb7d93ba6d07e5e8c8d9 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 1 Nov 2020 17:14:06 +0100 Subject: [PATCH 109/681] Added TVTHeader.StyleChanged() that is called by TBaseVirtualTree.VclStyleChanged() so that header elements can be adjusted to a new VCL style. --- Source/VirtualTrees.pas | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 9c6afc0a9..aa9cd42f7 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1389,6 +1389,7 @@ TVTHeader = class(TPersistent) Options: TVTColumnOptions = [coVisible]): TDimension; procedure RestoreColumns; procedure SaveToStream(const Stream: TStream); virtual; + procedure StyleChanged(); property DragImage: TVTDragImage read FDragImage; property RestoreSelectionColumnIndex: Integer read GetRestoreSelectionColumnIndex write fRestoreSelectionColumnIndex default NoColumn; @@ -9917,6 +9918,11 @@ procedure TVTHeader.SetStyle(Value: TVTHeaderStyle); end; end; +procedure TVTHeader.StyleChanged(); +begin + AutoScale(); // Elements may have chnaged in size +end; + //---------------------------------------------------------------------------------------------------------------------- function TVTHeader.CanWriteColumns: Boolean; @@ -25646,12 +25652,13 @@ procedure TBaseVirtualTree.ValidateNodeDataSize(var Size: Integer); //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.VclStyleChanged; +procedure TBaseVirtualTree.VclStyleChanged(); // Updates the member FVclStyleEnabled, should be called initially and when the VCL style changes begin FVclStyleEnabled := StyleServices.Enabled and not StyleServices.IsSystemStyle and not (csDesigning in ComponentState); + Header.StyleChanged(); end; //---------------------------------------------------------------------------------------------------------------------- From 45e8ac44fc0a6c1e701d4bd9a8b1c06b9f6e1df0 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 2 Nov 2020 09:07:37 +0100 Subject: [PATCH 110/681] Update VirtualTrees.pas Forgot virtual for new TVTHeader.StyleChnaged(); --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index aa9cd42f7..638b01eb1 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1389,7 +1389,7 @@ TVTHeader = class(TPersistent) Options: TVTColumnOptions = [coVisible]): TDimension; procedure RestoreColumns; procedure SaveToStream(const Stream: TStream); virtual; - procedure StyleChanged(); + procedure StyleChanged(); virtual; property DragImage: TVTDragImage read FDragImage; property RestoreSelectionColumnIndex: Integer read GetRestoreSelectionColumnIndex write fRestoreSelectionColumnIndex default NoColumn; From 17882879f4a0ad62518c8826539337eb0c24c966 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 15 Nov 2020 14:53:46 +0100 Subject: [PATCH 111/681] Fixed issue #997: OpenThemeDataForDpi() exists in Windows 10 1703 and later only. --- Source/VirtualTrees.pas | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 638b01eb1..928c942c1 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -14032,7 +14032,10 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); begin R := Rect(0, 0, 100, 100); {$if CompilerVersion >= 33} - Theme := OpenThemeDataForDPI(Handle, 'TREEVIEW', Self.FCurrentPPI); + if TOSVersion.Check(10) and (TOSVersion.Build >= 15063) then + Theme := OpenThemeDataForDPI(Handle, 'TREEVIEW', Self.FCurrentPPI) + else + Theme := OpenThemeData(Handle, 'TREEVIEW'); {$else} Theme := OpenThemeData(Handle, 'TREEVIEW'); {$ifend} From a95f9632df4751d603510da4eeebb1e34a8e98b8 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 22 Nov 2020 23:00:10 +0100 Subject: [PATCH 112/681] Isse #998: Fixed if statement and set TVTFixedAreaConstraints.MaxWidthPercent to a better default value. --- Source/VirtualTrees.pas | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 928c942c1..379e3d348 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1219,7 +1219,7 @@ TVTFixedAreaConstraints = class(TPersistent) property OnChange: TNotifyEvent read FOnChange write FOnChange; published property MaxHeightPercent: TVTConstraintPercent index 0 read FMaxHeightPercent write SetConstraints default 0; - property MaxWidthPercent: TVTConstraintPercent index 1 read FMaxWidthPercent write SetConstraints default 0; + property MaxWidthPercent: TVTConstraintPercent index 1 read FMaxWidthPercent write SetConstraints default 95; property MinHeightPercent: TVTConstraintPercent index 2 read FMinHeightPercent write SetConstraints default 0; property MinWidthPercent: TVTConstraintPercent index 3 read FMinWidthPercent write SetConstraints default 0; end; @@ -9465,7 +9465,7 @@ constructor TVTFixedAreaConstraints.Create(AOwner: TVTHeader); begin inherited Create; - + FMaxWidthPercent := 95; FHeader := AOwner; end; @@ -11030,7 +11030,7 @@ procedure TVTHeader.RescaleHeader; RecalculateHeader; with FFixedAreaConstraints do - if (FMinHeightPercent > 0) or (FMaxHeightPercent > 0) then + if (FMaxWidthPercent > 0) or (FMinWidthPercent > 0) or (FMinHeightPercent > 0) or (FMaxHeightPercent > 0) then begin ComputeConstraints; From ed4dc0f05d2014ad79897cc6f6a1de3b5a6db16b Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 1 Dec 2020 21:01:27 +0100 Subject: [PATCH 113/681] Fixed issue #1007: TCustomVirtualStringTree.InitializeTextProperties() doesn't honor option toPopupMode . --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 379e3d348..a1cf10696 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -33589,7 +33589,7 @@ procedure TCustomVirtualStringTree.InitializeTextProperties(var PaintInfo: TVTPa if Node = FDropTargetNode then begin if ((FLastDropMode = dmOnNode) or (vsSelected in Node.States)) then - Canvas.Font.Color := FColors.GetSelectedNodeFontColor(Focused); + Canvas.Font.Color := FColors.GetSelectedNodeFontColor(Focused or (toPopupMode in FOptions.FPaintOptions)); end else if vsSelected in Node.States then From 5d939f093951d2f2c999fd8459dbc301635ba596 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 1 Dec 2020 21:12:40 +0100 Subject: [PATCH 114/681] Fixed issue #1002: Prevent ImageList notifications after destruction --- Source/VirtualTrees.pas | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index a1cf10696..f76b6c372 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -12181,6 +12181,11 @@ destructor TBaseVirtualTree.Destroy; FSelectedHotPlusBM.Free; FSelectedHotMinusBM.Free; + // Fixes issue #1002 + Images := nil; + StateImages := nil; + CustomCheckImages := nil; + inherited; end; From 1ef496f1185b82f08a2945e9d044c96167305165 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 1 Dec 2020 23:10:36 +0100 Subject: [PATCH 115/681] Fix for issue #1000: Scale TotalHeight along with NodeHeight for non-initialized nodes. --- Source/VirtualTrees.pas | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index f76b6c372..53619afc1 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18453,7 +18453,10 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; if vsInitialized in Run.States then SetNodeHeight(Run, MulDiv(Run.NodeHeight, M, D)) else // prevent initialization of non-initialzed nodes + begin Run.NodeHeight := MulDiv(Run.NodeHeight, M, D); + Run.TotalHeight := MulDiv(Run.TotalHeight, M, D); // Fixes issue #1000 + end; Run := GetNextNoInit(Run); end; // while finally From ac8813049c8dd1554b61b941b5f0c4cdaa93821c Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 07:48:32 +0100 Subject: [PATCH 116/681] Set toAutoSort to False for more speed. --- Demos/Advanced/SpeedDemo.dfm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Demos/Advanced/SpeedDemo.dfm b/Demos/Advanced/SpeedDemo.dfm index 2a545939f..419af70ac 100644 --- a/Demos/Advanced/SpeedDemo.dfm +++ b/Demos/Advanced/SpeedDemo.dfm @@ -1556,20 +1556,15 @@ object SpeedForm: TSpeedForm Colors.BorderColor = clWindowText Colors.HotColor = clBlack Header.AutoSizeIndex = -1 - Header.Font.Charset = DEFAULT_CHARSET - Header.Font.Color = clWindowText - Header.Font.Height = -11 - Header.Font.Name = 'Tahoma' - Header.Font.Style = [] Header.MainColumn = -1 Header.Options = [hoColumnResize, hoDrag, hoHotTrack] - HintAnimation = hatNone IncrementalSearch = isAll ParentBiDiMode = False ParentShowHint = False ScrollBarOptions.VerticalIncrement = 18 ShowHint = True TabOrder = 0 + TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScrollOnExpand, toAutoTristateTracking, toAutoDeleteMovedNodes, toAutoChangeScale] TreeOptions.MiscOptions = [toAcceptOLEDrop, toEditable, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning] TreeOptions.SelectionOptions = [toMultiSelect] OnChange = VST1Change From e8e19bdc380f4cbbfd9f7af9698387753e09916f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 08:08:57 +0100 Subject: [PATCH 117/681] Possible fix for issue #1000: Check HandleAllocated also in the queued code as the control hanlde may have been destryoed. --- Source/VirtualTrees.pas | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 53619afc1..12c55a56f 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18480,11 +18480,10 @@ procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirt if (Self.HandleAllocated) then TThread.Synchronize(nil, procedure begin - // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) - if not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then + // issue #1000 // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) + if HandleAllocated and not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then DoStateChange(EnterStates, LeaveStates) end); - end; //---------------------------------------------------------------------------------------------------------------------- From 37f7d9afc60ab3b2ac5d0206c013a1938e293fc9 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 08:10:17 +0100 Subject: [PATCH 118/681] Corrected issue number --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 12c55a56f..01c129532 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18480,7 +18480,7 @@ procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirt if (Self.HandleAllocated) then TThread.Synchronize(nil, procedure begin - // issue #1000 // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) + // issue #1001 // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) if HandleAllocated and not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then DoStateChange(EnterStates, LeaveStates) end); From f9d7e8b9ba61876093d6198efcb31d10a9095b4a Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 21:52:25 +0100 Subject: [PATCH 119/681] Reverted previous commit for #1001 --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 01c129532..4a9bb2310 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18480,8 +18480,8 @@ procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirt if (Self.HandleAllocated) then TThread.Synchronize(nil, procedure begin - // issue #1001 // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) - if HandleAllocated and not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then + // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) + if not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then DoStateChange(EnterStates, LeaveStates) end); end; From 47d065230327a56d37db794478963b1dc4a1a9a1 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 21:56:41 +0100 Subject: [PATCH 120/681] Chnages related to issue #1001: * TWorkerThread.ReleaseThreadReference() calls CheckSynchronize() to Make sure code queued in the main thread by TBaseVirtualTree.ChangeTreeStatesAsync() get processed before the tree is being destroyed. * Removed unused property CurrentTree from TWorkerThread. --- Source/VirtualTrees.WorkerThread.pas | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 0359da533..fd1336f74 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -28,8 +28,6 @@ TWorkerThread = class(TThread) class procedure AddTree(Tree: TBaseVirtualTree); class procedure RemoveTree(Tree: TBaseVirtualTree); - - property CurrentTree: TBaseVirtualTree read FCurrentTree; end; @@ -98,6 +96,7 @@ class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean); WorkerThread.Dispose(ACanBlock); end; end; + CheckSynchronize(); // Make sure code queued in the main thread by TBaseVirtualTree.ChangeTreeStatesAsync() get processed before the tree is being destroyed. issue #1001 end; //---------------------------------------------------------------------------------------------------------------------- @@ -180,7 +179,7 @@ procedure TWorkerThread.Execute(); EnterStates := [tsUseCache]; finally - FCurrentTree := nil; //Clear variable to prevent deadlock in CancelValidation. See #434 + FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); Queue(TBaseVirtualTreeCracker(lCurrentTree).UpdateEditBounds); end; From 6c10973508756a16e8b36ee040ea0d78a28913b7 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 1 Dec 2020 21:01:27 +0100 Subject: [PATCH 121/681] Fixed issue #1007: TCustomVirtualStringTree.InitializeTextProperties() doesn't honor option toPopupMode . --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index c02414123..f7c87b796 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -33589,7 +33589,7 @@ procedure TCustomVirtualStringTree.InitializeTextProperties(var PaintInfo: TVTPa if Node = FDropTargetNode then begin if ((FLastDropMode = dmOnNode) or (vsSelected in Node.States)) then - Canvas.Font.Color := FColors.GetSelectedNodeFontColor(Focused); + Canvas.Font.Color := FColors.GetSelectedNodeFontColor(Focused or (toPopupMode in FOptions.FPaintOptions)); end else if vsSelected in Node.States then From 2b05805fdb7a2f9af38265b9bb677015694209de Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 1 Dec 2020 21:12:40 +0100 Subject: [PATCH 122/681] Fixed issue #1002: Prevent ImageList notifications after destruction --- Source/VirtualTrees.pas | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index f7c87b796..99a904e0b 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -12181,6 +12181,11 @@ destructor TBaseVirtualTree.Destroy; FSelectedHotPlusBM.Free; FSelectedHotMinusBM.Free; + // Fixes issue #1002 + Images := nil; + StateImages := nil; + CustomCheckImages := nil; + inherited; end; From 38c6348f62c0021440949629a0a56626e5a9ed38 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 1 Dec 2020 23:10:36 +0100 Subject: [PATCH 123/681] Fix for issue #1000: Scale TotalHeight along with NodeHeight for non-initialized nodes. --- Source/VirtualTrees.pas | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 99a904e0b..f3ad8520c 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18453,7 +18453,10 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; if vsInitialized in Run.States then SetNodeHeight(Run, MulDiv(Run.NodeHeight, M, D)) else // prevent initialization of non-initialzed nodes + begin Run.NodeHeight := MulDiv(Run.NodeHeight, M, D); + Run.TotalHeight := MulDiv(Run.TotalHeight, M, D); // Fixes issue #1000 + end; Run := GetNextNoInit(Run); end; // while finally From f38b938f003d6c0f9b9ab59f9c32b4867cc327f6 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 07:48:32 +0100 Subject: [PATCH 124/681] Set toAutoSort to False for more speed. --- Demos/Advanced/SpeedDemo.dfm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Demos/Advanced/SpeedDemo.dfm b/Demos/Advanced/SpeedDemo.dfm index 2a545939f..419af70ac 100644 --- a/Demos/Advanced/SpeedDemo.dfm +++ b/Demos/Advanced/SpeedDemo.dfm @@ -1556,20 +1556,15 @@ object SpeedForm: TSpeedForm Colors.BorderColor = clWindowText Colors.HotColor = clBlack Header.AutoSizeIndex = -1 - Header.Font.Charset = DEFAULT_CHARSET - Header.Font.Color = clWindowText - Header.Font.Height = -11 - Header.Font.Name = 'Tahoma' - Header.Font.Style = [] Header.MainColumn = -1 Header.Options = [hoColumnResize, hoDrag, hoHotTrack] - HintAnimation = hatNone IncrementalSearch = isAll ParentBiDiMode = False ParentShowHint = False ScrollBarOptions.VerticalIncrement = 18 ShowHint = True TabOrder = 0 + TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScrollOnExpand, toAutoTristateTracking, toAutoDeleteMovedNodes, toAutoChangeScale] TreeOptions.MiscOptions = [toAcceptOLEDrop, toEditable, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning] TreeOptions.SelectionOptions = [toMultiSelect] OnChange = VST1Change From 296bc91211b75785021e6520f654dfc3e6abf5bc Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 08:08:57 +0100 Subject: [PATCH 125/681] Possible fix for issue #1000: Check HandleAllocated also in the queued code as the control hanlde may have been destryoed. --- Source/VirtualTrees.pas | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index f3ad8520c..11c667efe 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18480,11 +18480,10 @@ procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirt if (Self.HandleAllocated) then TThread.Synchronize(nil, procedure begin - // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) - if not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then + // issue #1000 // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) + if HandleAllocated and not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then DoStateChange(EnterStates, LeaveStates) end); - end; //---------------------------------------------------------------------------------------------------------------------- From fd9266d8d4a877e0c6156c6b640c78664580c9b4 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 08:10:17 +0100 Subject: [PATCH 126/681] Corrected issue number --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 11c667efe..173ca7263 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18480,7 +18480,7 @@ procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirt if (Self.HandleAllocated) then TThread.Synchronize(nil, procedure begin - // issue #1000 // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) + // issue #1001 // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) if HandleAllocated and not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then DoStateChange(EnterStates, LeaveStates) end); From 6e90a83895d5fadeba4fe14ba5c630147c8cfbd8 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 21:52:25 +0100 Subject: [PATCH 127/681] Reverted previous commit for #1001 --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 173ca7263..74aed7d26 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18480,8 +18480,8 @@ procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirt if (Self.HandleAllocated) then TThread.Synchronize(nil, procedure begin - // issue #1001 // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) - if HandleAllocated and not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then + // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) + if not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then DoStateChange(EnterStates, LeaveStates) end); end; From 2e01b4d4fb2820eb5d6f7978e807f61c53f614ed Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 3 Dec 2020 21:56:41 +0100 Subject: [PATCH 128/681] Chnages related to issue #1001: * TWorkerThread.ReleaseThreadReference() calls CheckSynchronize() to Make sure code queued in the main thread by TBaseVirtualTree.ChangeTreeStatesAsync() get processed before the tree is being destroyed. * Removed unused property CurrentTree from TWorkerThread. --- Source/VirtualTrees.WorkerThread.pas | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 0359da533..fd1336f74 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -28,8 +28,6 @@ TWorkerThread = class(TThread) class procedure AddTree(Tree: TBaseVirtualTree); class procedure RemoveTree(Tree: TBaseVirtualTree); - - property CurrentTree: TBaseVirtualTree read FCurrentTree; end; @@ -98,6 +96,7 @@ class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean); WorkerThread.Dispose(ACanBlock); end; end; + CheckSynchronize(); // Make sure code queued in the main thread by TBaseVirtualTree.ChangeTreeStatesAsync() get processed before the tree is being destroyed. issue #1001 end; //---------------------------------------------------------------------------------------------------------------------- @@ -180,7 +179,7 @@ procedure TWorkerThread.Execute(); EnterStates := [tsUseCache]; finally - FCurrentTree := nil; //Clear variable to prevent deadlock in CancelValidation. See #434 + FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); Queue(TBaseVirtualTreeCracker(lCurrentTree).UpdateEditBounds); end; From b18657ff4a8ed91e47aac1d3c22181ea0026243f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 5 Dec 2020 21:47:47 +0100 Subject: [PATCH 129/681] Added test case for issue #1001. Thanks @modersohn for creating this. --- Tests/Tests.dpr | 3 +- Tests/Tests.dproj | 705 ++++++++++++++++++++----- Tests/VTWorkerThreadIssue1001Tests.pas | 91 ++++ 3 files changed, 667 insertions(+), 132 deletions(-) create mode 100644 Tests/VTWorkerThreadIssue1001Tests.pas diff --git a/Tests/Tests.dpr b/Tests/Tests.dpr index 903b72772..28b47ba4a 100644 --- a/Tests/Tests.dpr +++ b/Tests/Tests.dpr @@ -12,7 +12,8 @@ uses DUnitX.Loggers.Xml.NUnit, DUnitX.TestFramework, VirtualTreeTests in 'VirtualTreeTests.pas', - VirtualStringTreeTests in 'VirtualStringTreeTests.pas'; + VirtualStringTreeTests in 'VirtualStringTreeTests.pas', + VTWorkerThreadIssue1001Tests in 'VTWorkerThreadIssue1001Tests.pas'; var runner : ITestRunner; diff --git a/Tests/Tests.dproj b/Tests/Tests.dproj index 8bab5281a..967b2bdc6 100644 --- a/Tests/Tests.dproj +++ b/Tests/Tests.dproj @@ -1,18 +1,38 @@  {D37CFA56-3B13-4C93-91F7-DDC227C20116} - 17.2 + 19.2 Tests.dpr True Debug Win32 1 Console - None + VCL true + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + true Base @@ -41,7 +61,7 @@ $(BDS)\bin\delphi_PROJECTICNS.icns - System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) $(DUnitX);..\Source;$(DCC_UnitSearchPath) 1031 CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= @@ -52,6 +72,60 @@ false false false + $(BDS)\bin\delphi_PROJECTICON.ico + + + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png + $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png + $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png + true + true + true + true + true + true + true + true + true + true + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png + $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png + $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png + $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png + $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) @@ -89,6 +163,7 @@ + Cfg_2 Base @@ -116,121 +191,191 @@ Embarcadero C++Builder Office XP Servers Package - - + + + + + + + Tests.exe true - + - true - - - - - true - - - - 1 - .dylib 0 - .bpl - + + + + classes 1 - .dylib - + + classes 1 - .dylib - + + + + res\xml + 1 + + + res\xml 1 - .dylib - - + + + library\lib\armeabi-v7a 1 - .dylib - - 0 - .dll;.bpl + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 - - + + + library\lib\armeabi-v7a 1 - + + + + library\lib\mips 1 - + + library\lib\mips 1 - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + + + library\lib\armeabi-v7a 1 - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + + library\lib\arm64-v8a 1 - + + + library\lib\armeabi-v7a + 1 + + + - res\drawable-normal + res\drawable + 1 + + + res\drawable 1 - + - library\lib\x86 + res\values + 1 + + + res\values 1 - - + + + res\values-v21 1 - + + res\values-v21 1 - + + + + res\values + 1 + + + res\values 1 - - + - library\lib\armeabi-v7a + res\drawable + 1 + + + res\drawable 1 - - + + + res\drawable-xxhdpi 1 - + + res\drawable-xxhdpi 1 - + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi 1 - + - res\drawable-xlarge + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi 1 @@ -239,43 +384,118 @@ res\drawable-xhdpi 1 + + res\drawable-xhdpi + 1 + - - + + + res\drawable-mdpi 1 - + + res\drawable-mdpi 1 - + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi 1 - + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + res\drawable-xxhdpi 1 + + res\drawable-xxhdpi + 1 + - + - library\lib\mips + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi 1 - + - res\drawable + res\drawable-small + 1 + + + res\drawable-small 1 - - + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge 1 + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + 1 + + 1 + 0 @@ -285,181 +505,404 @@ 1 .framework + + 1 + .framework + 0 - - - res\drawable-small + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl - - + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + 1 + .dylib + + + 1 + .dylib - Contents\MacOS 0 + .bpl - + - classes + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - res\drawable + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - Contents\Resources + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - library\lib\armeabi-v7a + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - 0 + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 - + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - library\lib\armeabi + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - res\drawable-large + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 1 - - - 0 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + - 0 + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 - - 0 + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 - - 0 + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 - 0 + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 - - 0 + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 - + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - - - res\drawable-ldpi + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset 1 - + - res\values + 1 + + 1 - + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + 1 1 - + + + + + + + Contents\Resources + 1 + + + Contents\Resources 1 - + - res\drawable-mdpi + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + 1 + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + - - - res\drawable-hdpi + + + library\lib\armeabi-v7a 1 - - + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets 1 - - + + + + + - + + False + False + False + False + False + False True False diff --git a/Tests/VTWorkerThreadIssue1001Tests.pas b/Tests/VTWorkerThreadIssue1001Tests.pas new file mode 100644 index 000000000..6f979e0e3 --- /dev/null +++ b/Tests/VTWorkerThreadIssue1001Tests.pas @@ -0,0 +1,91 @@ +unit VTWorkerThreadIssue1001Tests; + +interface + +uses + DUnitX.TestFramework, + Classes, + Vcl.Forms, + VirtualTrees; + +type + TTestBaseVirtualTree = class(TBaseVirtualTree) + public + property OnCompareNodes; + end; + + [TestFixture] + TVTWorkerThreadIssue1001Tests = class + strict private + fTree: TTestBaseVirtualTree; + fForm: TForm; + procedure TreeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; + Column: TColumnIndex; var Result: Integer); + public + [Setup] + procedure Setup; + [TearDown] + procedure TearDown; + + /// Test for CheckSynchronize when tree is destroyed + /// repeated 50 times because AVs are not realiable + [Test, RepeatTestAttribute(100)] + procedure TestDestroyWhileWorkerThreadBusy; + end; + +implementation + +uses + VirtualTrees.WorkerThread, + SysUtils; + +procedure TVTWorkerThreadIssue1001Tests.Setup; +begin + TThread.Synchronize(nil, procedure + begin + fForm := TForm.Create(nil); + fTree := TTestBaseVirtualTree.Create(fForm); + fTree.TreeOptions.AutoOptions:= fTree.TreeOptions.AutoOptions - [toAutoSort]; + fTree.OnCompareNodes:= TreeCompareNodes; + end); +end; + +procedure TVTWorkerThreadIssue1001Tests.TearDown; +begin + TThread.Synchronize(nil, procedure + begin + FreeAndNil(fForm); + end); +end; + +procedure TVTWorkerThreadIssue1001Tests.TestDestroyWhileWorkerThreadBusy; +begin + TThread.Synchronize(nil, procedure + begin + fTree.BeginUpdate; + try + fTree.SetChildCount(fTree.RootNode, 10000); + Assert.AreEqual(fTree.RootNode.ChildCount + 1, fTree.RootNode.TotalCount, 'TotalCount <> ChildCount + 1'); + //fTree.SortTree(-1, sdAscending, false); + finally + fTree.EndUpdate; + end; + FreeAndNil(fTree); + FreeAndNil(fForm); + end); +end; + +procedure TVTWorkerThreadIssue1001Tests.TreeCompareNodes( + Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; + var Result: Integer); +begin + if Random(10) > 5 then + Result:= 1 else + Result:= -1; +end; + +initialization + Randomize; + TDUnitX.RegisterTestFixture(TVTWorkerThreadIssue1001Tests); + +end. From 139d8fd7f6bf5de93abeab47c6999eab0ce9bf3b Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 6 Dec 2020 09:49:47 +0100 Subject: [PATCH 130/681] Fix for issue #1000: Adjust FRoot.TotalHeight for non-inizialized nodes to fix scrollbar total height. --- Source/VirtualTrees.pas | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 4a9bb2310..c66b055af 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18420,6 +18420,7 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; var Flags: TScalingFlags; Run: PVirtualNode; + lNewNodeTotalHeight: Cardinal; begin if (toAutoChangeScale in FOptions.FAutoOptions) then begin @@ -18455,7 +18456,10 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; else // prevent initialization of non-initialzed nodes begin Run.NodeHeight := MulDiv(Run.NodeHeight, M, D); - Run.TotalHeight := MulDiv(Run.TotalHeight, M, D); // Fixes issue #1000 + // The next three lines fix issue #1000 + lNewNodeTotalHeight := MulDiv(Run.TotalHeight, M, D); + FRoot.TotalHeight := FRoot.TotalHeight + lNewNodeTotalHeight - Run.TotalHeight; + Run.TotalHeight := lNewNodeTotalHeight; end; Run := GetNextNoInit(Run); end; // while @@ -18477,7 +18481,7 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirtualTreeStates); begin //TODO: If this works reliable, move to TWorkerThread - if (Self.HandleAllocated) then + if not (csDestroying in ComponentState) then TThread.Synchronize(nil, procedure begin // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) From 31a9b5c58395ef35e89833964e4b930c49965d81 Mon Sep 17 00:00:00 2001 From: Daniel Trierweiler Date: Sun, 6 Dec 2020 15:28:24 +0100 Subject: [PATCH 131/681] Fix for #1004: Added protected functions to calculate the correct StyleServices for each context, so they will automatically be used over the globally resolved StyleServices from Vcl.Themes. All of these methods accept a control as optional parameter to faciliate the transition of code to Delphi >= 10.4. Using a fallback to the parameter-less StyleServices for Delphi <= 10.3 --- Source/VirtualTrees.pas | 83 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index c66b055af..1547a6aab 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -637,6 +637,8 @@ TCustomVirtualTreeOptions = class(TPersistent) procedure SetPaintOptions(const Value: TVTPaintOptions); procedure SetSelectionOptions(const Value: TVTSelectionOptions); protected + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; public constructor Create(AOwner: TBaseVirtualTree); virtual; procedure AssignTo(Dest: TPersistent); override; @@ -859,6 +861,8 @@ TVirtualTreeHintWindow = class(THintWindow) strict protected procedure CreateParams(var Params: TCreateParams); override; procedure Paint; override; + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; public function CalcHintRect(MaxWidth: TDimension; const AHint: string; AData: Pointer): TRect; override; function IsHintMsg(var Msg: TMsg): Boolean; override; @@ -1158,6 +1162,8 @@ TVirtualTreeColumns = class(TCollection) property HoverIndex: TColumnIndex read FHoverIndex; property DownIndex: TColumnIndex read FDownIndex; property CheckBoxHit: Boolean read FCheckBoxHit; + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; public constructor Create(AOwner: TVTHeader); virtual; destructor Destroy; override; @@ -1669,6 +1675,8 @@ TVTColors = class(TPersistent) property BackGroundColor: TColor read GetBackgroundColor; property HeaderFontColor: TColor read GetHeaderFontColor; property NodeFontColor: TColor read GetNodeFontColor; + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; published property BorderColor: TColor index cBorderColor read GetColor write SetColor default clBtnFace; property DisabledColor: TColor index cDisabledColor read GetColor write SetColor default clBtnShadow; @@ -2784,7 +2792,8 @@ TBaseVirtualTree = class(TCustomControl) procedure VclStyleChanged; virtual; property VclStyleEnabled: Boolean read GetVclStyleEnabled; property TotalInternalDataSize: Cardinal read FTotalInternalDataSize; - + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify; property AnimationDuration: Cardinal read FAnimationDuration write SetAnimationDuration default 200; property AutoExpandDelay: Cardinal read FAutoExpandDelay write FAutoExpandDelay default 1000; @@ -4280,6 +4289,14 @@ function CreateSystemImageSet(pControl: TWinControl): TImageList; //--------------------------------------------------------------------------- + {$if CompilerVersion >= 34} + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices: TCustomStyleServices; + begin + Result := Vcl.Themes.StyleServices(pControl); + end; + {$ifend} + procedure AddSystemImage(IL: TImageList; Index: Integer); const States: array [0..19] of Integer = ( @@ -4684,6 +4701,11 @@ procedure TCustomVirtualTreeOptions.SetSelectionOptions(const Value: TVTSelectio end; end; +function TCustomVirtualTreeOptions.StyleServices(AControl: TControl): TCustomStyleServices; +begin + Exit(Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(FOwner){$ifend}); +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TCustomVirtualTreeOptions.AssignTo(Dest: TPersistent); @@ -5506,16 +5528,16 @@ procedure TVirtualTreeHintWindow.Paint(); if Tree.VclStyleEnabled then begin InflateRect(R, -1, -1); // Fixes missing border when VCL styles are used - LDetails := StyleServices.GetElementDetails(thHintNormal); - if StyleServices.GetElementColor(LDetails, ecGradientColor1, LColor) and (LColor <> clNone) then + LDetails := StyleServices(Tree).GetElementDetails(thHintNormal); + if StyleServices(Tree).GetElementColor(LDetails, ecGradientColor1, LColor) and (LColor <> clNone) then LGradientStart := LColor else LGradientStart := clInfoBk; - if StyleServices.GetElementColor(LDetails, ecGradientColor2, LColor) and (LColor <> clNone) then + if StyleServices(Tree).GetElementColor(LDetails, ecGradientColor2, LColor) and (LColor <> clNone) then LGradientEnd := LColor else LGradientEnd := clInfoBk; - if StyleServices.GetElementColor(LDetails, ecTextColor, LColor) and (LColor <> clNone) then + if StyleServices(Tree).GetElementColor(LDetails, ecTextColor, LColor) and (LColor <> clNone) then Font.Color := LColor else Font.Color := Screen.HintFont.Color; @@ -5527,23 +5549,23 @@ procedure TVirtualTreeHintWindow.Paint(); Font.Color := clInfoText; Pen.Color := clBlack; Brush.Color := clInfoBk; - if IsWinVistaOrAbove and StyleServices.Enabled and ((toThemeAware in Tree.TreeOptions.PaintOptions) or + if IsWinVistaOrAbove and StyleServices(Tree).Enabled and ((toThemeAware in Tree.TreeOptions.PaintOptions) or (toUseExplorerTheme in Tree.TreeOptions.PaintOptions)) then begin if toUseExplorerTheme in Tree.TreeOptions.PaintOptions then // ToolTip style - StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) + StyleServices(Tree).DrawElement(Canvas.Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) else begin // Hint style LClipRect := R; InflateRect(R, 4, 4); - StyleServices.DrawElement(Handle, StyleServices.GetElementDetails(tttStandardNormal), R, @LClipRect{$IF CompilerVersion >= 34}, FCurrentPPI{$IFEND}); + StyleServices(Tree).DrawElement(Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R, @LClipRect{$IF CompilerVersion >= 34}, FCurrentPPI{$IFEND}); R := LClipRect; - StyleServices.DrawEdge(Handle, StyleServices.GetElementDetails(twWindowRoot), R, [eeRaisedOuter], [efRect]); + StyleServices(Tree).DrawEdge(Handle, StyleServices(Tree).GetElementDetails(twWindowRoot), R, [eeRaisedOuter], [efRect]); end; end else if Tree.VclStyleEnabled then - StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) + StyleServices(Tree).DrawElement(Canvas.Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) else Rectangle(R); end; @@ -5560,6 +5582,11 @@ procedure TVirtualTreeHintWindow.Paint(); end; end; +function TVirtualTreeHintWindow.StyleServices(AControl: TControl): TCustomStyleServices; +begin + Result := Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(AControl){$ifend}; +end; + //---------------------------------------------------------------------------------------------------------------------- function TVirtualTreeHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect; @@ -7821,6 +7848,18 @@ procedure TVirtualTreeColumns.SetItem(Index: TColumnIndex; Value: TVirtualTreeCo inherited SetItem(Index, Value); end; +function TVirtualTreeColumns.StyleServices(AControl: TControl): TCustomStyleServices; +begin +{$if CompilerVersion >= 34} + if AControl = nil then + Result := Vcl.Themes.StyleServices(FHeader.Treeview) + else + Result := Vcl.Themes.StyleServices(AControl); +{$else} + Result := Vcl.Themes.StyleServices; +{$ifend} +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TVirtualTreeColumns.AdjustAutoSize(CurrentIndex: TColumnIndex; Force: Boolean = False); @@ -11966,6 +12005,18 @@ procedure TVTColors.SetColor(const Index: TVTColorEnum; const Value: TColor); end; end; +function TVTColors.StyleServices(AControl: TControl): TCustomStyleServices; +begin +{$if CompilerVersion >= 34} + if AControl = nil then + Result := Vcl.Themes.StyleServices(fOwner) + else + Result := Vcl.Themes.StyleServices(AControl); +{$else} + Result := Vcl.Themes.StyleServices; +{$ifend} +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TVTColors.Assign(Source: TPersistent); @@ -25169,6 +25220,18 @@ procedure TBaseVirtualTree.StructureChange(Node: PVirtualNode; Reason: TChangeRe end; end; +function TBaseVirtualTree.StyleServices(AControl: TControl): TCustomStyleServices; +begin +{$if CompilerVersion >= 34} + if AControl = nil then + Result := Vcl.Themes.StyleServices(Self) + else + Result := Vcl.Themes.StyleServices(AControl); +{$else} + Result := Vcl.Themes.StyleServices; +{$ifend} +end; + //---------------------------------------------------------------------------------------------------------------------- function TBaseVirtualTree.SuggestDropEffect(Source: TObject; Shift: TShiftState; Pt: TPoint; From 8adce24ed6abb9b23c9bfd7e66d1412f2859612a Mon Sep 17 00:00:00 2001 From: Sebastian Modersohn Date: Sun, 6 Dec 2020 13:50:58 +0100 Subject: [PATCH 132/681] Improved test-case for #1001 * since we are in a console application, the test needs to call CheckSynchronize itself, which would otherwise be done via TApplication * this ensures that the running thread can continue it's work and cause the AV * if the original fix for #1001 is removed (CheckSynchronize in TWorkerThread.ReleaseThreadReference) this test will crash, but it will not make the test fail! * this happens because the exceptions are actually raised in the thread, and the thread is terminated via FreeOnTerminate = True * therefore the crashes will only be visible with the debugger * added Win64 to the Tests project * With Win32 there are actually other AVs when run from the debugger; * seen with Delphi 10.1 Berlin and 10.4 Sydney, but reasons unclear * these will make the tests fail * also simplified the test-case, removing SortTree and TeeCompareNodes handler; both not required to see the code crash --- Tests/Tests.dproj | 14 ++++++++--- Tests/VTWorkerThreadIssue1001Tests.pas | 32 +++++++++----------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Tests/Tests.dproj b/Tests/Tests.dproj index 967b2bdc6..681d49fc6 100644 --- a/Tests/Tests.dproj +++ b/Tests/Tests.dproj @@ -5,8 +5,8 @@ Tests.dpr True Debug - Win32 - 1 + Win64 + 3 Console VCL @@ -54,6 +54,12 @@ true true + + true + Cfg_1 + true + true + true Base @@ -73,6 +79,8 @@ false false $(BDS)\bin\delphi_PROJECTICON.ico + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png @@ -904,7 +912,7 @@ False False True - False + True 12 diff --git a/Tests/VTWorkerThreadIssue1001Tests.pas b/Tests/VTWorkerThreadIssue1001Tests.pas index 6f979e0e3..40d19abcb 100644 --- a/Tests/VTWorkerThreadIssue1001Tests.pas +++ b/Tests/VTWorkerThreadIssue1001Tests.pas @@ -19,8 +19,6 @@ TVTWorkerThreadIssue1001Tests = class strict private fTree: TTestBaseVirtualTree; fForm: TForm; - procedure TreeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; - Column: TColumnIndex; var Result: Integer); public [Setup] procedure Setup; @@ -46,7 +44,6 @@ procedure TVTWorkerThreadIssue1001Tests.Setup; fForm := TForm.Create(nil); fTree := TTestBaseVirtualTree.Create(fForm); fTree.TreeOptions.AutoOptions:= fTree.TreeOptions.AutoOptions - [toAutoSort]; - fTree.OnCompareNodes:= TreeCompareNodes; end); end; @@ -62,30 +59,23 @@ procedure TVTWorkerThreadIssue1001Tests.TestDestroyWhileWorkerThreadBusy; begin TThread.Synchronize(nil, procedure begin - fTree.BeginUpdate; - try - fTree.SetChildCount(fTree.RootNode, 10000); - Assert.AreEqual(fTree.RootNode.ChildCount + 1, fTree.RootNode.TotalCount, 'TotalCount <> ChildCount + 1'); - //fTree.SortTree(-1, sdAscending, false); - finally - fTree.EndUpdate; - end; + fTree.SetChildCount(fTree.RootNode, 10000); + Assert.AreEqual(fTree.RootNode.ChildCount + 1, fTree.RootNode.TotalCount, 'TotalCount <> ChildCount + 1'); FreeAndNil(fTree); FreeAndNil(fForm); - end); -end; -procedure TVTWorkerThreadIssue1001Tests.TreeCompareNodes( - Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; - var Result: Integer); -begin - if Random(10) > 5 then - Result:= 1 else - Result:= -1; + //Now that the tree is destroyed, we have to ensure that the code called + //from WorkerThread.Execute via Synchronize is executed and causes the AV. + //In a real-world GUI Application, CheckSynchronize is called often, but + //here in a Console application we have to do this ourselves. + //Unfortunately the AV will not make the test fail, since it is raised + //in WorkerThread, which has FreeOnTerminate = True; therefore the AVs + //can only be seen with the debugger. + CheckSynchronize; + end); end; initialization - Randomize; TDUnitX.RegisterTestFixture(TVTWorkerThreadIssue1001Tests); end. From 5aaba1ea1c2f247de6f31dfe118c2b61d9945eb2 Mon Sep 17 00:00:00 2001 From: Sebastian Modersohn Date: Sun, 6 Dec 2020 15:57:45 +0100 Subject: [PATCH 133/681] Fix to ensure WaitForValidationTermination actually waits; #1001 * this fix ensures that TWorkerThread.WaitForValidationTermination, called from RemoveTree, waits until .Execute is finished with processing FCurrentTree * completely eliminated lCurrentTree in Execute - now FCurrentTree is always used to ensure that WaitForValidationTermination can correctly determine if it's Tree parameter is currently processed, no matter how early or late in the processing it is * removed the condition for CheckSynchronize in WaitForValidationTermination - because .Execute synchronizes several code pieces and to ensure that while WaitForValidationTermination waits, .Execute actually has a chance to finish without deadlocking * removed the CheckSynchronize in ReleaseThreadReference because there is no longer a need for that, this late in the process * .Execute no longer calls ChangeTreeStatesAsync and does not Queue UpdateEditBounds, but calls everything in Synchronize directly * the Queue could also cause AVs, if it is eventually executed after FCurrentTree had already been destroyed --- Source/VirtualTrees.WorkerThread.pas | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index fd1336f74..37e8f074f 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -96,7 +96,6 @@ class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean); WorkerThread.Dispose(ACanBlock); end; end; - CheckSynchronize(); // Make sure code queued in the main thread by TBaseVirtualTree.ChangeTreeStatesAsync() get processed before the tree is being destroyed. issue #1001 end; //---------------------------------------------------------------------------------------------------------------------- @@ -127,8 +126,7 @@ procedure TWorkerThread.WaitForValidationTermination(Tree: TBaseVirtualTree); while FCurrentTree = Tree do begin Sleep(1); - if (toVariableNodeHeight in TBaseVirtualTreeCracker(Tree).TreeOptions.MiscOptions) then - CheckSynchronize(); // We need to call CheckSynchronize here because we are using TThread.Synchronize in TBaseVirtualTree.MeasureItemHeight() + CheckSynchronize(); //since Execute uses Synchronize, it must have a chance to finish; #1000 end; end; @@ -140,8 +138,6 @@ procedure TWorkerThread.Execute(); var EnterStates: TVirtualTreeStates; - lCurrentTree: TBaseVirtualTree; - begin TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do @@ -155,7 +151,7 @@ procedure TWorkerThread.Execute(); try if Count > 0 then begin - lCurrentTree := Items[0]; + FCurrentTree := Items[0]; // Remove this tree from waiter list. Delete(0); // If there is yet another tree to work on then set the work event to keep looping. @@ -163,25 +159,30 @@ procedure TWorkerThread.Execute(); SetEvent(WorkEvent); end else - lCurrentTree := nil; + FCurrentTree := nil; finally FWaiterList.UnlockList; end; // Something to do? - if Assigned(lCurrentTree) then + if Assigned(FCurrentTree) then begin try - TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); - FCurrentTree := lCurrentTree; + TThread.Synchronize(nil, procedure + begin + TBaseVirtualTreeCracker(FCurrentTree).DoStateChange([tsValidating], [tsUseCache, tsValidationNeeded]); + end); EnterStates := []; if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then EnterStates := [tsUseCache]; finally - FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() - TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); - Queue(TBaseVirtualTreeCracker(lCurrentTree).UpdateEditBounds); + TThread.Synchronize(nil, procedure + begin + TBaseVirtualTreeCracker(FCurrentTree).DoStateChange(EnterStates, [tsValidating, tsStopValidation]); + TBaseVirtualTreeCracker(FCurrentTree).UpdateEditBounds; + end); + FCurrentTree := nil; end; end; end;//while From b4cc98381b3575b315171dfb3586da2c0efbbdb0 Mon Sep 17 00:00:00 2001 From: Daniel Trierweiler Date: Sun, 6 Dec 2020 15:28:24 +0100 Subject: [PATCH 134/681] Fix for #1004: Added protected functions to calculate the correct StyleServices for each context, so they will automatically be used over the globally resolved StyleServices from Vcl.Themes. All of these methods accept a control as optional parameter to faciliate the transition of code to Delphi >= 10.4. Using a fallback to the parameter-less StyleServices for Delphi <= 10.3 --- Source/VirtualTrees.pas | 83 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ff7c1721d..24bdf78a3 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -637,6 +637,8 @@ TCustomVirtualTreeOptions = class(TPersistent) procedure SetPaintOptions(const Value: TVTPaintOptions); procedure SetSelectionOptions(const Value: TVTSelectionOptions); protected + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; public constructor Create(AOwner: TBaseVirtualTree); virtual; procedure AssignTo(Dest: TPersistent); override; @@ -859,6 +861,8 @@ TVirtualTreeHintWindow = class(THintWindow) strict protected procedure CreateParams(var Params: TCreateParams); override; procedure Paint; override; + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; public function CalcHintRect(MaxWidth: TDimension; const AHint: string; AData: Pointer): TRect; override; function IsHintMsg(var Msg: TMsg): Boolean; override; @@ -1158,6 +1162,8 @@ TVirtualTreeColumns = class(TCollection) property HoverIndex: TColumnIndex read FHoverIndex; property DownIndex: TColumnIndex read FDownIndex; property CheckBoxHit: Boolean read FCheckBoxHit; + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; public constructor Create(AOwner: TVTHeader); virtual; destructor Destroy; override; @@ -1669,6 +1675,8 @@ TVTColors = class(TPersistent) property BackGroundColor: TColor read GetBackgroundColor; property HeaderFontColor: TColor read GetHeaderFontColor; property NodeFontColor: TColor read GetNodeFontColor; + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; published property BorderColor: TColor index cBorderColor read GetColor write SetColor default clBtnFace; property DisabledColor: TColor index cDisabledColor read GetColor write SetColor default clBtnShadow; @@ -2784,7 +2792,8 @@ TBaseVirtualTree = class(TCustomControl) procedure VclStyleChanged; virtual; property VclStyleEnabled: Boolean read GetVclStyleEnabled; property TotalInternalDataSize: Cardinal read FTotalInternalDataSize; - + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl: TControl = nil): TCustomStyleServices; property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify; property AnimationDuration: Cardinal read FAnimationDuration write SetAnimationDuration default 200; property AutoExpandDelay: Cardinal read FAutoExpandDelay write FAutoExpandDelay default 1000; @@ -4280,6 +4289,14 @@ function CreateSystemImageSet(pControl: TWinControl): TImageList; //--------------------------------------------------------------------------- + {$if CompilerVersion >= 34} + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices: TCustomStyleServices; + begin + Result := Vcl.Themes.StyleServices(pControl); + end; + {$ifend} + procedure AddSystemImage(IL: TImageList; Index: Integer); const States: array [0..19] of Integer = ( @@ -4684,6 +4701,11 @@ procedure TCustomVirtualTreeOptions.SetSelectionOptions(const Value: TVTSelectio end; end; +function TCustomVirtualTreeOptions.StyleServices(AControl: TControl): TCustomStyleServices; +begin + Exit(Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(FOwner){$ifend}); +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TCustomVirtualTreeOptions.AssignTo(Dest: TPersistent); @@ -5506,16 +5528,16 @@ procedure TVirtualTreeHintWindow.Paint(); if Tree.VclStyleEnabled then begin InflateRect(R, -1, -1); // Fixes missing border when VCL styles are used - LDetails := StyleServices.GetElementDetails(thHintNormal); - if StyleServices.GetElementColor(LDetails, ecGradientColor1, LColor) and (LColor <> clNone) then + LDetails := StyleServices(Tree).GetElementDetails(thHintNormal); + if StyleServices(Tree).GetElementColor(LDetails, ecGradientColor1, LColor) and (LColor <> clNone) then LGradientStart := LColor else LGradientStart := clInfoBk; - if StyleServices.GetElementColor(LDetails, ecGradientColor2, LColor) and (LColor <> clNone) then + if StyleServices(Tree).GetElementColor(LDetails, ecGradientColor2, LColor) and (LColor <> clNone) then LGradientEnd := LColor else LGradientEnd := clInfoBk; - if StyleServices.GetElementColor(LDetails, ecTextColor, LColor) and (LColor <> clNone) then + if StyleServices(Tree).GetElementColor(LDetails, ecTextColor, LColor) and (LColor <> clNone) then Font.Color := LColor else Font.Color := Screen.HintFont.Color; @@ -5527,23 +5549,23 @@ procedure TVirtualTreeHintWindow.Paint(); Font.Color := clInfoText; Pen.Color := clBlack; Brush.Color := clInfoBk; - if IsWinVistaOrAbove and StyleServices.Enabled and ((toThemeAware in Tree.TreeOptions.PaintOptions) or + if IsWinVistaOrAbove and StyleServices(Tree).Enabled and ((toThemeAware in Tree.TreeOptions.PaintOptions) or (toUseExplorerTheme in Tree.TreeOptions.PaintOptions)) then begin if toUseExplorerTheme in Tree.TreeOptions.PaintOptions then // ToolTip style - StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) + StyleServices(Tree).DrawElement(Canvas.Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) else begin // Hint style LClipRect := R; InflateRect(R, 4, 4); - StyleServices.DrawElement(Handle, StyleServices.GetElementDetails(tttStandardNormal), R, @LClipRect{$IF CompilerVersion >= 34}, FCurrentPPI{$IFEND}); + StyleServices(Tree).DrawElement(Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R, @LClipRect{$IF CompilerVersion >= 34}, FCurrentPPI{$IFEND}); R := LClipRect; - StyleServices.DrawEdge(Handle, StyleServices.GetElementDetails(twWindowRoot), R, [eeRaisedOuter], [efRect]); + StyleServices(Tree).DrawEdge(Handle, StyleServices(Tree).GetElementDetails(twWindowRoot), R, [eeRaisedOuter], [efRect]); end; end else if Tree.VclStyleEnabled then - StyleServices.DrawElement(Canvas.Handle, StyleServices.GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) + StyleServices(Tree).DrawElement(Canvas.Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) else Rectangle(R); end; @@ -5560,6 +5582,11 @@ procedure TVirtualTreeHintWindow.Paint(); end; end; +function TVirtualTreeHintWindow.StyleServices(AControl: TControl): TCustomStyleServices; +begin + Result := Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(AControl){$ifend}; +end; + //---------------------------------------------------------------------------------------------------------------------- function TVirtualTreeHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect; @@ -7821,6 +7848,18 @@ procedure TVirtualTreeColumns.SetItem(Index: TColumnIndex; Value: TVirtualTreeCo inherited SetItem(Index, Value); end; +function TVirtualTreeColumns.StyleServices(AControl: TControl): TCustomStyleServices; +begin +{$if CompilerVersion >= 34} + if AControl = nil then + Result := Vcl.Themes.StyleServices(FHeader.Treeview) + else + Result := Vcl.Themes.StyleServices(AControl); +{$else} + Result := Vcl.Themes.StyleServices; +{$ifend} +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TVirtualTreeColumns.AdjustAutoSize(CurrentIndex: TColumnIndex; Force: Boolean = False); @@ -11966,6 +12005,18 @@ procedure TVTColors.SetColor(const Index: TVTColorEnum; const Value: TColor); end; end; +function TVTColors.StyleServices(AControl: TControl): TCustomStyleServices; +begin +{$if CompilerVersion >= 34} + if AControl = nil then + Result := Vcl.Themes.StyleServices(fOwner) + else + Result := Vcl.Themes.StyleServices(AControl); +{$else} + Result := Vcl.Themes.StyleServices; +{$ifend} +end; + //---------------------------------------------------------------------------------------------------------------------- procedure TVTColors.Assign(Source: TPersistent); @@ -25169,6 +25220,18 @@ procedure TBaseVirtualTree.StructureChange(Node: PVirtualNode; Reason: TChangeRe end; end; +function TBaseVirtualTree.StyleServices(AControl: TControl): TCustomStyleServices; +begin +{$if CompilerVersion >= 34} + if AControl = nil then + Result := Vcl.Themes.StyleServices(Self) + else + Result := Vcl.Themes.StyleServices(AControl); +{$else} + Result := Vcl.Themes.StyleServices; +{$ifend} +end; + //---------------------------------------------------------------------------------------------------------------------- function TBaseVirtualTree.SuggestDropEffect(Source: TObject; Shift: TShiftState; Pt: TPoint; From 177b0ceff908076c90a898d5e7dc764f2fe7cfbd Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 6 Dec 2020 20:56:35 +0100 Subject: [PATCH 135/681] Added full unit scope names to ensure compiling without unit aliases. --- Tests/VirtualTreeTests.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/VirtualTreeTests.pas b/Tests/VirtualTreeTests.pas index 5a4d84be5..19e8c1269 100644 --- a/Tests/VirtualTreeTests.pas +++ b/Tests/VirtualTreeTests.pas @@ -4,7 +4,7 @@ interface uses DUnitX.TestFramework, - Windows, + WinApi.Windows, VirtualTrees, VirtualTrees.Utils, Vcl.Graphics; From 0470fc888cd2231fbd944d790b38832fa79588d8 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 6 Dec 2020 21:01:41 +0100 Subject: [PATCH 136/681] Switched to UTF-8. Issue #1006 --- Source/VirtualTrees.WorkerThread.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 37e8f074f..e62562c3b 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.WorkerThread; +unit VirtualTrees.WorkerThread; interface From 8632d8428cb3029bb16bff5590f45269996cf23f Mon Sep 17 00:00:00 2001 From: Daniel Trierweiler Date: Mon, 7 Dec 2020 13:19:53 +0100 Subject: [PATCH 137/681] #1004: Publish the StyleName property, so it can be used with the DFM. --- Source/VirtualTrees.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 24bdf78a3..5b254864a 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -3595,6 +3595,7 @@ TVirtualStringTree = class(TCustomVirtualStringTree) property ShowHint; property StateImages; property StyleElements; + {$if CompilerVersion >= 34}property StyleName;{$ifend} property TabOrder; property TabStop default True; property TextMargin; From 1264ba4b654e2fb0416850e258ebcd74e2153150 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 13:49:36 +0100 Subject: [PATCH 138/681] Revert "Merge pull request #1005 from modersohn/master" This reverts commit 77ad8184807323ba662cd1c2a3a9b1e85046d495, reversing changes made to 31a9b5c58395ef35e89833964e4b930c49965d81. --- Source/VirtualTrees.WorkerThread.pas | 27 +++++++++++----------- Source/VirtualTrees.pas | 8 +++---- Tests/Tests.dproj | 14 +++-------- Tests/VTWorkerThreadIssue1001Tests.pas | 32 +++++++++++++++++--------- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index e62562c3b..740dfb3c2 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -96,6 +96,7 @@ class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean); WorkerThread.Dispose(ACanBlock); end; end; + CheckSynchronize(); // Make sure code queued in the main thread by TBaseVirtualTree.ChangeTreeStatesAsync() get processed before the tree is being destroyed. issue #1001 end; //---------------------------------------------------------------------------------------------------------------------- @@ -126,7 +127,8 @@ procedure TWorkerThread.WaitForValidationTermination(Tree: TBaseVirtualTree); while FCurrentTree = Tree do begin Sleep(1); - CheckSynchronize(); //since Execute uses Synchronize, it must have a chance to finish; #1000 + if (toVariableNodeHeight in TBaseVirtualTreeCracker(Tree).TreeOptions.MiscOptions) then + CheckSynchronize(); // We need to call CheckSynchronize here because we are using TThread.Synchronize in TBaseVirtualTree.MeasureItemHeight() end; end; @@ -138,6 +140,8 @@ procedure TWorkerThread.Execute(); var EnterStates: TVirtualTreeStates; + lCurrentTree: TBaseVirtualTree; + begin TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do @@ -151,7 +155,7 @@ procedure TWorkerThread.Execute(); try if Count > 0 then begin - FCurrentTree := Items[0]; + lCurrentTree := Items[0]; // Remove this tree from waiter list. Delete(0); // If there is yet another tree to work on then set the work event to keep looping. @@ -159,30 +163,25 @@ procedure TWorkerThread.Execute(); SetEvent(WorkEvent); end else - FCurrentTree := nil; + lCurrentTree := nil; finally FWaiterList.UnlockList; end; // Something to do? - if Assigned(FCurrentTree) then + if Assigned(lCurrentTree) then begin try - TThread.Synchronize(nil, procedure - begin - TBaseVirtualTreeCracker(FCurrentTree).DoStateChange([tsValidating], [tsUseCache, tsValidationNeeded]); - end); + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); + FCurrentTree := lCurrentTree; EnterStates := []; if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then EnterStates := [tsUseCache]; finally - TThread.Synchronize(nil, procedure - begin - TBaseVirtualTreeCracker(FCurrentTree).DoStateChange(EnterStates, [tsValidating, tsStopValidation]); - TBaseVirtualTreeCracker(FCurrentTree).UpdateEditBounds; - end); - FCurrentTree := nil; + FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); + Queue(TBaseVirtualTreeCracker(lCurrentTree).UpdateEditBounds); end; end; end;//while diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 24bdf78a3..1547a6aab 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -4058,7 +4058,7 @@ implementation // Do not modify the copyright in any way! Usage of this unit is prohibited without the copyright notice // in the compiled binary file. - Copyright: string = 'Virtual Treeview � 1999, 2010, 2016 Mike Lischke, Joachim Marder'; + Copyright: string = 'Virtual Treeview © 1999, 2010, 2016 Mike Lischke, Joachim Marder'; var StandardOLEFormat: TFormatEtc = ( @@ -14124,7 +14124,7 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); FillBitmap(FMinusBM); FillBitmap(FHotMinusBM); FillBitmap(FSelectedHotMinusBM); - // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style schei�e aus + // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style scheiße aus // Because the self-drawn bitmaps view Vcl Style shit if Theme = 0 then begin @@ -34565,7 +34565,7 @@ function TCustomVirtualStringTree.ContentToHTML(Source: TVSTTextSourceType; cons // Renders the current tree content (depending on Source) as HTML text encoded in UTF-8. // If Caption is not empty then it is used to create and fill the header for the table built here. -// Based on ideas and code from Frank van den Bergh and Andreas H�rstemeier. +// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. begin Result := VirtualTrees.Export.ContentToHTML(Self, Source, Caption); @@ -34639,7 +34639,7 @@ procedure TCustomVirtualStringTree.RemoveFromSelection(Node: PVirtualNode); function TCustomVirtualStringTree.ContentToRTF(Source: TVSTTextSourceType): RawByteString; // Renders the current tree content (depending on Source) as RTF (rich text). -// Based on ideas and code from Frank van den Bergh and Andreas H�rstemeier. +// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. begin Result := VirtualTrees.Export.ContentToRTF(Self, Source); diff --git a/Tests/Tests.dproj b/Tests/Tests.dproj index 681d49fc6..967b2bdc6 100644 --- a/Tests/Tests.dproj +++ b/Tests/Tests.dproj @@ -5,8 +5,8 @@ Tests.dpr True Debug - Win64 - 3 + Win32 + 1 Console VCL @@ -54,12 +54,6 @@ true true - - true - Cfg_1 - true - true - true Base @@ -79,8 +73,6 @@ false false $(BDS)\bin\delphi_PROJECTICON.ico - .\$(Platform)\$(Config) - .\$(Platform)\$(Config) $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png @@ -912,7 +904,7 @@ False False True - True + False 12 diff --git a/Tests/VTWorkerThreadIssue1001Tests.pas b/Tests/VTWorkerThreadIssue1001Tests.pas index 40d19abcb..6f979e0e3 100644 --- a/Tests/VTWorkerThreadIssue1001Tests.pas +++ b/Tests/VTWorkerThreadIssue1001Tests.pas @@ -19,6 +19,8 @@ TVTWorkerThreadIssue1001Tests = class strict private fTree: TTestBaseVirtualTree; fForm: TForm; + procedure TreeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; + Column: TColumnIndex; var Result: Integer); public [Setup] procedure Setup; @@ -44,6 +46,7 @@ procedure TVTWorkerThreadIssue1001Tests.Setup; fForm := TForm.Create(nil); fTree := TTestBaseVirtualTree.Create(fForm); fTree.TreeOptions.AutoOptions:= fTree.TreeOptions.AutoOptions - [toAutoSort]; + fTree.OnCompareNodes:= TreeCompareNodes; end); end; @@ -59,23 +62,30 @@ procedure TVTWorkerThreadIssue1001Tests.TestDestroyWhileWorkerThreadBusy; begin TThread.Synchronize(nil, procedure begin - fTree.SetChildCount(fTree.RootNode, 10000); - Assert.AreEqual(fTree.RootNode.ChildCount + 1, fTree.RootNode.TotalCount, 'TotalCount <> ChildCount + 1'); + fTree.BeginUpdate; + try + fTree.SetChildCount(fTree.RootNode, 10000); + Assert.AreEqual(fTree.RootNode.ChildCount + 1, fTree.RootNode.TotalCount, 'TotalCount <> ChildCount + 1'); + //fTree.SortTree(-1, sdAscending, false); + finally + fTree.EndUpdate; + end; FreeAndNil(fTree); FreeAndNil(fForm); - - //Now that the tree is destroyed, we have to ensure that the code called - //from WorkerThread.Execute via Synchronize is executed and causes the AV. - //In a real-world GUI Application, CheckSynchronize is called often, but - //here in a Console application we have to do this ourselves. - //Unfortunately the AV will not make the test fail, since it is raised - //in WorkerThread, which has FreeOnTerminate = True; therefore the AVs - //can only be seen with the debugger. - CheckSynchronize; end); end; +procedure TVTWorkerThreadIssue1001Tests.TreeCompareNodes( + Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; + var Result: Integer); +begin + if Random(10) > 5 then + Result:= 1 else + Result:= -1; +end; + initialization + Randomize; TDUnitX.RegisterTestFixture(TVTWorkerThreadIssue1001Tests); end. From 175998ee0553d237a5dd7c8d0107674d27914f9a Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 13:58:52 +0100 Subject: [PATCH 139/681] Reverted commit 5aaba1e on this file, it was merged by accident with the pull request for the unit test. --- Source/VirtualTrees.WorkerThread.pas | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index e62562c3b..740dfb3c2 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -96,6 +96,7 @@ class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean); WorkerThread.Dispose(ACanBlock); end; end; + CheckSynchronize(); // Make sure code queued in the main thread by TBaseVirtualTree.ChangeTreeStatesAsync() get processed before the tree is being destroyed. issue #1001 end; //---------------------------------------------------------------------------------------------------------------------- @@ -126,7 +127,8 @@ procedure TWorkerThread.WaitForValidationTermination(Tree: TBaseVirtualTree); while FCurrentTree = Tree do begin Sleep(1); - CheckSynchronize(); //since Execute uses Synchronize, it must have a chance to finish; #1000 + if (toVariableNodeHeight in TBaseVirtualTreeCracker(Tree).TreeOptions.MiscOptions) then + CheckSynchronize(); // We need to call CheckSynchronize here because we are using TThread.Synchronize in TBaseVirtualTree.MeasureItemHeight() end; end; @@ -138,6 +140,8 @@ procedure TWorkerThread.Execute(); var EnterStates: TVirtualTreeStates; + lCurrentTree: TBaseVirtualTree; + begin TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do @@ -151,7 +155,7 @@ procedure TWorkerThread.Execute(); try if Count > 0 then begin - FCurrentTree := Items[0]; + lCurrentTree := Items[0]; // Remove this tree from waiter list. Delete(0); // If there is yet another tree to work on then set the work event to keep looping. @@ -159,30 +163,25 @@ procedure TWorkerThread.Execute(); SetEvent(WorkEvent); end else - FCurrentTree := nil; + lCurrentTree := nil; finally FWaiterList.UnlockList; end; // Something to do? - if Assigned(FCurrentTree) then + if Assigned(lCurrentTree) then begin try - TThread.Synchronize(nil, procedure - begin - TBaseVirtualTreeCracker(FCurrentTree).DoStateChange([tsValidating], [tsUseCache, tsValidationNeeded]); - end); + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); + FCurrentTree := lCurrentTree; EnterStates := []; if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then EnterStates := [tsUseCache]; finally - TThread.Synchronize(nil, procedure - begin - TBaseVirtualTreeCracker(FCurrentTree).DoStateChange(EnterStates, [tsValidating, tsStopValidation]); - TBaseVirtualTreeCracker(FCurrentTree).UpdateEditBounds; - end); - FCurrentTree := nil; + FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); + Queue(TBaseVirtualTreeCracker(lCurrentTree).UpdateEditBounds); end; end; end;//while From 34aceddbebf0cc10705ff74d00c2cc7ee89298f5 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:02:24 +0100 Subject: [PATCH 140/681] Update VirtualTrees.pas Switched encoding to UTF-8. Issue #1006 --- Source/VirtualTrees.pas | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 1547a6aab..71be72352 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1,4 +1,4 @@ -unit VirtualTrees; +unit VirtualTrees; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance @@ -4058,7 +4058,7 @@ implementation // Do not modify the copyright in any way! Usage of this unit is prohibited without the copyright notice // in the compiled binary file. - Copyright: string = 'Virtual Treeview © 1999, 2010, 2016 Mike Lischke, Joachim Marder'; + Copyright: string = 'Virtual Treeview © 1999, 2010, 2016 Mike Lischke, Joachim Marder'; var StandardOLEFormat: TFormatEtc = ( @@ -14124,7 +14124,7 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); FillBitmap(FMinusBM); FillBitmap(FHotMinusBM); FillBitmap(FSelectedHotMinusBM); - // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style scheiße aus + // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style scheiße aus // Because the self-drawn bitmaps view Vcl Style shit if Theme = 0 then begin @@ -34565,7 +34565,7 @@ function TCustomVirtualStringTree.ContentToHTML(Source: TVSTTextSourceType; cons // Renders the current tree content (depending on Source) as HTML text encoded in UTF-8. // If Caption is not empty then it is used to create and fill the header for the table built here. -// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. +// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. begin Result := VirtualTrees.Export.ContentToHTML(Self, Source, Caption); @@ -34639,7 +34639,7 @@ procedure TCustomVirtualStringTree.RemoveFromSelection(Node: PVirtualNode); function TCustomVirtualStringTree.ContentToRTF(Source: TVSTTextSourceType): RawByteString; // Renders the current tree content (depending on Source) as RTF (rich text). -// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. +// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. begin Result := VirtualTrees.Export.ContentToRTF(Self, Source); From 33d0dbbf7c70c20cedf7e46039d7685b17b8c5cb Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:04:53 +0100 Subject: [PATCH 141/681] Update VirtualTrees.WorkerThread.pas Fixed issue #1008: TWorkerThread.Create creates FWaiterList too late --- Source/VirtualTrees.WorkerThread.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 740dfb3c2..2552daf6e 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -104,9 +104,9 @@ class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean); constructor TWorkerThread.Create(); begin + FWaiterList := TThreadList.Create; inherited Create(False); FreeOnTerminate := True; - FWaiterList := TThreadList.Create; end; //---------------------------------------------------------------------------------------------------------------------- From 04db3a8fadaee0ab9c408a949cedf24be1180ac6 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:39:56 +0100 Subject: [PATCH 142/681] Issue #1006: Make variable WorkEvent a member variable. --- Source/VirtualTrees.WorkerThread.pas | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 2552daf6e..2ceef8f3c 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -13,6 +13,7 @@ TWorkerThread = class(TThread) FCurrentTree: TBaseVirtualTree; FWaiterList: TThreadList; FRefCount: Integer; + FWorkEvent: THandle; class procedure EnsureCreated(); class procedure Dispose(CanBlock: Boolean); procedure WaitForValidationTermination(Tree: TBaseVirtualTree); @@ -47,21 +48,13 @@ TBaseVirtualTreeCracker = class(TBaseVirtualTree) var WorkerThread: TWorkerThread = nil; - WorkEvent: THandle; //----------------- TWorkerThread -------------------------------------------------------------------------------------- class procedure TWorkerThread.EnsureCreated(); begin if not Assigned(WorkerThread) then - begin - // Create an event used to trigger our worker thread when something is to do. - WorkEvent := CreateEvent(nil, False, False, nil); - if WorkEvent = 0 then - RaiseLastOSError; - // Create worker thread, initialize it and send it to its wait loop. WorkerThread := TWorkerThread.Create(); - end; end; class procedure TWorkerThread.Dispose; @@ -70,10 +63,9 @@ class procedure TWorkerThread.Dispose; begin WorkerThread.FreeOnTerminate := not CanBlock; WorkerThread.Terminate(); - SetEvent(WorkEvent); + SetEvent(WorkerThread.FWorkEvent); LRef := WorkerThread; WorkerThread := nil; //Will be freed usinf TThreaf.FreeOnTerminate - CloseHandle(WorkEvent); if CanBlock then LRef.Free; end; @@ -105,6 +97,10 @@ constructor TWorkerThread.Create(); begin FWaiterList := TThreadList.Create; + // Create an event used to trigger our worker thread when something is to do. + FWorkEvent := CreateEvent(nil, False, False, nil); + if FWorkEvent = 0 then + RaiseLastOSError; inherited Create(False); FreeOnTerminate := True; end; @@ -116,6 +112,7 @@ destructor TWorkerThread.Destroy; begin // First let the ancestor stop the thread before freeing our resources. inherited; + CloseHandle(FWorkEvent); FWaiterList.Free; end; @@ -146,7 +143,7 @@ procedure TWorkerThread.Execute(); TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do begin - WaitForSingleObject(WorkEvent, INFINITE); + WaitForSingleObject(FWorkEvent, INFINITE); if Terminated then exit; @@ -160,7 +157,7 @@ procedure TWorkerThread.Execute(); Delete(0); // If there is yet another tree to work on then set the work event to keep looping. if Count > 0 then - SetEvent(WorkEvent); + SetEvent(FWorkEvent); end else lCurrentTree := nil; From de6c06d0d7292534522971a4b37c9725378f25a4 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:44:32 +0100 Subject: [PATCH 143/681] Remainiung chnage for issue #1007 --- Demos/Advanced/Advanced.dproj | 65 ++++++++++++-------------- Demos/Minimal/Minimal.dproj | 68 ++++++++++++++-------------- Source/VirtualTrees.WorkerThread.pas | 2 +- 3 files changed, 62 insertions(+), 73 deletions(-) diff --git a/Demos/Advanced/Advanced.dproj b/Demos/Advanced/Advanced.dproj index d58c96f90..496922864 100644 --- a/Demos/Advanced/Advanced.dproj +++ b/Demos/Advanced/Advanced.dproj @@ -1,14 +1,14 @@  - {E5FD8257-AE07-4A8D-AB79-44170493F9A2} - Advanced.dpr True - Debug - 1 Application + Debug VCL - 19.0 + Advanced.dpr Win32 + {E5FD8257-AE07-4A8D-AB79-44170493F9A2} + 19.1 + 1 true @@ -36,48 +36,43 @@ Advanced - false - true - FileVersion=1.0.0.0 - 1031 - false - C:\windows\microsoft.net\framework\v1.1.4322\system.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.data.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.drawing.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.windows.forms.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.xml.dll;vcl;rtl;$(DCC_UsePackage) - 4096 - true 00400000 - true - false + true + 4096 Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) - false false + true ..\..\Source;..\..\Common;$(DCC_UnitSearchPath) + C:\windows\microsoft.net\framework\v1.1.4322\system.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.data.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.drawing.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.windows.forms.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.xml.dll;vcl;rtl;$(DCC_UsePackage) + FileVersion=1.0.0.0 + 1031 - 1033 - true + true System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) $(BDS)\bin\default_app.manifest - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - true - $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + true + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + 1033 - 0 - false RELEASE;$(DCC_Define) + false + 0 - true DEBUG;$(DCC_Define) + true - true - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - true PerMonitorV2 + true Debug + true + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + 1033 @@ -123,10 +118,6 @@
StateForm
- - Cfg_2 - Base - Base @@ -134,6 +125,10 @@ Cfg_1 Base
+ + Cfg_2 + Base +
Delphi.Personality.12 @@ -143,6 +138,7 @@ Advanced.dpr + False False @@ -161,11 +157,6 @@ 1.0.0.0 - - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - True diff --git a/Demos/Minimal/Minimal.dproj b/Demos/Minimal/Minimal.dproj index 406e5a8b8..3906e4b4d 100644 --- a/Demos/Minimal/Minimal.dproj +++ b/Demos/Minimal/Minimal.dproj @@ -1,14 +1,14 @@  - {9ED56071-1730-40BE-A992-27309A7C55CB} - Minimal.dpr True - Debug - 1 Application + Debug VCL - 14.4 + Minimal.dpr Win32 + {9ED56071-1730-40BE-A992-27309A7C55CB} + 18.8 + 1 true @@ -18,11 +18,6 @@ Base true - - true - Base - true - true Base @@ -33,43 +28,46 @@ Base true + + true + Cfg_2 + true + true + Minimal - 1 - false - 1031 - vcl;rtl;vclx;$(DCC_UsePackage) - false - 3 - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= 00400000 + 3 Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) - false - false + 1 ..\..\Source;$(DCC_UnitSearchPath) - true + vcl;rtl;vclx;$(DCC_UsePackage) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1031 - true + PerMonitor true - 1033 - true - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) $(BDS)\bin\default_app.manifest - - - Minimal_Icon.ico + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + true + 1033 - 0 - false + 0 RELEASE;$(DCC_Define) - false + false + 0 + DEBUG;$(DCC_Define) true false - DEBUG;$(DCC_Define) + + + Debug @@ -78,10 +76,6 @@
MainForm
- - Cfg_2 - Base - Base @@ -89,6 +83,10 @@ Cfg_1 Base + + Cfg_2 + Base +
Delphi.Personality.12 diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 2ceef8f3c..86c15958e 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -202,7 +202,7 @@ class procedure TWorkerThread.AddTree(Tree: TBaseVirtualTree); WorkerThread.FWaiterList.UnlockList; end; - SetEvent(WorkEvent); + SetEvent(WorkerThread.FWorkEvent); end; //---------------------------------------------------------------------------------------------------------------------- From b2331100701b576d4b94c4847750e16bf08a8d8b Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:49:08 +0100 Subject: [PATCH 144/681] Fixed Issue #1006: Change encoding to UTF8. --- Source/VirtualTrees.Accessibility.pas | 2 +- Source/VirtualTrees.AccessibilityFactory.pas | 4 ++-- Source/VirtualTrees.Actions.pas | 2 +- Source/VirtualTrees.Classes.pas | 2 +- Source/VirtualTrees.ClipBoard.pas | 2 +- Source/VirtualTrees.HeaderPopup.pas | 2 +- Source/VirtualTrees.StyleHooks.pas | 2 +- Source/VirtualTrees.Utils.pas | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/VirtualTrees.Accessibility.pas b/Source/VirtualTrees.Accessibility.pas index 81a86ab1b..04b516d2a 100644 --- a/Source/VirtualTrees.Accessibility.pas +++ b/Source/VirtualTrees.Accessibility.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.Accessibility; +unit VirtualTrees.Accessibility; // This unit implements iAccessible interfaces for the VirtualTree visual components // and the currently focused node. diff --git a/Source/VirtualTrees.AccessibilityFactory.pas b/Source/VirtualTrees.AccessibilityFactory.pas index 5c772d81a..7bfbda85b 100644 --- a/Source/VirtualTrees.AccessibilityFactory.pas +++ b/Source/VirtualTrees.AccessibilityFactory.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.AccessibilityFactory; +unit VirtualTrees.AccessibilityFactory; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance @@ -30,7 +30,7 @@ // the AccessibleItem is returned when the Accessible is being asked for the first child // To create your own IAccessibles, use the VTStandardAccessible unit as a reference, // and assign your Accessibles to the variables in the unit's initialization. -// You only need to add the unit to your project, and voilá, you have an accessible string tree! +// You only need to add the unit to your project, and voilá, you have an accessible string tree! // // Written by Marco Zehe. (c) 2007 diff --git a/Source/VirtualTrees.Actions.pas b/Source/VirtualTrees.Actions.pas index 7951199b3..8e6078107 100644 --- a/Source/VirtualTrees.Actions.pas +++ b/Source/VirtualTrees.Actions.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.Actions; +unit VirtualTrees.Actions; interface diff --git a/Source/VirtualTrees.Classes.pas b/Source/VirtualTrees.Classes.pas index c8284e980..d73b1585f 100644 --- a/Source/VirtualTrees.Classes.pas +++ b/Source/VirtualTrees.Classes.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.Classes; +unit VirtualTrees.Classes; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance diff --git a/Source/VirtualTrees.ClipBoard.pas b/Source/VirtualTrees.ClipBoard.pas index b406f0da8..e995a8775 100644 --- a/Source/VirtualTrees.ClipBoard.pas +++ b/Source/VirtualTrees.ClipBoard.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.ClipBoard; +unit VirtualTrees.ClipBoard; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance diff --git a/Source/VirtualTrees.HeaderPopup.pas b/Source/VirtualTrees.HeaderPopup.pas index 2499b4e82..e5eed0041 100644 --- a/Source/VirtualTrees.HeaderPopup.pas +++ b/Source/VirtualTrees.HeaderPopup.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.HeaderPopup; +unit VirtualTrees.HeaderPopup; //---------------------------------------------------------------------------------------------------------------------- // diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index 9eed669a7..ad0d317cc 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.StyleHooks; +unit VirtualTrees.StyleHooks; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance diff --git a/Source/VirtualTrees.Utils.pas b/Source/VirtualTrees.Utils.pas index cd04a3f46..14bc9cae2 100644 --- a/Source/VirtualTrees.Utils.pas +++ b/Source/VirtualTrees.Utils.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.Utils; +unit VirtualTrees.Utils; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance From f3d894c885744b8ee0c5237c6ac50a8b4237e9dc Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 6 Dec 2020 20:56:35 +0100 Subject: [PATCH 145/681] Added full unit scope names to ensure compiling without unit aliases. --- Tests/VirtualTreeTests.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/VirtualTreeTests.pas b/Tests/VirtualTreeTests.pas index 5a4d84be5..19e8c1269 100644 --- a/Tests/VirtualTreeTests.pas +++ b/Tests/VirtualTreeTests.pas @@ -4,7 +4,7 @@ interface uses DUnitX.TestFramework, - Windows, + WinApi.Windows, VirtualTrees, VirtualTrees.Utils, Vcl.Graphics; From 32ec6ad7ac7710afa94e76451b61c7b62dde66eb Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 6 Dec 2020 21:01:41 +0100 Subject: [PATCH 146/681] Switched to UTF-8. Issue #1006 --- Source/VirtualTrees.WorkerThread.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 37e8f074f..e62562c3b 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.WorkerThread; +unit VirtualTrees.WorkerThread; interface From 998e454561d1a8c62d9962a0f9088f5ff4e1306b Mon Sep 17 00:00:00 2001 From: Daniel Trierweiler Date: Mon, 7 Dec 2020 13:19:53 +0100 Subject: [PATCH 147/681] #1004: Publish the StyleName property, so it can be used with the DFM. --- Source/VirtualTrees.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 24bdf78a3..5b254864a 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -3595,6 +3595,7 @@ TVirtualStringTree = class(TCustomVirtualStringTree) property ShowHint; property StateImages; property StyleElements; + {$if CompilerVersion >= 34}property StyleName;{$ifend} property TabOrder; property TabStop default True; property TextMargin; From c699ca6447985354d789706f6250d4267efe5f0c Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 13:49:36 +0100 Subject: [PATCH 148/681] Revert "Merge pull request #1005 from modersohn/master" This reverts commit 77ad8184807323ba662cd1c2a3a9b1e85046d495, reversing changes made to 31a9b5c58395ef35e89833964e4b930c49965d81. --- Source/VirtualTrees.WorkerThread.pas | 27 +++++++++++----------- Source/VirtualTrees.pas | 8 +++---- Tests/Tests.dproj | 14 +++-------- Tests/VTWorkerThreadIssue1001Tests.pas | 32 +++++++++++++++++--------- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index e62562c3b..740dfb3c2 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -96,6 +96,7 @@ class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean); WorkerThread.Dispose(ACanBlock); end; end; + CheckSynchronize(); // Make sure code queued in the main thread by TBaseVirtualTree.ChangeTreeStatesAsync() get processed before the tree is being destroyed. issue #1001 end; //---------------------------------------------------------------------------------------------------------------------- @@ -126,7 +127,8 @@ procedure TWorkerThread.WaitForValidationTermination(Tree: TBaseVirtualTree); while FCurrentTree = Tree do begin Sleep(1); - CheckSynchronize(); //since Execute uses Synchronize, it must have a chance to finish; #1000 + if (toVariableNodeHeight in TBaseVirtualTreeCracker(Tree).TreeOptions.MiscOptions) then + CheckSynchronize(); // We need to call CheckSynchronize here because we are using TThread.Synchronize in TBaseVirtualTree.MeasureItemHeight() end; end; @@ -138,6 +140,8 @@ procedure TWorkerThread.Execute(); var EnterStates: TVirtualTreeStates; + lCurrentTree: TBaseVirtualTree; + begin TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do @@ -151,7 +155,7 @@ procedure TWorkerThread.Execute(); try if Count > 0 then begin - FCurrentTree := Items[0]; + lCurrentTree := Items[0]; // Remove this tree from waiter list. Delete(0); // If there is yet another tree to work on then set the work event to keep looping. @@ -159,30 +163,25 @@ procedure TWorkerThread.Execute(); SetEvent(WorkEvent); end else - FCurrentTree := nil; + lCurrentTree := nil; finally FWaiterList.UnlockList; end; // Something to do? - if Assigned(FCurrentTree) then + if Assigned(lCurrentTree) then begin try - TThread.Synchronize(nil, procedure - begin - TBaseVirtualTreeCracker(FCurrentTree).DoStateChange([tsValidating], [tsUseCache, tsValidationNeeded]); - end); + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); + FCurrentTree := lCurrentTree; EnterStates := []; if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then EnterStates := [tsUseCache]; finally - TThread.Synchronize(nil, procedure - begin - TBaseVirtualTreeCracker(FCurrentTree).DoStateChange(EnterStates, [tsValidating, tsStopValidation]); - TBaseVirtualTreeCracker(FCurrentTree).UpdateEditBounds; - end); - FCurrentTree := nil; + FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); + Queue(TBaseVirtualTreeCracker(lCurrentTree).UpdateEditBounds); end; end; end;//while diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 5b254864a..859cea7aa 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -4059,7 +4059,7 @@ implementation // Do not modify the copyright in any way! Usage of this unit is prohibited without the copyright notice // in the compiled binary file. - Copyright: string = 'Virtual Treeview � 1999, 2010, 2016 Mike Lischke, Joachim Marder'; + Copyright: string = 'Virtual Treeview © 1999, 2010, 2016 Mike Lischke, Joachim Marder'; var StandardOLEFormat: TFormatEtc = ( @@ -14125,7 +14125,7 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); FillBitmap(FMinusBM); FillBitmap(FHotMinusBM); FillBitmap(FSelectedHotMinusBM); - // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style schei�e aus + // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style scheiße aus // Because the self-drawn bitmaps view Vcl Style shit if Theme = 0 then begin @@ -34566,7 +34566,7 @@ function TCustomVirtualStringTree.ContentToHTML(Source: TVSTTextSourceType; cons // Renders the current tree content (depending on Source) as HTML text encoded in UTF-8. // If Caption is not empty then it is used to create and fill the header for the table built here. -// Based on ideas and code from Frank van den Bergh and Andreas H�rstemeier. +// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. begin Result := VirtualTrees.Export.ContentToHTML(Self, Source, Caption); @@ -34640,7 +34640,7 @@ procedure TCustomVirtualStringTree.RemoveFromSelection(Node: PVirtualNode); function TCustomVirtualStringTree.ContentToRTF(Source: TVSTTextSourceType): RawByteString; // Renders the current tree content (depending on Source) as RTF (rich text). -// Based on ideas and code from Frank van den Bergh and Andreas H�rstemeier. +// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. begin Result := VirtualTrees.Export.ContentToRTF(Self, Source); diff --git a/Tests/Tests.dproj b/Tests/Tests.dproj index 681d49fc6..967b2bdc6 100644 --- a/Tests/Tests.dproj +++ b/Tests/Tests.dproj @@ -5,8 +5,8 @@ Tests.dpr True Debug - Win64 - 3 + Win32 + 1 Console VCL @@ -54,12 +54,6 @@ true true - - true - Cfg_1 - true - true - true Base @@ -79,8 +73,6 @@ false false $(BDS)\bin\delphi_PROJECTICON.ico - .\$(Platform)\$(Config) - .\$(Platform)\$(Config) $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png @@ -912,7 +904,7 @@ False False True - True + False
12 diff --git a/Tests/VTWorkerThreadIssue1001Tests.pas b/Tests/VTWorkerThreadIssue1001Tests.pas index 40d19abcb..6f979e0e3 100644 --- a/Tests/VTWorkerThreadIssue1001Tests.pas +++ b/Tests/VTWorkerThreadIssue1001Tests.pas @@ -19,6 +19,8 @@ TVTWorkerThreadIssue1001Tests = class strict private fTree: TTestBaseVirtualTree; fForm: TForm; + procedure TreeCompareNodes(Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; + Column: TColumnIndex; var Result: Integer); public [Setup] procedure Setup; @@ -44,6 +46,7 @@ procedure TVTWorkerThreadIssue1001Tests.Setup; fForm := TForm.Create(nil); fTree := TTestBaseVirtualTree.Create(fForm); fTree.TreeOptions.AutoOptions:= fTree.TreeOptions.AutoOptions - [toAutoSort]; + fTree.OnCompareNodes:= TreeCompareNodes; end); end; @@ -59,23 +62,30 @@ procedure TVTWorkerThreadIssue1001Tests.TestDestroyWhileWorkerThreadBusy; begin TThread.Synchronize(nil, procedure begin - fTree.SetChildCount(fTree.RootNode, 10000); - Assert.AreEqual(fTree.RootNode.ChildCount + 1, fTree.RootNode.TotalCount, 'TotalCount <> ChildCount + 1'); + fTree.BeginUpdate; + try + fTree.SetChildCount(fTree.RootNode, 10000); + Assert.AreEqual(fTree.RootNode.ChildCount + 1, fTree.RootNode.TotalCount, 'TotalCount <> ChildCount + 1'); + //fTree.SortTree(-1, sdAscending, false); + finally + fTree.EndUpdate; + end; FreeAndNil(fTree); FreeAndNil(fForm); - - //Now that the tree is destroyed, we have to ensure that the code called - //from WorkerThread.Execute via Synchronize is executed and causes the AV. - //In a real-world GUI Application, CheckSynchronize is called often, but - //here in a Console application we have to do this ourselves. - //Unfortunately the AV will not make the test fail, since it is raised - //in WorkerThread, which has FreeOnTerminate = True; therefore the AVs - //can only be seen with the debugger. - CheckSynchronize; end); end; +procedure TVTWorkerThreadIssue1001Tests.TreeCompareNodes( + Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode; Column: TColumnIndex; + var Result: Integer); +begin + if Random(10) > 5 then + Result:= 1 else + Result:= -1; +end; + initialization + Randomize; TDUnitX.RegisterTestFixture(TVTWorkerThreadIssue1001Tests); end. From 2cd200051d3baec18b4a3f929b3b87211ab87262 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:02:24 +0100 Subject: [PATCH 149/681] Update VirtualTrees.pas Switched encoding to UTF-8. Issue #1006 --- Source/VirtualTrees.pas | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 859cea7aa..0b6ca3261 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1,4 +1,4 @@ -unit VirtualTrees; +unit VirtualTrees; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance @@ -4059,7 +4059,7 @@ implementation // Do not modify the copyright in any way! Usage of this unit is prohibited without the copyright notice // in the compiled binary file. - Copyright: string = 'Virtual Treeview © 1999, 2010, 2016 Mike Lischke, Joachim Marder'; + Copyright: string = 'Virtual Treeview © 1999, 2010, 2016 Mike Lischke, Joachim Marder'; var StandardOLEFormat: TFormatEtc = ( @@ -14125,7 +14125,7 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); FillBitmap(FMinusBM); FillBitmap(FHotMinusBM); FillBitmap(FSelectedHotMinusBM); - // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style scheiße aus + // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style scheiße aus // Because the self-drawn bitmaps view Vcl Style shit if Theme = 0 then begin @@ -34566,7 +34566,7 @@ function TCustomVirtualStringTree.ContentToHTML(Source: TVSTTextSourceType; cons // Renders the current tree content (depending on Source) as HTML text encoded in UTF-8. // If Caption is not empty then it is used to create and fill the header for the table built here. -// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. +// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. begin Result := VirtualTrees.Export.ContentToHTML(Self, Source, Caption); @@ -34640,7 +34640,7 @@ procedure TCustomVirtualStringTree.RemoveFromSelection(Node: PVirtualNode); function TCustomVirtualStringTree.ContentToRTF(Source: TVSTTextSourceType): RawByteString; // Renders the current tree content (depending on Source) as RTF (rich text). -// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. +// Based on ideas and code from Frank van den Bergh and Andreas Hörstemeier. begin Result := VirtualTrees.Export.ContentToRTF(Self, Source); From 73b92e923f941a120f17ae7d87025a5e559166e8 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:04:53 +0100 Subject: [PATCH 150/681] Update VirtualTrees.WorkerThread.pas Fixed issue #1008: TWorkerThread.Create creates FWaiterList too late --- Source/VirtualTrees.WorkerThread.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 740dfb3c2..2552daf6e 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -104,9 +104,9 @@ class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean); constructor TWorkerThread.Create(); begin + FWaiterList := TThreadList.Create; inherited Create(False); FreeOnTerminate := True; - FWaiterList := TThreadList.Create; end; //---------------------------------------------------------------------------------------------------------------------- From 867ef8faa3c70e891d7265cec5052c6e21229a29 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:39:56 +0100 Subject: [PATCH 151/681] Issue #1006: Make variable WorkEvent a member variable. --- Source/VirtualTrees.WorkerThread.pas | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 2552daf6e..2ceef8f3c 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -13,6 +13,7 @@ TWorkerThread = class(TThread) FCurrentTree: TBaseVirtualTree; FWaiterList: TThreadList; FRefCount: Integer; + FWorkEvent: THandle; class procedure EnsureCreated(); class procedure Dispose(CanBlock: Boolean); procedure WaitForValidationTermination(Tree: TBaseVirtualTree); @@ -47,21 +48,13 @@ TBaseVirtualTreeCracker = class(TBaseVirtualTree) var WorkerThread: TWorkerThread = nil; - WorkEvent: THandle; //----------------- TWorkerThread -------------------------------------------------------------------------------------- class procedure TWorkerThread.EnsureCreated(); begin if not Assigned(WorkerThread) then - begin - // Create an event used to trigger our worker thread when something is to do. - WorkEvent := CreateEvent(nil, False, False, nil); - if WorkEvent = 0 then - RaiseLastOSError; - // Create worker thread, initialize it and send it to its wait loop. WorkerThread := TWorkerThread.Create(); - end; end; class procedure TWorkerThread.Dispose; @@ -70,10 +63,9 @@ class procedure TWorkerThread.Dispose; begin WorkerThread.FreeOnTerminate := not CanBlock; WorkerThread.Terminate(); - SetEvent(WorkEvent); + SetEvent(WorkerThread.FWorkEvent); LRef := WorkerThread; WorkerThread := nil; //Will be freed usinf TThreaf.FreeOnTerminate - CloseHandle(WorkEvent); if CanBlock then LRef.Free; end; @@ -105,6 +97,10 @@ constructor TWorkerThread.Create(); begin FWaiterList := TThreadList.Create; + // Create an event used to trigger our worker thread when something is to do. + FWorkEvent := CreateEvent(nil, False, False, nil); + if FWorkEvent = 0 then + RaiseLastOSError; inherited Create(False); FreeOnTerminate := True; end; @@ -116,6 +112,7 @@ destructor TWorkerThread.Destroy; begin // First let the ancestor stop the thread before freeing our resources. inherited; + CloseHandle(FWorkEvent); FWaiterList.Free; end; @@ -146,7 +143,7 @@ procedure TWorkerThread.Execute(); TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do begin - WaitForSingleObject(WorkEvent, INFINITE); + WaitForSingleObject(FWorkEvent, INFINITE); if Terminated then exit; @@ -160,7 +157,7 @@ procedure TWorkerThread.Execute(); Delete(0); // If there is yet another tree to work on then set the work event to keep looping. if Count > 0 then - SetEvent(WorkEvent); + SetEvent(FWorkEvent); end else lCurrentTree := nil; From 1daf1049d17bc5c26d9c5666b3f9e42463871956 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:44:32 +0100 Subject: [PATCH 152/681] Remainiung chnage for issue #1007 --- Demos/Advanced/Advanced.dproj | 65 ++++++++++++-------------- Demos/Minimal/Minimal.dproj | 68 ++++++++++++++-------------- Source/VirtualTrees.WorkerThread.pas | 2 +- 3 files changed, 62 insertions(+), 73 deletions(-) diff --git a/Demos/Advanced/Advanced.dproj b/Demos/Advanced/Advanced.dproj index d58c96f90..496922864 100644 --- a/Demos/Advanced/Advanced.dproj +++ b/Demos/Advanced/Advanced.dproj @@ -1,14 +1,14 @@  - {E5FD8257-AE07-4A8D-AB79-44170493F9A2} - Advanced.dpr True - Debug - 1 Application + Debug VCL - 19.0 + Advanced.dpr Win32 + {E5FD8257-AE07-4A8D-AB79-44170493F9A2} + 19.1 + 1 true @@ -36,48 +36,43 @@ Advanced - false - true - FileVersion=1.0.0.0 - 1031 - false - C:\windows\microsoft.net\framework\v1.1.4322\system.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.data.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.drawing.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.windows.forms.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.xml.dll;vcl;rtl;$(DCC_UsePackage) - 4096 - true 00400000 - true - false + true + 4096 Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) - false false + true ..\..\Source;..\..\Common;$(DCC_UnitSearchPath) + C:\windows\microsoft.net\framework\v1.1.4322\system.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.data.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.drawing.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.windows.forms.dll;C:\windows\microsoft.net\framework\v1.1.4322\system.xml.dll;vcl;rtl;$(DCC_UsePackage) + FileVersion=1.0.0.0 + 1031 - 1033 - true + true System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) $(BDS)\bin\default_app.manifest - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - true - $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + true + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + 1033 - 0 - false RELEASE;$(DCC_Define) + false + 0 - true DEBUG;$(DCC_Define) + true - true - 1033 - CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) - true PerMonitorV2 + true Debug + true + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + 1033 @@ -123,10 +118,6 @@
StateForm
- - Cfg_2 - Base - Base @@ -134,6 +125,10 @@ Cfg_1 Base + + Cfg_2 + Base +
Delphi.Personality.12 @@ -143,6 +138,7 @@ Advanced.dpr + False False @@ -161,11 +157,6 @@ 1.0.0.0 - - DBExpress Enterprise Data Explorer Integration - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - True diff --git a/Demos/Minimal/Minimal.dproj b/Demos/Minimal/Minimal.dproj index 406e5a8b8..3906e4b4d 100644 --- a/Demos/Minimal/Minimal.dproj +++ b/Demos/Minimal/Minimal.dproj @@ -1,14 +1,14 @@  - {9ED56071-1730-40BE-A992-27309A7C55CB} - Minimal.dpr True - Debug - 1 Application + Debug VCL - 14.4 + Minimal.dpr Win32 + {9ED56071-1730-40BE-A992-27309A7C55CB} + 18.8 + 1 true @@ -18,11 +18,6 @@ Base true - - true - Base - true - true Base @@ -33,43 +28,46 @@ Base true + + true + Cfg_2 + true + true + Minimal - 1 - false - 1031 - vcl;rtl;vclx;$(DCC_UsePackage) - false - 3 - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= 00400000 + 3 Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) - false - false + 1 ..\..\Source;$(DCC_UnitSearchPath) - true + vcl;rtl;vclx;$(DCC_UsePackage) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1031 - true + PerMonitor true - 1033 - true - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) $(BDS)\bin\default_app.manifest - - - Minimal_Icon.ico + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + true + 1033 - 0 - false + 0 RELEASE;$(DCC_Define) - false + false + 0 + DEBUG;$(DCC_Define) true false - DEBUG;$(DCC_Define) + + + Debug @@ -78,10 +76,6 @@
MainForm
- - Cfg_2 - Base - Base @@ -89,6 +83,10 @@ Cfg_1 Base + + Cfg_2 + Base +
Delphi.Personality.12 diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 2ceef8f3c..86c15958e 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -202,7 +202,7 @@ class procedure TWorkerThread.AddTree(Tree: TBaseVirtualTree); WorkerThread.FWaiterList.UnlockList; end; - SetEvent(WorkEvent); + SetEvent(WorkerThread.FWorkEvent); end; //---------------------------------------------------------------------------------------------------------------------- From 3a08dfa7a783d93dbb598474885e13ecffc05851 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 14:49:08 +0100 Subject: [PATCH 153/681] Fixed Issue #1006: Change encoding to UTF8. --- Source/VirtualTrees.Accessibility.pas | 2 +- Source/VirtualTrees.AccessibilityFactory.pas | 4 ++-- Source/VirtualTrees.Actions.pas | 2 +- Source/VirtualTrees.Classes.pas | 2 +- Source/VirtualTrees.ClipBoard.pas | 2 +- Source/VirtualTrees.HeaderPopup.pas | 2 +- Source/VirtualTrees.StyleHooks.pas | 2 +- Source/VirtualTrees.Utils.pas | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/VirtualTrees.Accessibility.pas b/Source/VirtualTrees.Accessibility.pas index 81a86ab1b..04b516d2a 100644 --- a/Source/VirtualTrees.Accessibility.pas +++ b/Source/VirtualTrees.Accessibility.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.Accessibility; +unit VirtualTrees.Accessibility; // This unit implements iAccessible interfaces for the VirtualTree visual components // and the currently focused node. diff --git a/Source/VirtualTrees.AccessibilityFactory.pas b/Source/VirtualTrees.AccessibilityFactory.pas index 5c772d81a..7bfbda85b 100644 --- a/Source/VirtualTrees.AccessibilityFactory.pas +++ b/Source/VirtualTrees.AccessibilityFactory.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.AccessibilityFactory; +unit VirtualTrees.AccessibilityFactory; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance @@ -30,7 +30,7 @@ // the AccessibleItem is returned when the Accessible is being asked for the first child // To create your own IAccessibles, use the VTStandardAccessible unit as a reference, // and assign your Accessibles to the variables in the unit's initialization. -// You only need to add the unit to your project, and voilá, you have an accessible string tree! +// You only need to add the unit to your project, and voilá, you have an accessible string tree! // // Written by Marco Zehe. (c) 2007 diff --git a/Source/VirtualTrees.Actions.pas b/Source/VirtualTrees.Actions.pas index 7951199b3..8e6078107 100644 --- a/Source/VirtualTrees.Actions.pas +++ b/Source/VirtualTrees.Actions.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.Actions; +unit VirtualTrees.Actions; interface diff --git a/Source/VirtualTrees.Classes.pas b/Source/VirtualTrees.Classes.pas index c8284e980..d73b1585f 100644 --- a/Source/VirtualTrees.Classes.pas +++ b/Source/VirtualTrees.Classes.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.Classes; +unit VirtualTrees.Classes; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance diff --git a/Source/VirtualTrees.ClipBoard.pas b/Source/VirtualTrees.ClipBoard.pas index b406f0da8..e995a8775 100644 --- a/Source/VirtualTrees.ClipBoard.pas +++ b/Source/VirtualTrees.ClipBoard.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.ClipBoard; +unit VirtualTrees.ClipBoard; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance diff --git a/Source/VirtualTrees.HeaderPopup.pas b/Source/VirtualTrees.HeaderPopup.pas index 2499b4e82..e5eed0041 100644 --- a/Source/VirtualTrees.HeaderPopup.pas +++ b/Source/VirtualTrees.HeaderPopup.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.HeaderPopup; +unit VirtualTrees.HeaderPopup; //---------------------------------------------------------------------------------------------------------------------- // diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index 9eed669a7..ad0d317cc 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.StyleHooks; +unit VirtualTrees.StyleHooks; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance diff --git a/Source/VirtualTrees.Utils.pas b/Source/VirtualTrees.Utils.pas index cd04a3f46..14bc9cae2 100644 --- a/Source/VirtualTrees.Utils.pas +++ b/Source/VirtualTrees.Utils.pas @@ -1,4 +1,4 @@ -unit VirtualTrees.Utils; +unit VirtualTrees.Utils; // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in compliance From 062cda284b69b63ce7d5162571153d95b0e02877 Mon Sep 17 00:00:00 2001 From: Sebastian Modersohn Date: Mon, 7 Dec 2020 15:19:46 +0100 Subject: [PATCH 154/681] Added exception handling to TWorkerThread.Execute * local exception object is acquired and re-raised in a Synchronize call --- Source/VirtualTrees.WorkerThread.pas | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 86c15958e..02e51de2f 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -138,11 +138,13 @@ procedure TWorkerThread.Execute(); var EnterStates: TVirtualTreeStates; lCurrentTree: TBaseVirtualTree; - + lExceptAddr: Pointer; + lException: TObject; + begin TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do - begin + try WaitForSingleObject(FWorkEvent, INFINITE); if Terminated then exit; @@ -181,6 +183,17 @@ procedure TWorkerThread.Execute(); Queue(TBaseVirtualTreeCracker(lCurrentTree).UpdateEditBounds); end; end; + except + on Exception do + begin + lExceptAddr := ExceptAddr; + lException := AcquireExceptionObject; + TThread.Synchronize(nil, procedure + begin + raise lException at lExceptAddr; + end); + Continue; //the thread should continue to run + end; end;//while end; From e66d5fbc617036447216a84d7ed2a6d0becf777a Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 21:07:30 +0100 Subject: [PATCH 155/681] * Added Win64 platform * Cleanup of file using Projec Magician --- Tests/Tests.dproj | 864 +++------------------------------------------- 1 file changed, 42 insertions(+), 822 deletions(-) diff --git a/Tests/Tests.dproj b/Tests/Tests.dproj index 967b2bdc6..e7ee6dd23 100644 --- a/Tests/Tests.dproj +++ b/Tests/Tests.dproj @@ -1,38 +1,18 @@  - {D37CFA56-3B13-4C93-91F7-DDC227C20116} - 19.2 - Tests.dpr True - Debug - Win32 - 1 Console + Debug VCL + Tests.dpr + Win32 + {D37CFA56-3B13-4C93-91F7-DDC227C20116} + 19.2 + 3 true - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - true Base @@ -54,108 +34,64 @@ true true + + true + Cfg_1 + true + true + true Base true - $(BDS)\bin\delphi_PROJECTICNS.icns + Tests + DCU\$(Platform)\$(Config) System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) $(DUnitX);..\Source;$(DCC_UnitSearchPath) - 1031 - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - $(BDS)\bin\default_app.manifest - Tests - false - false - false - false - false + $(BDS)\bin\delphi_PROJECTICNS.icns $(BDS)\bin\delphi_PROJECTICON.ico - - - $(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png - $(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png - $(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png - $(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png - $(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png - $(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png - $(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png - $(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png - $(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png - true - true - true - true - true - true - true - true - true - true - $(BDS)\bin\Artwork\Android\FM_NotificationIcon_24x24.png - $(BDS)\bin\Artwork\Android\FM_NotificationIcon_36x36.png - $(BDS)\bin\Artwork\Android\FM_NotificationIcon_48x48.png - $(BDS)\bin\Artwork\Android\FM_NotificationIcon_72x72.png - $(BDS)\bin\Artwork\Android\FM_NotificationIcon_96x96.png - - - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png - $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png - - - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png - $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png - - - $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_120x120.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPhone\FM_NotificationIcon_60x60.png - $(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png - $(BDS)\bin\Artwork\iOS\iPad\FM_NotificationIcon_40x40.png - $(BDS)\bin\Artwork\iOS\iPad\FM_ApplicationIcon_152x152.png + $(BDS)\bin\default_app.manifest + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 1031 Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - 1033 FireDACTDataDriver;FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;FireDACPgDriver;fmx;IndySystem;TeeDB;tethering;DBXInterBaseDriver;DataSnapClient;DataSnapServer;DataSnapCommon;DataSnapProviderClient;DBXSybaseASEDriver;frxe22;DbxCommonDriver;vclimg;DUnitXRuntime;dbxcds;DatasnapConnectorsFreePascal;appanalytics;vcldb;vcldsnap;fmxFireDAC;UIRibbonPackage;DBXDb2Driver;DBXOracleDriver;CustomIPTransport;vclribbon;dsnap;IndyIPServer;fmxase;vcl;IndyCore;DBXMSSQLDriver;IndyIPCommon;CloudService;FmxTeeUI;FireDACIBDriver;DataSnapFireDAC;FireDACDBXDriver;soapserver;inetdbxpress;dsnapxml;FireDACInfxDriver;FireDACDb2Driver;adortl;madBasic_;FireDACASADriver;frx22;bindcompfmx;FireDACODBCDriver;RESTBackendComponents;emsclientfiredac;rtl;dbrtl;DbxClientDriver;FireDACCommon;bindcomp;inetdb;Tee;DBXOdbcDriver;vclFireDAC;madDisAsm_;xmlrtl;DataSnapNativeClient;svnui;IndyProtocols;DBXMySQLDriver;FireDACCommonDriver;bindengine;vclactnband;bindcompdbx;soaprtl;FMXTee;TeeUI;bindcompvcl;vclie;frxDB22;FireDACADSDriver;vcltouch;madExcept_;emsclient;VCLRESTComponents;FireDACMSSQLDriver;FireDAC;VclSmp;DBXInformixDriver;DataSnapConnectors;DataSnapServerMidas;dsnapcon;DBXFirebirdDriver;inet;fmxobj;FireDACMySQLDriver;soapmidas;vclx;svn;DBXSybaseASADriver;FireDACOracleDriver;fmxdae;RESTComponents;VirtualTreesR;FireDACMSAccDriver;dbexpress;DataSnapIndy10ServerTransport;IndyIPClient;$(DCC_UsePackage) + 1033 + Debug + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) FireDACTDataDriver;FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;FireDACPgDriver;fmx;IndySystem;TeeDB;tethering;DBXInterBaseDriver;DataSnapClient;DataSnapServer;DataSnapCommon;DataSnapProviderClient;DBXSybaseASEDriver;DbxCommonDriver;vclimg;dbxcds;DatasnapConnectorsFreePascal;appanalytics;vcldb;vcldsnap;fmxFireDAC;DBXDb2Driver;DBXOracleDriver;CustomIPTransport;vclribbon;dsnap;IndyIPServer;fmxase;vcl;IndyCore;DBXMSSQLDriver;IndyIPCommon;CloudService;FmxTeeUI;FireDACIBDriver;DataSnapFireDAC;FireDACDBXDriver;soapserver;inetdbxpress;dsnapxml;FireDACInfxDriver;FireDACDb2Driver;adortl;FireDACASADriver;bindcompfmx;FireDACODBCDriver;RESTBackendComponents;emsclientfiredac;rtl;dbrtl;DbxClientDriver;FireDACCommon;bindcomp;inetdb;Tee;DBXOdbcDriver;vclFireDAC;xmlrtl;DataSnapNativeClient;IndyProtocols;DBXMySQLDriver;FireDACCommonDriver;bindengine;vclactnband;bindcompdbx;soaprtl;FMXTee;TeeUI;bindcompvcl;vclie;FireDACADSDriver;vcltouch;emsclient;VCLRESTComponents;FireDACMSSQLDriver;FireDAC;VclSmp;DBXInformixDriver;DataSnapConnectors;DataSnapServerMidas;dsnapcon;DBXFirebirdDriver;inet;fmxobj;FireDACMySQLDriver;soapmidas;vclx;DBXSybaseASADriver;FireDACOracleDriver;fmxdae;RESTComponents;VirtualTreesR;FireDACMSAccDriver;dbexpress;DataSnapIndy10ServerTransport;IndyIPClient;$(DCC_UsePackage) + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 - true - true - false - off - DEBUG;$(DCC_Define) true - true true + DEBUG;$(DCC_Define) + true + off + true + false + true true - 1033 - None + madExcept;$(DCC_Define) + 3 false + + 3 + - false + 0 RELEASE;$(DCC_Define) + false 0 - 0 @@ -164,10 +100,6 @@ - - Cfg_2 - Base - Base @@ -175,6 +107,10 @@ Cfg_1 Base + + Cfg_2 + Base + Delphi.Personality.12 @@ -184,727 +120,11 @@ Tests.dpr - - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - Embarcadero C++Builder Office 2000 Servers Package - Embarcadero C++Builder Office XP Servers Package - + - - - - - - - - - Tests.exe - true - - - - - 1 - - - 0 - - - - - classes - 1 - - - classes - 1 - - - - - res\xml - 1 - - - res\xml - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\armeabi - 1 - - - library\lib\armeabi - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\mips - 1 - - - library\lib\mips - 1 - - - - - library\lib\armeabi-v7a - 1 - - - library\lib\arm64-v8a - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - res\drawable - 1 - - - res\drawable - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - res\values-v21 - 1 - - - res\values-v21 - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - res\drawable - 1 - - - res\drawable - 1 - - - - - res\drawable-xxhdpi - 1 - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-xxxhdpi - 1 - - - res\drawable-xxxhdpi - 1 - - - - - res\drawable-ldpi - 1 - - - res\drawable-ldpi - 1 - - - - - res\drawable-mdpi - 1 - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - res\drawable-xhdpi - 1 - - - - - res\drawable-mdpi - 1 - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - res\drawable-xhdpi - 1 - - - - - res\drawable-xxhdpi - 1 - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-xxxhdpi - 1 - - - res\drawable-xxxhdpi - 1 - - - - - res\drawable-small - 1 - - - res\drawable-small - 1 - - - - - res\drawable-normal - 1 - - - res\drawable-normal - 1 - - - - - res\drawable-large - 1 - - - res\drawable-large - 1 - - - - - res\drawable-xlarge - 1 - - - res\drawable-xlarge - 1 - - - - - res\values - 1 - - - res\values - 1 - - - - - 1 - - - 1 - - - 0 - - - - - 1 - .framework - - - 1 - .framework - - - 0 - - - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .dll;.bpl - - - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .bpl - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset - 1 - - - - - 1 - - - 1 - - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - - - - - - 1 - - - 1 - - - 1 - - - - - - - - Contents\Resources - 1 - - - Contents\Resources - 1 - - - - - library\lib\armeabi-v7a - 1 - - - library\lib\arm64-v8a - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 0 - - - - - library\lib\armeabi-v7a - 1 - - - - - 1 - - - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - - - - - - - - - - False - False - False - False - False - False True - False + True 12 From a34a76b36958f1606cd89528923e92ff7a6aab65 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 7 Dec 2020 21:25:30 +0100 Subject: [PATCH 156/681] Fix for issue #1001: Prevent potential AV seen in unit test project. --- Source/VirtualTrees.WorkerThread.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 02e51de2f..ee796d0c4 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -171,8 +171,8 @@ procedure TWorkerThread.Execute(); if Assigned(lCurrentTree) then begin try - TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); FCurrentTree := lCurrentTree; + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); EnterStates := []; if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then EnterStates := [tsUseCache]; From 725e1fc3de434004ecc53db4a4635cb8850d9178 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 8 Dec 2020 13:30:33 +0100 Subject: [PATCH 157/681] Update VirtualTrees.WorkerThread.pas More fixes for issue #1001: * In TWorkerThread.Execute() remove local variable lCurrentTree, it does not have a benefit any more and FCurrentTree should be assigned as soon as possible. * WaitForValidationTermination() needs to call CheckSynchronize() in case FCurrentTree equals the given Tree, because ChangeTreeStatesAsync() also uses TThread.Synchronize(). --- Source/VirtualTrees.WorkerThread.pas | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index ee796d0c4..f1eb14384 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -123,9 +123,8 @@ procedure TWorkerThread.WaitForValidationTermination(Tree: TBaseVirtualTree); // Wait for any references to this tree to be released. while FCurrentTree = Tree do begin - Sleep(1); - if (toVariableNodeHeight in TBaseVirtualTreeCracker(Tree).TreeOptions.MiscOptions) then - CheckSynchronize(); // We need to call CheckSynchronize here because we are using TThread.Synchronize in TBaseVirtualTree.MeasureItemHeight() + Sleep(1); // Don't do busy waiting, let the OS scheduler give other threads a time slice + CheckSynchronize(); // We need to call CheckSynchronize here because we are using TThread.Synchronize in TBaseVirtualTree.MeasureItemHeight() and ChangeTreeStatesAsync() end; end; @@ -137,10 +136,9 @@ procedure TWorkerThread.Execute(); var EnterStates: TVirtualTreeStates; - lCurrentTree: TBaseVirtualTree; lExceptAddr: Pointer; lException: TObject; - + begin TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do @@ -154,7 +152,7 @@ procedure TWorkerThread.Execute(); try if Count > 0 then begin - lCurrentTree := Items[0]; + FCurrentTree := Items[0]; // Remove this tree from waiter list. Delete(0); // If there is yet another tree to work on then set the work event to keep looping. @@ -162,25 +160,24 @@ procedure TWorkerThread.Execute(); SetEvent(FWorkEvent); end else - lCurrentTree := nil; + FCurrentTree := nil; finally FWaiterList.UnlockList; end; // Something to do? - if Assigned(lCurrentTree) then + if Assigned(FCurrentTree) then begin try - FCurrentTree := lCurrentTree; - TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); + TBaseVirtualTreeCracker(FCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); EnterStates := []; if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then EnterStates := [tsUseCache]; finally FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() - TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); - Queue(TBaseVirtualTreeCracker(lCurrentTree).UpdateEditBounds); + TBaseVirtualTreeCracker(FCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); + Queue(TBaseVirtualTreeCracker(FCurrentTree).UpdateEditBounds); end; end; except From a990b79c6091ffac424641cbe719d9a04c140680 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 8 Dec 2020 13:50:07 +0100 Subject: [PATCH 158/681] Moved call to UpdateEditBounds() to ChangeTreeStatesAsync(). There is no need to invoke tio the UI thread two times. Also we need to make sure that UpdateEditBounds() is not called on a tree that is already destroyed. --- Source/VirtualTrees.WorkerThread.pas | 3 +-- Source/VirtualTrees.pas | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index f1eb14384..31afaa4bf 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -175,9 +175,8 @@ procedure TWorkerThread.Execute(); EnterStates := [tsUseCache]; finally - FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() TBaseVirtualTreeCracker(FCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); - Queue(TBaseVirtualTreeCracker(FCurrentTree).UpdateEditBounds); + FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() end; end; except diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 0b6ca3261..36d1892e3 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18538,7 +18538,9 @@ procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirt begin // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) if not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then - DoStateChange(EnterStates, LeaveStates) + DoStateChange(EnterStates, LeaveStates); + if (tsValidating in FStates) and (tsValidating in LeaveStates) then + UpdateEditBounds(); end); end; From 4cb29b7d051c3ed65ada7403302ca7b9d0164d8f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 8 Dec 2020 14:11:28 +0100 Subject: [PATCH 159/681] removed the CheckSynchronize in ReleaseThreadReference because we no longer use TThread.Queue(). So there is no longer a need for that, this late in the process. --- Source/VirtualTrees.WorkerThread.pas | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 31afaa4bf..b9e1a71a5 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -88,7 +88,6 @@ class procedure TWorkerThread.ReleaseThreadReference(ACanBlock: Boolean); WorkerThread.Dispose(ACanBlock); end; end; - CheckSynchronize(); // Make sure code queued in the main thread by TBaseVirtualTree.ChangeTreeStatesAsync() get processed before the tree is being destroyed. issue #1001 end; //---------------------------------------------------------------------------------------------------------------------- From 6ed20a192fe4e7e2bf99dba79e25c10f26912518 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 9 Dec 2020 18:58:01 +0100 Subject: [PATCH 160/681] Reverted one chnage for issue #1001: In TWorkerThread.Execute() clear FCurrentTree before calling ChangeTreeStatesAsync() --- Source/VirtualTrees.WorkerThread.pas | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index b9e1a71a5..7e34ae37c 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -137,7 +137,7 @@ procedure TWorkerThread.Execute(); EnterStates: TVirtualTreeStates; lExceptAddr: Pointer; lException: TObject; - + lCurrentTree: TBaseVirtualTree; begin TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do @@ -174,8 +174,9 @@ procedure TWorkerThread.Execute(); EnterStates := [tsUseCache]; finally + lCurrentTree := FCurrentTree; // Save reference in a local variable as it is going to be cleared in the next line. + FCurrentTree := nil; // Important: Clear variable before calling ChangeTreeStatesAsync() to prevent deadlock in WaitForValidationTermination() TBaseVirtualTreeCracker(FCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); - FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() end; end; except From 6e8673e0c666db735854e345462873bf9be42a4d Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 9 Dec 2020 18:59:42 +0100 Subject: [PATCH 161/681] Reverted one chnage for issue #1001: In TWorkerThread.Execute() clear FCurrentTree before calling ChangeTreeStatesAsync() --- Source/VirtualTrees.WorkerThread.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 7e34ae37c..7d3eaaf6c 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -176,7 +176,7 @@ procedure TWorkerThread.Execute(); finally lCurrentTree := FCurrentTree; // Save reference in a local variable as it is going to be cleared in the next line. FCurrentTree := nil; // Important: Clear variable before calling ChangeTreeStatesAsync() to prevent deadlock in WaitForValidationTermination() - TBaseVirtualTreeCracker(FCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); end; end; except From b27306ff6612edaff51732644419e999e2b933f0 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 10 Dec 2020 10:58:44 +0100 Subject: [PATCH 162/681] Reverted one more change for issue #1001 In TWorkerThread.Execute() assign FCurrentTree again after calling ChangeTreeStatesAsync() to prevent a deadlock. --- Source/VirtualTrees.WorkerThread.pas | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index b9e1a71a5..633d97196 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -137,7 +137,7 @@ procedure TWorkerThread.Execute(); EnterStates: TVirtualTreeStates; lExceptAddr: Pointer; lException: TObject; - + lCurrentTree: TBaseVirtualTree; begin TThread.NameThreadForDebugging('VirtualTrees.TWorkerThread'); while not Terminated do @@ -151,7 +151,7 @@ procedure TWorkerThread.Execute(); try if Count > 0 then begin - FCurrentTree := Items[0]; + lCurrentTree := Items[0]; // Remove this tree from waiter list. Delete(0); // If there is yet another tree to work on then set the work event to keep looping. @@ -159,23 +159,24 @@ procedure TWorkerThread.Execute(); SetEvent(FWorkEvent); end else - FCurrentTree := nil; + lCurrentTree := nil; finally FWaiterList.UnlockList; end; // Something to do? - if Assigned(FCurrentTree) then + if Assigned(lCurrentTree) then begin try - TBaseVirtualTreeCracker(FCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); + FCurrentTree := lCurrentTree; EnterStates := []; if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then EnterStates := [tsUseCache]; finally - TBaseVirtualTreeCracker(FCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); - FCurrentTree := nil; //Clear variable to prevent deadlock in WaitForValidationTermination() + FCurrentTree := nil; // Important: Clear variable before calling ChangeTreeStatesAsync() to prevent deadlock in WaitForValidationTermination() + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); end; end; except From eb2159509d349953106b92dc35c484563155fb0e Mon Sep 17 00:00:00 2001 From: Uwe Raabe Date: Sat, 12 Dec 2020 14:11:19 +0100 Subject: [PATCH 163/681] wrapped StyleServices access When a TBaseVirtualTree descendant is used inside a VCL plugin we need the StyleServices to return the current IDE style. Unfortunately that is not what Vcl.Themes.StyleServices returns. Routing this through our own function allows to intercept this and provide the correct style matching the IDE. --- Source/VirtualTrees.pas | 51 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 36d1892e3..6631487aa 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -4012,6 +4012,14 @@ TVirtualDrawTree = class(TCustomVirtualDrawTree) // utility routines function TreeFromNode(Node: PVirtualNode): TBaseVirtualTree; +type + TVTStyleServicesFunc = function (AControl: TControl = nil): TCustomStyleServices; + +function VTStyleServices(AControl: TControl = nil): TCustomStyleServices; + +var + VTStyleServicesFunc: TVTStyleServicesFunc = nil; + //---------------------------------------------------------------------------------------------------------------------- implementation @@ -4290,13 +4298,11 @@ function CreateSystemImageSet(pControl: TWinControl): TImageList; //--------------------------------------------------------------------------- - {$if CompilerVersion >= 34} // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) function StyleServices: TCustomStyleServices; begin - Result := Vcl.Themes.StyleServices(pControl); + Result := VTStyleServices(pControl); end; - {$ifend} procedure AddSystemImage(IL: TImageList; Index: Integer); const @@ -4704,7 +4710,7 @@ procedure TCustomVirtualTreeOptions.SetSelectionOptions(const Value: TVTSelectio function TCustomVirtualTreeOptions.StyleServices(AControl: TControl): TCustomStyleServices; begin - Exit(Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(FOwner){$ifend}); + Result := VTStyleServices(FOwner); end; //---------------------------------------------------------------------------------------------------------------------- @@ -5585,7 +5591,7 @@ procedure TVirtualTreeHintWindow.Paint(); function TVirtualTreeHintWindow.StyleServices(AControl: TControl): TCustomStyleServices; begin - Result := Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(AControl){$ifend}; + Result := VTStyleServices(AControl); end; //---------------------------------------------------------------------------------------------------------------------- @@ -7851,14 +7857,9 @@ procedure TVirtualTreeColumns.SetItem(Index: TColumnIndex; Value: TVirtualTreeCo function TVirtualTreeColumns.StyleServices(AControl: TControl): TCustomStyleServices; begin -{$if CompilerVersion >= 34} if AControl = nil then - Result := Vcl.Themes.StyleServices(FHeader.Treeview) - else - Result := Vcl.Themes.StyleServices(AControl); -{$else} - Result := Vcl.Themes.StyleServices; -{$ifend} + AControl := FHeader.Treeview; + Result := VTStyleServices(AControl); end; //---------------------------------------------------------------------------------------------------------------------- @@ -12008,14 +12009,9 @@ procedure TVTColors.SetColor(const Index: TVTColorEnum; const Value: TColor); function TVTColors.StyleServices(AControl: TControl): TCustomStyleServices; begin -{$if CompilerVersion >= 34} if AControl = nil then - Result := Vcl.Themes.StyleServices(fOwner) - else - Result := Vcl.Themes.StyleServices(AControl); -{$else} - Result := Vcl.Themes.StyleServices; -{$ifend} + AControl := fOwner; + Result := VTStyleServices(AControl); end; //---------------------------------------------------------------------------------------------------------------------- @@ -25225,14 +25221,9 @@ procedure TBaseVirtualTree.StructureChange(Node: PVirtualNode; Reason: TChangeRe function TBaseVirtualTree.StyleServices(AControl: TControl): TCustomStyleServices; begin -{$if CompilerVersion >= 34} if AControl = nil then - Result := Vcl.Themes.StyleServices(Self) - else - Result := Vcl.Themes.StyleServices(AControl); -{$else} - Result := Vcl.Themes.StyleServices; -{$ifend} + AControl := Self; + Result := VTStyleServices(AControl); end; //---------------------------------------------------------------------------------------------------------------------- @@ -35098,6 +35089,14 @@ procedure THeaderPaintInfo.DrawSortArrow(pDirection: TSortDirection); TargetCanvas.Pen.Color := lOldColor; end; +function VTStyleServices(AControl: TControl = nil): TCustomStyleServices; +begin + if Assigned(VTStyleServicesFunc) then + Result := VTStyleServicesFunc(AControl) + else + Result := Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(AControl){$ifend}; +end; + initialization finalization From d90abf79af9ec46349a121d66004fc6a91f821f3 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 14 Dec 2020 20:47:47 +0100 Subject: [PATCH 164/681] * Added comments for new code supplied with pull request #1011 * Moved global callback VTStyleServicesFunc and its prototype to unit VirtualTrees.StyleHooks --- Source/VirtualTrees.StyleHooks.pas | 11 +++++++++++ Source/VirtualTrees.pas | 8 ++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index ad0d317cc..abf83c864 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -101,6 +101,17 @@ TScrollWindow = class(TWinControl) property VertScrollRect; end; +type + /// prototype for the global callback VTStyleServicesFunc. + TVTStyleServicesFunc = function (AControl: TControl = nil): TCustomStyleServices; + + +var + /// Callback that can be used to assign an alternative function to supply style services. + /// Needed for IDE plugins. See pull request #1011 + VTStyleServicesFunc: TVTStyleServicesFunc = nil; + + implementation uses diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 6631487aa..67652fc98 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -4012,14 +4012,10 @@ TVirtualDrawTree = class(TCustomVirtualDrawTree) // utility routines function TreeFromNode(Node: PVirtualNode): TBaseVirtualTree; -type - TVTStyleServicesFunc = function (AControl: TControl = nil): TCustomStyleServices; - +/// Wrapper function for styles services that handles differences between RAD Studio 10.4 and older versions, +/// as well as the case if these controls are used inside the IDE. function VTStyleServices(AControl: TControl = nil): TCustomStyleServices; -var - VTStyleServicesFunc: TVTStyleServicesFunc = nil; - //---------------------------------------------------------------------------------------------------------------------- implementation From d0a04a267a2f71d62c295940598f3ff1a241fb99 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 16 Dec 2020 13:32:46 +0100 Subject: [PATCH 165/681] Follow-up for pull request #1011: Made helper function VTStyleServices() implementation-only. --- Source/VirtualTrees.pas | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 67652fc98..3f36d38a4 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -4012,9 +4012,6 @@ TVirtualDrawTree = class(TCustomVirtualDrawTree) // utility routines function TreeFromNode(Node: PVirtualNode): TBaseVirtualTree; -/// Wrapper function for styles services that handles differences between RAD Studio 10.4 and older versions, -/// as well as the case if these controls are used inside the IDE. -function VTStyleServices(AControl: TControl = nil): TCustomStyleServices; //---------------------------------------------------------------------------------------------------------------------- @@ -4183,6 +4180,18 @@ function TreeFromNode(Node: PVirtualNode): TBaseVirtualTree; //---------------------------------------------------------------------------------------------------------------------- +/// Wrapper function for styles services that handles differences between RAD Studio 10.4 and older versions, +/// as well as the case if these controls are used inside the IDE. +function VTStyleServices(AControl: TControl = nil): TCustomStyleServices; +begin + if Assigned(VTStyleServicesFunc) then + Result := VTStyleServicesFunc(AControl) + else + Result := Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(AControl){$ifend}; +end; + +//---------------------------------------------------------------------------------------------------------------------- + procedure QuickSort(const TheArray: TNodeArray; L, R: Integer); @@ -35085,14 +35094,6 @@ procedure THeaderPaintInfo.DrawSortArrow(pDirection: TSortDirection); TargetCanvas.Pen.Color := lOldColor; end; -function VTStyleServices(AControl: TControl = nil): TCustomStyleServices; -begin - if Assigned(VTStyleServicesFunc) then - Result := VTStyleServicesFunc(AControl) - else - Result := Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(AControl){$ifend}; -end; - initialization finalization From 13b181388b75cd288347ed4fb738b0997a3ba041 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 24 Dec 2020 14:20:39 +0100 Subject: [PATCH 166/681] Possible fix for issue #1001: Make waiting for the working thread parmeterized. --- Source/VirtualTrees.WorkerThread.pas | 17 ++++++++--------- Source/VirtualTrees.pas | 13 ++++++------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 633d97196..f0056d80c 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -28,7 +28,7 @@ TWorkerThread = class(TThread) class procedure ReleaseThreadReference(ACanBlock: Boolean = False); class procedure AddTree(Tree: TBaseVirtualTree); - class procedure RemoveTree(Tree: TBaseVirtualTree); + class procedure RemoveTree(pTree: TBaseVirtualTree; pWaitForValidationTermination: Boolean); end; @@ -168,15 +168,14 @@ procedure TWorkerThread.Execute(); if Assigned(lCurrentTree) then begin try - TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); FCurrentTree := lCurrentTree; + TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); EnterStates := []; if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then EnterStates := [tsUseCache]; - finally - FCurrentTree := nil; // Important: Clear variable before calling ChangeTreeStatesAsync() to prevent deadlock in WaitForValidationTermination() TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); + FCurrentTree := nil; // Important: Clear variable before calling ChangeTreeStatesAsync() to prevent deadlock in WaitForValidationTermination() end; end; except @@ -216,20 +215,20 @@ class procedure TWorkerThread.AddTree(Tree: TBaseVirtualTree); //---------------------------------------------------------------------------------------------------------------------- -class procedure TWorkerThread.RemoveTree(Tree: TBaseVirtualTree); - +class procedure TWorkerThread.RemoveTree(pTree: TBaseVirtualTree; pWaitForValidationTermination: Boolean); begin if not Assigned(WorkerThread) then exit; - Assert(Assigned(Tree), 'Tree must not be nil.'); + Assert(Assigned(pTree), 'pTree must not be nil.'); with WorkerThread.FWaiterList.LockList do try - Remove(Tree); + Remove(pTree); finally WorkerThread.FWaiterList.UnlockList; // Seen several AVs in this line, was called from TWorkerThrea.Destroy. Joachim Marder. end; - WorkerThread.WaitForValidationTermination(Tree); + if pWaitForValidationTermination then + WorkerThread.WaitForValidationTermination(pTree); end; diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 3f36d38a4..c07d8c6b5 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -2736,7 +2736,7 @@ TBaseVirtualTree = class(TCustomControl) function InternalData(Node: PVirtualNode): Pointer; procedure InternalDisconnectNode(Node: PVirtualNode; KeepFocus: Boolean; Reindex: Boolean = True; ParentClearing: Boolean = False); virtual; procedure InternalRemoveFromSelection(Node: PVirtualNode); virtual; - procedure InterruptValidation; + procedure InterruptValidation(pWaitForValidationTermination: Boolean = True); procedure InvalidateCache; procedure Loaded; override; procedure MainColumnChanged; virtual; @@ -12190,7 +12190,7 @@ destructor TBaseVirtualTree.Destroy; fAccessible := nil; end; - InterruptValidation(); + InterruptValidation(True); Exclude(FOptions.FMiscOptions, toReadOnly); // Make sure there is no reference remaining to the releasing tree. TWorkerThread.ReleaseThreadReference(); @@ -13834,17 +13834,16 @@ procedure TBaseVirtualTree.InitRootNode(OldSize: Cardinal = 0); //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InterruptValidation; +procedure TBaseVirtualTree.InterruptValidation(pWaitForValidationTermination: Boolean = True); var WasValidating: Boolean; - begin DoStateChange([tsStopValidation], [tsUseCache]); // Check the worker thread existance. It might already be gone (usually on destruction of the last tree). WasValidating := (tsValidating in FStates); - TWorkerThread.RemoveTree(Self); + TWorkerThread.RemoveTree(Self, pWaitForValidationTermination); if WasValidating then InvalidateCache(); end; @@ -25705,8 +25704,8 @@ procedure TBaseVirtualTree.ValidateCache(); // (if not already there) and signalling the thread it can start validating. begin - // Wait for thread to stop validation if it is currently validating this tree's cache. - InterruptValidation; + // stop validation if it is currently validating this tree's cache. + InterruptValidation(False); FStartIndex := 0; if (tsValidationNeeded in FStates) and (FVisibleCount > CacheThreshold) then From 84aec749f83d39b51e1051f4343c576f664c524d Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 28 Dec 2020 08:18:58 +0100 Subject: [PATCH 167/681] Fix for issue #390: Check HandleAllocated before accessing hnadle of scrollbar windows. --- Source/VirtualTrees.StyleHooks.pas | 58 ++++++++++++++++-------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index abf83c864..e1d759a06 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -369,36 +369,42 @@ procedure TVclStyleScrollBarsHook.UpdateScroll; Inc(BorderSize, GetSystemMetrics(SM_CYEDGE)); // VertScrollBarWindow - if FVertScrollWnd.Visible then + if FVertScrollWnd.HandleAllocated then begin - R := VertScrollRect; - if Control.UseRightToLeftScrollBar then - OffsetRect(R, -R.Left + BorderSize, 0); - - ShowWindow(FVertScrollWnd.Handle, SW_SHOW); - SetWindowPos(FVertScrollWnd.Handle, HWND_TOP, - Control.Left + R.Left + PaddingSize, - Control.Top + R.Top + HeaderHeight + PaddingSize, - R.Width, - Control.Height - HeaderHeight - ((PaddingSize + BorderSize) * 2), // <> R.Height - SWP_SHOWWINDOW); - end else - ShowWindow(FVertScrollWnd.Handle, SW_HIDE); + if FVertScrollWnd.Visible then + begin + R := VertScrollRect; + if Control.UseRightToLeftScrollBar then + OffsetRect(R, -R.Left + BorderSize, 0); + + ShowWindow(FVertScrollWnd.Handle, SW_SHOW); + SetWindowPos(FVertScrollWnd.Handle, HWND_TOP, + Control.Left + R.Left + PaddingSize, + Control.Top + R.Top + HeaderHeight + PaddingSize, + R.Width, + Control.Height - HeaderHeight - ((PaddingSize + BorderSize) * 2), // <> R.Height + SWP_SHOWWINDOW); + end else + ShowWindow(FVertScrollWnd.Handle, SW_HIDE); + end;// if FVertScrollWnd // HorzScrollBarWindow - if FHorzScrollWnd.Visible then + if FHorzScrollWnd.HandleAllocated then begin - R := HorzScrollRect; - if Control.UseRightToLeftScrollBar then - OffsetRect(R, VertScrollRect.Width, 0); - - ShowWindow(FHorzScrollWnd.Handle, SW_SHOW); - SetWindowPos(FHorzScrollWnd.Handle, HWND_TOP, - Control.Left + R.Left + PaddingSize, - Control.Top + R.Top + HeaderHeight + PaddingSize, - R.Width, R.Height, SWP_SHOWWINDOW); - end else - ShowWindow(FHorzScrollWnd.Handle, SW_HIDE); + if FHorzScrollWnd.Visible then + begin + R := HorzScrollRect; + if Control.UseRightToLeftScrollBar then + OffsetRect(R, VertScrollRect.Width, 0); + + ShowWindow(FHorzScrollWnd.Handle, SW_SHOW); + SetWindowPos(FHorzScrollWnd.Handle, HWND_TOP, + Control.Left + R.Left + PaddingSize, + Control.Top + R.Top + HeaderHeight + PaddingSize, + R.Width, R.Height, SWP_SHOWWINDOW); + end else + ShowWindow(FHorzScrollWnd.Handle, SW_HIDE); + end;// if FHorzScrollWnd end; procedure TVclStyleScrollBarsHook.WMCaptureChanged(var Msg: TMessage); From 6687954679d16be13ecd180d7a74c51f01806a30 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 29 Dec 2020 22:12:07 +0100 Subject: [PATCH 168/681] Improved fix for issue #390: Allow automatic handle creation of scollbars if the control's handle is already allocated. --- Source/VirtualTrees.StyleHooks.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index e1d759a06..0378fd64a 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -369,7 +369,7 @@ procedure TVclStyleScrollBarsHook.UpdateScroll; Inc(BorderSize, GetSystemMetrics(SM_CYEDGE)); // VertScrollBarWindow - if FVertScrollWnd.HandleAllocated then + if Control.HandleAllocated then begin if FVertScrollWnd.Visible then begin @@ -389,7 +389,7 @@ procedure TVclStyleScrollBarsHook.UpdateScroll; end;// if FVertScrollWnd // HorzScrollBarWindow - if FHorzScrollWnd.HandleAllocated then + if Control.HandleAllocated then begin if FHorzScrollWnd.Visible then begin From 110b6d8801f6c57640f8c56ef61a9f9f4f7a9372 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 29 Dec 2020 23:15:16 +0100 Subject: [PATCH 169/681] =?UTF-8?q?Reverted=20some=20recent=20changes=20f?= =?UTF-8?q?=C3=BCr=20issue=20#1001=20that=20caused=20a=20potential=20deadl?= =?UTF-8?q?ock=20in=20case=20CheckSynchronize()=20is=20called=20nested.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/VirtualTrees.WorkerThread.pas | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index f0056d80c..679180098 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -57,7 +57,7 @@ class procedure TWorkerThread.EnsureCreated(); WorkerThread := TWorkerThread.Create(); end; -class procedure TWorkerThread.Dispose; +class procedure TWorkerThread.Dispose(CanBlock: Boolean); var LRef: TThread; begin @@ -168,14 +168,14 @@ procedure TWorkerThread.Execute(); if Assigned(lCurrentTree) then begin try - FCurrentTree := lCurrentTree; TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync([tsValidating], [tsUseCache, tsValidationNeeded]); + FCurrentTree := lCurrentTree; EnterStates := []; if not (tsStopValidation in FCurrentTree.TreeStates) and TBaseVirtualTreeCracker(FCurrentTree).DoValidateCache then EnterStates := [tsUseCache]; finally + FCurrentTree := nil; // Important: Clear variable before calling ChangeTreeStatesAsync() to prevent deadlock in WaitForValidationTermination(). See issue #1001 TBaseVirtualTreeCracker(lCurrentTree).ChangeTreeStatesAsync(EnterStates, [tsValidating, tsStopValidation]); - FCurrentTree := nil; // Important: Clear variable before calling ChangeTreeStatesAsync() to prevent deadlock in WaitForValidationTermination() end; end; except From 7bbd6daabc4f18d608c090123999f13b82e02431 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 29 Dec 2020 23:19:14 +0100 Subject: [PATCH 170/681] * Fixed mem leak reported in issue #1001 * Partial fix for issue #1013: Changing the Check State from InitNode may leave the tree in "updating" state. --- Source/VirtualTrees.pas | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index c07d8c6b5..d36bf2cc7 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -12178,7 +12178,8 @@ constructor TBaseVirtualTree.Create(AOwner: TComponent); //---------------------------------------------------------------------------------------------------------------------- destructor TBaseVirtualTree.Destroy; - +var + WasValidating: Boolean; begin // Disconnect all remote MSAA connections if Assigned(FAccessibleItem) then begin @@ -12190,7 +12191,10 @@ destructor TBaseVirtualTree.Destroy; fAccessible := nil; end; + WasValidating := (tsValidating in FStates); InterruptValidation(True); + if WasValidating then + CheckSynchronize(); // Make sure to dequeue all synchronized calls from ChangeTreeStatesAsync(), fixes mem leak reported in issue #1001. Exclude(FOptions.FMiscOptions, toReadOnly); // Make sure there is no reference remaining to the releasing tree. TWorkerThread.ReleaseThreadReference(); @@ -25705,7 +25709,7 @@ procedure TBaseVirtualTree.ValidateCache(); begin // stop validation if it is currently validating this tree's cache. - InterruptValidation(False); + InterruptValidation(); FStartIndex := 0; if (tsValidationNeeded in FStates) and (FVisibleCount > CacheThreshold) then @@ -32628,7 +32632,7 @@ procedure TBaseVirtualTree.UpdateHorizontalScrollBar(DoRepaint: Boolean); begin UpdateHorizontalRange; - if (tsUpdating in FStates) or not HandleAllocated then + if (FUpdateCount > 0) or not HandleAllocated then Exit; // Adjust effect scroll offset depending on bidi mode. @@ -32735,7 +32739,7 @@ procedure TBaseVirtualTree.UpdateVerticalScrollBar(DoRepaint: Boolean); begin UpdateVerticalRange; - if tsUpdating in FStates then + if (fUpdateCount > 0) then Exit; Assert(GetCurrentThreadId = MainThreadId, 'UI controls like ' + Classname + ' and its scrollbars should only be manipulated through the main thread.'); From 717e6eadb47dfd8992a804d8fa1cdd5a624dee8f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 29 Dec 2020 23:44:11 +0100 Subject: [PATCH 171/681] Improved the workaround for issue #1001: Worst case we have two synchonized calls from ChangeTreeStatesAsync() in the queue. --- Source/VirtualTrees.pas | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index d36bf2cc7..6a8e98d45 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -12194,7 +12194,11 @@ destructor TBaseVirtualTree.Destroy; WasValidating := (tsValidating in FStates); InterruptValidation(True); if WasValidating then - CheckSynchronize(); // Make sure to dequeue all synchronized calls from ChangeTreeStatesAsync(), fixes mem leak reported in issue #1001. + begin + // Make sure we dequeue the two synchronized calls from ChangeTreeStatesAsync(), fixes mem leak and AV reported in issue #1001, but is more a workaround. + CheckSynchronize(); + CheckSynchronize(); + end;// if Exclude(FOptions.FMiscOptions, toReadOnly); // Make sure there is no reference remaining to the releasing tree. TWorkerThread.ReleaseThreadReference(); From 0793fe47a1768cc64fccd90b37a800a2cc69b165 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 3 Jan 2021 22:02:27 +0100 Subject: [PATCH 172/681] Introduced function IsUpdating which can be used instead of checking for flag tsUpdating or (fUpdateCount > 0). Issue #1013. --- Source/VirtualTrees.pas | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 6a8e98d45..26e905c57 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -3130,6 +3130,7 @@ TBaseVirtualTree = class(TCustomControl) function IsEditing: Boolean; function IsMouseSelecting: Boolean; function IsEmpty: Boolean; inline; + function IsUpdating(): Boolean; function IterateSubtree(Node: PVirtualNode; Callback: TVTGetNodeProc; Data: Pointer; Filter: TVirtualNodeStates = []; DoInit: Boolean = False; ChildNodesOnly: Boolean = False): PVirtualNode; procedure LoadFromFile(const FileName: TFileName); virtual; @@ -8431,7 +8432,7 @@ procedure TVirtualTreeColumns.Update(Item: TCollectionItem); Treeview.Invalidate; end; - if not (tsUpdating in Treeview.FStates) then + if not (Treeview.IsUpdating) then // This is mainly to let the designer know when a change occurs at design time which // doesn't involve the object inspector (like column resizing with the mouse). // This does NOT include design time code as the communication is done via an interface. @@ -19727,7 +19728,7 @@ procedure TBaseVirtualTree.DoColumnResize(Column: TColumnIndex); if [hsColumnWidthTracking, hsResizing] * FHeader.States = [hsColumnWidthTracking] then UpdateWindow(Handle); - if not (tsUpdating in FStates) then + if not (IsUpdating) then UpdateDesigner; // design time only if Assigned(FOnColumnResize) and not (hsResizing in FHeader.States) then @@ -23280,7 +23281,7 @@ procedure TBaseVirtualTree.InternalClearSelection(); // It is possible that there are invalid node references in the selection array // if the tree update is locked and changes in the structure were made. // Handle this potentially dangerous situation by packing the selection array explicitely. - if FUpdateCount > 0 then + if IsUpdating then begin Count := PackArray(FSelection, FSelectionCount); if Count > -1 then @@ -30019,6 +30020,12 @@ function TBaseVirtualTree.IsMouseSelecting: Boolean; Result := (tsDrawSelPending in FStates) or (tsDrawSelecting in FStates); end; +function TBaseVirtualTree.IsUpdating: Boolean; +// The tree does currently not update its window because a BeginUpdate has not yet ended. +begin + Exit(UpdateCount > 0); +end; + //---------------------------------------------------------------------------------------------------------------------- function TBaseVirtualTree.IterateSubtree(Node: PVirtualNode; Callback: TVTGetNodeProc; Data: Pointer; @@ -32636,7 +32643,7 @@ procedure TBaseVirtualTree.UpdateHorizontalScrollBar(DoRepaint: Boolean); begin UpdateHorizontalRange; - if (FUpdateCount > 0) or not HandleAllocated then + if IsUpdating or not HandleAllocated then Exit; // Adjust effect scroll offset depending on bidi mode. @@ -32743,7 +32750,7 @@ procedure TBaseVirtualTree.UpdateVerticalScrollBar(DoRepaint: Boolean); begin UpdateVerticalRange; - if (fUpdateCount > 0) then + if (IsUpdating) then Exit; Assert(GetCurrentThreadId = MainThreadId, 'UI controls like ' + Classname + ' and its scrollbars should only be manipulated through the main thread.'); From 31b90d54857b228bf618e32e0771a47c19ee1f55 Mon Sep 17 00:00:00 2001 From: Daniel Trierweiler Date: Thu, 7 Jan 2021 11:29:14 +0100 Subject: [PATCH 173/681] Recreate the two TScrollWindow's if their handle has been destroyed. This may happen, when the parent-handle has been destroyed/recreated after changing to a system-style (which doesn't use the sfHandleMessages flag), and then activate a custom style (with sfHandleMessages) again. Note: This code is basically copied from the base class (Delphi 10.4.1, Vcl.Forms.TScrollingStyleHook.UpdateScroll). #390 --- Source/VirtualTrees.StyleHooks.pas | 31 ++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index 0378fd64a..1a28cde1b 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -86,6 +86,8 @@ TScrollWindow = class(TWinControl) procedure WMSize(var Msg: TMessage); message WM_SIZE; procedure WMMove(var Msg: TMessage); message WM_MOVE; procedure WMPosChanged(var Msg: TMessage); message WM_WINDOWPOSCHANGED; + + procedure InitScrollBars; protected procedure CalcScrollBarsRect; virtual; procedure DrawHorzScrollBar(DC: HDC); virtual; @@ -161,12 +163,7 @@ procedure TVclStyleScrollBarsHook.CalcScrollBarsRect; constructor TVclStyleScrollBarsHook.Create(AControl: TWinControl); begin inherited; - FVertScrollWnd := TScrollWindow.CreateParented(GetParent(Control.Handle)); - FVertScrollWnd.StyleHook := Self; - FVertScrollWnd.Vertical := True; - - FHorzScrollWnd := TScrollWindow.CreateParented(GetParent(Control.Handle)); - FHorzScrollWnd.StyleHook := Self; + InitScrollBars; VertSliderState := tsThumbBtnVertNormal; VertUpState := tsArrowBtnUpNormal; @@ -176,6 +173,16 @@ constructor TVclStyleScrollBarsHook.Create(AControl: TWinControl); HorzDownState := tsArrowBtnRightNormal; end; +procedure TVclStyleScrollBarsHook.InitScrollBars; +begin + FVertScrollWnd := TScrollWindow.CreateParented(GetParent(Control.Handle)); + FVertScrollWnd.StyleHook := Self; + FVertScrollWnd.Vertical := True; + + FHorzScrollWnd := TScrollWindow.CreateParented(GetParent(Control.Handle)); + FHorzScrollWnd.StyleHook := Self; +end; + destructor TVclStyleScrollBarsHook.Destroy; begin FVertScrollWnd.StyleHook := nil; @@ -348,6 +355,18 @@ procedure TVclStyleScrollBarsHook.UpdateScroll; PaddingSize: Integer; BorderSize: Integer; begin + if ((FVertScrollWnd <> nil) and not FVertScrollWnd.HandleAllocated) or + ((FHorzScrollWnd <> nil) and not FHorzScrollWnd.HandleAllocated) + then + begin + if FVertScrollWnd <> nil then + FreeAndNil(FVertScrollWnd); + if FHorzScrollWnd <> nil then + FreeAndNil(FHorzScrollWnd); + + InitScrollBars; + end; + // ScrollBarWindow Visible/Enabled Control CalcScrollBarsRect; From 99968b8cfd099ae847ab6a9a91a56c59ae40f4d2 Mon Sep 17 00:00:00 2001 From: Daniel Trierweiler Date: Thu, 7 Jan 2021 12:18:10 +0100 Subject: [PATCH 174/681] * Unified line breaks --- Source/VirtualTrees.StyleHooks.pas | 1904 ++++++++++++++-------------- 1 file changed, 952 insertions(+), 952 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index 1a28cde1b..ea6afc5f3 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -1,109 +1,109 @@ -unit VirtualTrees.StyleHooks; - -// The contents of this file are subject to the Mozilla Public License -// Version 1.1 (the "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ -// -// Alternatively, you may redistribute this library, use and/or modify it under the terms of the -// GNU Lesser General Public License as published by the Free Software Foundation; -// either version 2.1 of the License, or (at your option) any later version. -// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/. -// -// Software distributed under the License is distributed on an "AS IS" basis, -// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the -// specific language governing rights and limitations under the License. -// -// The original code is VirtualTrees.pas, released September 30, 2000. -// -// The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de), -// written by Mike Lischke (public@soft-gems.net, www.soft-gems.net). -// -// Portions created by digital publishing AG are Copyright -// (C) 1999-2001 digital publishing AG. All Rights Reserved. -//---------------------------------------------------------------------------------------------------------------------- - - -interface - -{$WARN UNSAFE_TYPE OFF} -{$WARN UNSAFE_CAST OFF} -{$WARN UNSAFE_CODE OFF} - -uses - Winapi.Windows, - Winapi.Messages, - Winapi.UxTheme, - - System.Classes, - Vcl.Themes, - Vcl.Forms, - Vcl.Controls; - -const - CM_UPDATE_VCLSTYLE_SCROLLBARS = CM_BASE + 2050; - -type - // XE2+ VCL Style - TVclStyleScrollBarsHook = class(TScrollingStyleHook) - strict private type - {$REGION 'TVclStyleScrollBarWindow'} - TScrollWindow = class(TWinControl) - strict private - FStyleHook: TVclStyleScrollBarsHook; - FVertical: Boolean; - procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST; - procedure WMEraseBkgnd(var Msg: TMessage); message WM_ERASEBKGND; - procedure WMPaint(var Msg: TWMPaint); message WM_PAINT; - public - constructor Create(AOwner: TComponent); override; - property StyleHook: TVclStyleScrollBarsHook read FStyleHook write FStyleHook; - property Vertical: Boolean read FVertical write FVertical; - end; - {$ENDREGION} - private - FHorzScrollWnd: TScrollWindow; - FLeftMouseButtonDown: Boolean; - FVertScrollWnd: TScrollWindow; - - function NCMousePosToClient(const P: TPoint): TPoint; - - procedure CMUpdateVclStyleScrollbars(var Msg: TMessage); message CM_UPDATE_VCLSTYLE_SCROLLBARS; - procedure WMEraseBkgnd(var Msg: TWMEraseBkgnd); message WM_ERASEBKGND; - procedure WMKeyDown(var Msg: TMessage); message WM_KEYDOWN; - procedure WMKeyUp(var Msg: TMessage); message WM_KEYUP; - procedure WMLButtonDown(var Msg: TWMMouse); message WM_LBUTTONDOWN; - procedure WMLButtonUp(var Msg: TWMMouse); message WM_LBUTTONUP; - procedure WMNCLButtonDown(var Msg: TWMMouse); message WM_NCLBUTTONDOWN; - procedure WMNCMouseMove(var Msg: TWMMouse); message WM_NCMOUSEMOVE; - procedure WMNCLButtonUp(var Msg: TWMMouse); message WM_NCLBUTTONUP; - procedure WMNCPaint(var Msg: TMessage); message WM_NCPAINT; - procedure WMMouseMove(var Msg: TWMMouse); message WM_MOUSEMOVE; - procedure WMMouseWheel(var Msg: TMessage); message WM_MOUSEWHEEL; - procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL; - procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL; - procedure WMCaptureChanged(var Msg: TMessage); message WM_CAPTURECHANGED; - procedure WMNCLButtonDblClk(var Msg: TWMMouse); message WM_NCLBUTTONDBLCLK; - procedure WMSize(var Msg: TMessage); message WM_SIZE; - procedure WMMove(var Msg: TMessage); message WM_MOVE; - procedure WMPosChanged(var Msg: TMessage); message WM_WINDOWPOSCHANGED; - - procedure InitScrollBars; - protected - procedure CalcScrollBarsRect; virtual; - procedure DrawHorzScrollBar(DC: HDC); virtual; - procedure DrawVertScrollBar(DC: HDC); virtual; - procedure MouseLeave; override; - procedure PaintScroll; override; - function PointInTreeHeader(const P: TPoint): Boolean; - procedure UpdateScroll;{$if CompilerVersion >= 34}override;{$ifend} - public - constructor Create(AControl: TWinControl); override; - destructor Destroy; override; - property HorzScrollRect; - property VertScrollRect; - end; - -type +unit VirtualTrees.StyleHooks; + +// The contents of this file are subject to the Mozilla Public License +// Version 1.1 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ +// +// Alternatively, you may redistribute this library, use and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation; +// either version 2.1 of the License, or (at your option) any later version. +// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/. +// +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +// specific language governing rights and limitations under the License. +// +// The original code is VirtualTrees.pas, released September 30, 2000. +// +// The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de), +// written by Mike Lischke (public@soft-gems.net, www.soft-gems.net). +// +// Portions created by digital publishing AG are Copyright +// (C) 1999-2001 digital publishing AG. All Rights Reserved. +//---------------------------------------------------------------------------------------------------------------------- + + +interface + +{$WARN UNSAFE_TYPE OFF} +{$WARN UNSAFE_CAST OFF} +{$WARN UNSAFE_CODE OFF} + +uses + Winapi.Windows, + Winapi.Messages, + Winapi.UxTheme, + + System.Classes, + Vcl.Themes, + Vcl.Forms, + Vcl.Controls; + +const + CM_UPDATE_VCLSTYLE_SCROLLBARS = CM_BASE + 2050; + +type + // XE2+ VCL Style + TVclStyleScrollBarsHook = class(TScrollingStyleHook) + strict private type + {$REGION 'TVclStyleScrollBarWindow'} + TScrollWindow = class(TWinControl) + strict private + FStyleHook: TVclStyleScrollBarsHook; + FVertical: Boolean; + procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST; + procedure WMEraseBkgnd(var Msg: TMessage); message WM_ERASEBKGND; + procedure WMPaint(var Msg: TWMPaint); message WM_PAINT; + public + constructor Create(AOwner: TComponent); override; + property StyleHook: TVclStyleScrollBarsHook read FStyleHook write FStyleHook; + property Vertical: Boolean read FVertical write FVertical; + end; + {$ENDREGION} + private + FHorzScrollWnd: TScrollWindow; + FLeftMouseButtonDown: Boolean; + FVertScrollWnd: TScrollWindow; + + function NCMousePosToClient(const P: TPoint): TPoint; + + procedure CMUpdateVclStyleScrollbars(var Msg: TMessage); message CM_UPDATE_VCLSTYLE_SCROLLBARS; + procedure WMEraseBkgnd(var Msg: TWMEraseBkgnd); message WM_ERASEBKGND; + procedure WMKeyDown(var Msg: TMessage); message WM_KEYDOWN; + procedure WMKeyUp(var Msg: TMessage); message WM_KEYUP; + procedure WMLButtonDown(var Msg: TWMMouse); message WM_LBUTTONDOWN; + procedure WMLButtonUp(var Msg: TWMMouse); message WM_LBUTTONUP; + procedure WMNCLButtonDown(var Msg: TWMMouse); message WM_NCLBUTTONDOWN; + procedure WMNCMouseMove(var Msg: TWMMouse); message WM_NCMOUSEMOVE; + procedure WMNCLButtonUp(var Msg: TWMMouse); message WM_NCLBUTTONUP; + procedure WMNCPaint(var Msg: TMessage); message WM_NCPAINT; + procedure WMMouseMove(var Msg: TWMMouse); message WM_MOUSEMOVE; + procedure WMMouseWheel(var Msg: TMessage); message WM_MOUSEWHEEL; + procedure WMVScroll(var Msg: TWMVScroll); message WM_VSCROLL; + procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL; + procedure WMCaptureChanged(var Msg: TMessage); message WM_CAPTURECHANGED; + procedure WMNCLButtonDblClk(var Msg: TWMMouse); message WM_NCLBUTTONDBLCLK; + procedure WMSize(var Msg: TMessage); message WM_SIZE; + procedure WMMove(var Msg: TMessage); message WM_MOVE; + procedure WMPosChanged(var Msg: TMessage); message WM_WINDOWPOSCHANGED; + + procedure InitScrollBars; + protected + procedure CalcScrollBarsRect; virtual; + procedure DrawHorzScrollBar(DC: HDC); virtual; + procedure DrawVertScrollBar(DC: HDC); virtual; + procedure MouseLeave; override; + procedure PaintScroll; override; + function PointInTreeHeader(const P: TPoint): Boolean; + procedure UpdateScroll;{$if CompilerVersion >= 34}override;{$ifend} + public + constructor Create(AControl: TWinControl); override; + destructor Destroy; override; + property HorzScrollRect; + property VertScrollRect; + end; + +type /// prototype for the global callback VTStyleServicesFunc. TVTStyleServicesFunc = function (AControl: TControl = nil): TCustomStyleServices; @@ -113,249 +113,249 @@ TScrollWindow = class(TWinControl) /// Needed for IDE plugins. See pull request #1011 VTStyleServicesFunc: TVTStyleServicesFunc = nil; - -implementation - -uses - System.SysUtils, - System.Math, - System.Types, - Vcl.Graphics, - VirtualTrees; - -type - TBaseVirtualTreeCracker = class(TBaseVirtualTree) - end; - - -// XE2+ VCL Style -{ TVclStyleScrollBarsHook } - -procedure TVclStyleScrollBarsHook.CalcScrollBarsRect; - - procedure CalcVerticalRects; - var - BarInfo: TScrollBarInfo; - Ret: BOOL; - begin - BarInfo.cbSize := SizeOf(BarInfo); - Ret := GetScrollBarInfo(Handle, Integer(OBJID_VSCROLL), BarInfo); - FVertScrollWnd.Visible := (seBorder in Control.StyleElements) and Ret and (not (STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0)); - FVertScrollWnd.Enabled := FVertScrollWnd.Visible and (not (STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0)); - end; - - procedure CalcHorizontalRects; - var - BarInfo: TScrollBarInfo; - Ret: BOOL; - begin - BarInfo.cbSize := SizeOf(BarInfo); - Ret := GetScrollBarInfo(Handle, Integer(OBJID_HSCROLL), BarInfo); - FHorzScrollWnd.Visible := (seBorder in Control.StyleElements) and Ret and (not (STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0)); - FHorzScrollWnd.Enabled := FHorzScrollWnd.Visible and (not (STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0)); - end; - -begin - CalcVerticalRects; - CalcHorizontalRects; -end; - -constructor TVclStyleScrollBarsHook.Create(AControl: TWinControl); -begin - inherited; - InitScrollBars; - - VertSliderState := tsThumbBtnVertNormal; - VertUpState := tsArrowBtnUpNormal; - VertDownState := tsArrowBtnDownNormal; - HorzSliderState := tsThumbBtnHorzNormal; - HorzUpState := tsArrowBtnLeftNormal; - HorzDownState := tsArrowBtnRightNormal; -end; - -procedure TVclStyleScrollBarsHook.InitScrollBars; + +implementation + +uses + System.SysUtils, + System.Math, + System.Types, + Vcl.Graphics, + VirtualTrees; + +type + TBaseVirtualTreeCracker = class(TBaseVirtualTree) + end; + + +// XE2+ VCL Style +{ TVclStyleScrollBarsHook } + +procedure TVclStyleScrollBarsHook.CalcScrollBarsRect; + + procedure CalcVerticalRects; + var + BarInfo: TScrollBarInfo; + Ret: BOOL; + begin + BarInfo.cbSize := SizeOf(BarInfo); + Ret := GetScrollBarInfo(Handle, Integer(OBJID_VSCROLL), BarInfo); + FVertScrollWnd.Visible := (seBorder in Control.StyleElements) and Ret and (not (STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0)); + FVertScrollWnd.Enabled := FVertScrollWnd.Visible and (not (STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0)); + end; + + procedure CalcHorizontalRects; + var + BarInfo: TScrollBarInfo; + Ret: BOOL; + begin + BarInfo.cbSize := SizeOf(BarInfo); + Ret := GetScrollBarInfo(Handle, Integer(OBJID_HSCROLL), BarInfo); + FHorzScrollWnd.Visible := (seBorder in Control.StyleElements) and Ret and (not (STATE_SYSTEM_INVISIBLE and BarInfo.rgstate[0] <> 0)); + FHorzScrollWnd.Enabled := FHorzScrollWnd.Visible and (not (STATE_SYSTEM_UNAVAILABLE and BarInfo.rgstate[0] <> 0)); + end; + +begin + CalcVerticalRects; + CalcHorizontalRects; +end; + +constructor TVclStyleScrollBarsHook.Create(AControl: TWinControl); +begin + inherited; + InitScrollBars; + + VertSliderState := tsThumbBtnVertNormal; + VertUpState := tsArrowBtnUpNormal; + VertDownState := tsArrowBtnDownNormal; + HorzSliderState := tsThumbBtnHorzNormal; + HorzUpState := tsArrowBtnLeftNormal; + HorzDownState := tsArrowBtnRightNormal; +end; + +procedure TVclStyleScrollBarsHook.InitScrollBars; begin FVertScrollWnd := TScrollWindow.CreateParented(GetParent(Control.Handle)); - FVertScrollWnd.StyleHook := Self; - FVertScrollWnd.Vertical := True; - - FHorzScrollWnd := TScrollWindow.CreateParented(GetParent(Control.Handle)); - FHorzScrollWnd.StyleHook := Self; -end; - -destructor TVclStyleScrollBarsHook.Destroy; -begin - FVertScrollWnd.StyleHook := nil; - FreeAndNil(FVertScrollWnd); - FHorzScrollWnd.StyleHook := nil; - FreeAndNil(FHorzScrollWnd); - inherited; -end; - -procedure TVclStyleScrollBarsHook.DrawHorzScrollBar(DC: HDC); -var - B: TBitmap; - Details: TThemedElementDetails; - R: TRect; -begin - if ((Handle = 0) or (DC = 0)) then - Exit; - - if FHorzScrollWnd.Visible and StyleServices.Available and (seBorder in Control.StyleElements) then - begin - B := TBitmap.Create; - try - R := HorzScrollRect; - B.Width := R.Width; - B.Height := R.Height; - MoveWindowOrg(B.Canvas.Handle, -R.Left, -R.Top); - - R.Left := HorzUpButtonRect.Right; - R.Right := HorzDownButtonRect.Left; - Details := StyleServices.GetElementDetails(tsUpperTrackHorzNormal); - StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); - - if FHorzScrollWnd.Enabled then - Details := StyleServices.GetElementDetails(HorzSliderState); - StyleServices.DrawElement(B.Canvas.Handle, Details, HorzSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); - - if FHorzScrollWnd.Enabled then - Details := StyleServices.GetElementDetails(HorzUpState) - else - Details := StyleServices.GetElementDetails(tsArrowBtnLeftDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, HorzUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); - - if FHorzScrollWnd.Enabled then - Details := StyleServices.GetElementDetails(HorzDownState) - else - Details := StyleServices.GetElementDetails(tsArrowBtnRightDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, HorzDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); - - R := HorzScrollRect; - MoveWindowOrg(B.Canvas.Handle, R.Left, R.Top); - BitBlt(DC, R.Left, R.Top, B.Width, B.Height, B.Canvas.Handle, 0, 0, SRCCOPY); - finally - B.Free; - end; - end; -end; - -procedure TVclStyleScrollBarsHook.DrawVertScrollBar(DC: HDC); -var - B: TBitmap; - Details: TThemedElementDetails; - R: TRect; -begin - if ((Handle = 0) or (DC = 0)) then - Exit; - - if FVertScrollWnd.Visible and StyleServices.Available and (seBorder in Control.StyleElements) then - begin - B := TBitmap.Create; - try - R := VertScrollRect; - B.Width := R.Width; - B.Height := FVertScrollWnd.Height; // <> R.Height - MoveWindowOrg(B.Canvas.Handle, -R.Left, -R.Top); - - R.Bottom := B.Height + R.Top; - Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal); - StyleServices.DrawElement(B.Canvas.Handle, Details, R {$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); - - R.Top := VertUpButtonRect.Bottom; - R.Bottom := VertDownButtonRect.Top; - Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal); - StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); - - if FVertScrollWnd.Enabled then - Details := StyleServices.GetElementDetails(VertSliderState); - StyleServices.DrawElement(B.Canvas.Handle, Details, VertSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); - - if FVertScrollWnd.Enabled then - Details := StyleServices.GetElementDetails(VertUpState) - else - Details := StyleServices.GetElementDetails(tsArrowBtnUpDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, VertUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); - - if FVertScrollWnd.Enabled then - Details := StyleServices.GetElementDetails(VertDownState) - else - Details := StyleServices.GetElementDetails(tsArrowBtnDownDisabled); - StyleServices.DrawElement(B.Canvas.Handle, Details, VertDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); - - R := VertScrollRect; - MoveWindowOrg(B.Canvas.Handle, R.Left, R.Top); - BitBlt(DC, R.Left, R.Top, B.Width, B.Height, B.Canvas.Handle, 0, 0, SRCCOPY); - finally - B.Free; - end; - end; -end; - -procedure TVclStyleScrollBarsHook.MouseLeave; -begin - inherited; - if VertSliderState = tsThumbBtnVertHot then - VertSliderState := tsThumbBtnVertNormal; - - if HorzSliderState = tsThumbBtnHorzHot then - HorzSliderState := tsThumbBtnHorzNormal; - - if VertUpState = tsArrowBtnUpHot then - VertUpState := tsArrowBtnUpNormal; - - if VertDownState = tsArrowBtnDownHot then - VertDownState := tsArrowBtnDownNormal; - - if HorzUpState = tsArrowBtnLeftHot then - HorzUpState := tsArrowBtnLeftNormal; - - if HorzDownState = tsArrowBtnRightHot then - HorzDownState := tsArrowBtnRightNormal; - - PaintScroll; -end; - -function TVclStyleScrollBarsHook.NCMousePosToClient(const P: TPoint): TPoint; -begin - Result := P; - ScreenToClient(Handle, Result); - if HasBorder then - begin - if HasClientEdge then - Result.Offset(2, 2) - else - Result.Offset(1, 1); - end; -end; - -procedure TVclStyleScrollBarsHook.PaintScroll; -begin - if FVertScrollWnd.HandleAllocated then - begin - FVertScrollWnd.Repaint; - RedrawWindow(FVertScrollWnd.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 - end; - if FHorzScrollWnd.HandleAllocated then - begin - FHorzScrollWnd.Repaint; - RedrawWindow(FHorzScrollWnd.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 - end; -end; - -function TVclStyleScrollBarsHook.PointInTreeHeader(const P: TPoint): Boolean; -begin - Result := TBaseVirtualTree(Control).Header.InHeader(P); -end; - -procedure TVclStyleScrollBarsHook.UpdateScroll; -var - R: TRect; - HeaderHeight: Integer; - PaddingSize: Integer; - BorderSize: Integer; -begin - if ((FVertScrollWnd <> nil) and not FVertScrollWnd.HandleAllocated) or + FVertScrollWnd.StyleHook := Self; + FVertScrollWnd.Vertical := True; + + FHorzScrollWnd := TScrollWindow.CreateParented(GetParent(Control.Handle)); + FHorzScrollWnd.StyleHook := Self; +end; + +destructor TVclStyleScrollBarsHook.Destroy; +begin + FVertScrollWnd.StyleHook := nil; + FreeAndNil(FVertScrollWnd); + FHorzScrollWnd.StyleHook := nil; + FreeAndNil(FHorzScrollWnd); + inherited; +end; + +procedure TVclStyleScrollBarsHook.DrawHorzScrollBar(DC: HDC); +var + B: TBitmap; + Details: TThemedElementDetails; + R: TRect; +begin + if ((Handle = 0) or (DC = 0)) then + Exit; + + if FHorzScrollWnd.Visible and StyleServices.Available and (seBorder in Control.StyleElements) then + begin + B := TBitmap.Create; + try + R := HorzScrollRect; + B.Width := R.Width; + B.Height := R.Height; + MoveWindowOrg(B.Canvas.Handle, -R.Left, -R.Top); + + R.Left := HorzUpButtonRect.Right; + R.Right := HorzDownButtonRect.Left; + Details := StyleServices.GetElementDetails(tsUpperTrackHorzNormal); + StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); + + if FHorzScrollWnd.Enabled then + Details := StyleServices.GetElementDetails(HorzSliderState); + StyleServices.DrawElement(B.Canvas.Handle, Details, HorzSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); + + if FHorzScrollWnd.Enabled then + Details := StyleServices.GetElementDetails(HorzUpState) + else + Details := StyleServices.GetElementDetails(tsArrowBtnLeftDisabled); + StyleServices.DrawElement(B.Canvas.Handle, Details, HorzUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); + + if FHorzScrollWnd.Enabled then + Details := StyleServices.GetElementDetails(HorzDownState) + else + Details := StyleServices.GetElementDetails(tsArrowBtnRightDisabled); + StyleServices.DrawElement(B.Canvas.Handle, Details, HorzDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); + + R := HorzScrollRect; + MoveWindowOrg(B.Canvas.Handle, R.Left, R.Top); + BitBlt(DC, R.Left, R.Top, B.Width, B.Height, B.Canvas.Handle, 0, 0, SRCCOPY); + finally + B.Free; + end; + end; +end; + +procedure TVclStyleScrollBarsHook.DrawVertScrollBar(DC: HDC); +var + B: TBitmap; + Details: TThemedElementDetails; + R: TRect; +begin + if ((Handle = 0) or (DC = 0)) then + Exit; + + if FVertScrollWnd.Visible and StyleServices.Available and (seBorder in Control.StyleElements) then + begin + B := TBitmap.Create; + try + R := VertScrollRect; + B.Width := R.Width; + B.Height := FVertScrollWnd.Height; // <> R.Height + MoveWindowOrg(B.Canvas.Handle, -R.Left, -R.Top); + + R.Bottom := B.Height + R.Top; + Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal); + StyleServices.DrawElement(B.Canvas.Handle, Details, R {$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); + + R.Top := VertUpButtonRect.Bottom; + R.Bottom := VertDownButtonRect.Top; + Details := StyleServices.GetElementDetails(tsUpperTrackVertNormal); + StyleServices.DrawElement(B.Canvas.Handle, Details, R{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); + + if FVertScrollWnd.Enabled then + Details := StyleServices.GetElementDetails(VertSliderState); + StyleServices.DrawElement(B.Canvas.Handle, Details, VertSliderRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); + + if FVertScrollWnd.Enabled then + Details := StyleServices.GetElementDetails(VertUpState) + else + Details := StyleServices.GetElementDetails(tsArrowBtnUpDisabled); + StyleServices.DrawElement(B.Canvas.Handle, Details, VertUpButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); + + if FVertScrollWnd.Enabled then + Details := StyleServices.GetElementDetails(VertDownState) + else + Details := StyleServices.GetElementDetails(tsArrowBtnDownDisabled); + StyleServices.DrawElement(B.Canvas.Handle, Details, VertDownButtonRect{$IF CompilerVersion >= 34}, nil, FVertScrollWnd.CurrentPPI{$IFEND}); + + R := VertScrollRect; + MoveWindowOrg(B.Canvas.Handle, R.Left, R.Top); + BitBlt(DC, R.Left, R.Top, B.Width, B.Height, B.Canvas.Handle, 0, 0, SRCCOPY); + finally + B.Free; + end; + end; +end; + +procedure TVclStyleScrollBarsHook.MouseLeave; +begin + inherited; + if VertSliderState = tsThumbBtnVertHot then + VertSliderState := tsThumbBtnVertNormal; + + if HorzSliderState = tsThumbBtnHorzHot then + HorzSliderState := tsThumbBtnHorzNormal; + + if VertUpState = tsArrowBtnUpHot then + VertUpState := tsArrowBtnUpNormal; + + if VertDownState = tsArrowBtnDownHot then + VertDownState := tsArrowBtnDownNormal; + + if HorzUpState = tsArrowBtnLeftHot then + HorzUpState := tsArrowBtnLeftNormal; + + if HorzDownState = tsArrowBtnRightHot then + HorzDownState := tsArrowBtnRightNormal; + + PaintScroll; +end; + +function TVclStyleScrollBarsHook.NCMousePosToClient(const P: TPoint): TPoint; +begin + Result := P; + ScreenToClient(Handle, Result); + if HasBorder then + begin + if HasClientEdge then + Result.Offset(2, 2) + else + Result.Offset(1, 1); + end; +end; + +procedure TVclStyleScrollBarsHook.PaintScroll; +begin + if FVertScrollWnd.HandleAllocated then + begin + FVertScrollWnd.Repaint; + RedrawWindow(FVertScrollWnd.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 + end; + if FHorzScrollWnd.HandleAllocated then + begin + FHorzScrollWnd.Repaint; + RedrawWindow(FHorzScrollWnd.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 + end; +end; + +function TVclStyleScrollBarsHook.PointInTreeHeader(const P: TPoint): Boolean; +begin + Result := TBaseVirtualTree(Control).Header.InHeader(P); +end; + +procedure TVclStyleScrollBarsHook.UpdateScroll; +var + R: TRect; + HeaderHeight: Integer; + PaddingSize: Integer; + BorderSize: Integer; +begin + if ((FVertScrollWnd <> nil) and not FVertScrollWnd.HandleAllocated) or ((FHorzScrollWnd <> nil) and not FHorzScrollWnd.HandleAllocated) then begin @@ -366,608 +366,608 @@ procedure TVclStyleScrollBarsHook.UpdateScroll; InitScrollBars; end; - - // ScrollBarWindow Visible/Enabled Control - CalcScrollBarsRect; - - HeaderHeight := 0; - if (hoVisible in TBaseVirtualTree(Control).Header.Options) then - Inc(HeaderHeight, TBaseVirtualTree(Control).Header.Height); - - PaddingSize := TBaseVirtualTreeCracker(Control).BorderWidth; - if TBaseVirtualTreeCracker(Control).BevelKind <> bkNone then - begin - if TBaseVirtualTreeCracker(Control).BevelInner <> bvNone then - Inc(PaddingSize, TBaseVirtualTreeCracker(Control).BevelWidth); - if TBaseVirtualTreeCracker(Control).BevelOuter <> bvNone then - Inc(PaddingSize, TBaseVirtualTreeCracker(Control).BevelWidth); - end; - - BorderSize := 0; - if HasBorder then - Inc(BorderSize, GetSystemMetrics(SM_CYEDGE)); - - // VertScrollBarWindow - if Control.HandleAllocated then - begin - if FVertScrollWnd.Visible then - begin - R := VertScrollRect; - if Control.UseRightToLeftScrollBar then - OffsetRect(R, -R.Left + BorderSize, 0); - - ShowWindow(FVertScrollWnd.Handle, SW_SHOW); - SetWindowPos(FVertScrollWnd.Handle, HWND_TOP, - Control.Left + R.Left + PaddingSize, - Control.Top + R.Top + HeaderHeight + PaddingSize, - R.Width, - Control.Height - HeaderHeight - ((PaddingSize + BorderSize) * 2), // <> R.Height - SWP_SHOWWINDOW); - end else - ShowWindow(FVertScrollWnd.Handle, SW_HIDE); - end;// if FVertScrollWnd - - // HorzScrollBarWindow - if Control.HandleAllocated then - begin - if FHorzScrollWnd.Visible then - begin - R := HorzScrollRect; - if Control.UseRightToLeftScrollBar then - OffsetRect(R, VertScrollRect.Width, 0); - - ShowWindow(FHorzScrollWnd.Handle, SW_SHOW); - SetWindowPos(FHorzScrollWnd.Handle, HWND_TOP, - Control.Left + R.Left + PaddingSize, - Control.Top + R.Top + HeaderHeight + PaddingSize, - R.Width, R.Height, SWP_SHOWWINDOW); - end else - ShowWindow(FHorzScrollWnd.Handle, SW_HIDE); - end;// if FHorzScrollWnd -end; - -procedure TVclStyleScrollBarsHook.WMCaptureChanged(var Msg: TMessage); -begin - if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then - begin - if VertUpState = tsArrowBtnUpPressed then - begin - VertUpState := tsArrowBtnUpNormal; - PaintScroll; - end; - - if VertDownState = tsArrowBtnDownPressed then - begin - VertDownState := tsArrowBtnDownNormal; - PaintScroll; - end; - end; - - if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then - begin - if HorzUpState = tsArrowBtnLeftPressed then - begin - HorzUpState := tsArrowBtnLeftNormal; - PaintScroll; - end; - - if HorzDownState = tsArrowBtnRightPressed then - begin - HorzDownState := tsArrowBtnRightNormal; - PaintScroll; - end; - end; - - CallDefaultProc(TMessage(Msg)); - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMEraseBkgnd(var Msg: TWMEraseBkgnd); -begin - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMHScroll(var Msg: TWMHScroll); -begin - CallDefaultProc(TMessage(Msg)); - if not (Msg.ScrollCode in [SB_THUMBTRACK, SB_THUMBPOSITION]) then - UpdateScroll - else - PaintScroll; - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.CMUpdateVclStyleScrollbars(var Msg: TMessage); -begin - CalcScrollBarsRect; - PaintScroll; -end; - -procedure TVclStyleScrollBarsHook.WMKeyDown(var Msg: TMessage); -begin - CallDefaultProc(TMessage(Msg)); - UpdateScroll; - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMKeyUp(var Msg: TMessage); -begin - CallDefaultProc(TMessage(Msg)); - PaintScroll; - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMLButtonDown(var Msg: TWMMouse); -begin - CallDefaultProc(TMessage(Msg)); - UpdateScroll; - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMLButtonUp(var Msg: TWMMouse); -var - P: TPoint; -begin - P := Point(Msg.XPos, Msg.YPos); - ScreenToClient(Handle, P); - if not PointInTreeHeader(P) then - begin - if FVertScrollWnd.Visible then - begin - if VertSliderState = tsThumbBtnVertPressed then - begin - PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_ENDSCROLL, 0)), 0); - FLeftMouseButtonDown := False; - VertSliderState := tsThumbBtnVertNormal; - PaintScroll; - Handled := True; - Mouse.Capture := 0; - Exit; - end else - if VertUpState = tsArrowBtnUpPressed then - VertUpState := tsArrowBtnUpNormal - else if VertDownState = tsArrowBtnDownPressed then - VertDownState := tsArrowBtnDownNormal; - end; - - if FHorzScrollWnd.Visible then - begin - if HorzSliderState = tsThumbBtnHorzPressed then - begin - PostMessage(Handle, WM_HSCROLL, Integer(SmallPoint(SB_ENDSCROLL, 0)), 0); - FLeftMouseButtonDown := False; - HorzSliderState := tsThumbBtnHorzNormal; - PaintScroll; - Handled := True; - Mouse.Capture := 0; - Exit; - end else - if HorzUpState = tsArrowBtnLeftPressed then - HorzUpState := tsArrowBtnLeftNormal - else if HorzDownState = tsArrowBtnRightPressed then - HorzDownState := tsArrowBtnRightNormal; - end; - PaintScroll; - end; - FLeftMouseButtonDown := False; -end; - -procedure TVclStyleScrollBarsHook.WMMouseMove(var Msg: TWMMouse); -var - SF: TScrollInfo; - OverrideMax: Integer; -begin - if VertSliderState = tsThumbBtnVertPressed then - begin - SF.fMask := SIF_ALL; - SF.cbSize := SizeOf(SF); - GetScrollInfo(Handle, SB_VERT, SF); - - OverrideMax := SF.nMax; - if 0 < SF.nPage then - OverrideMax := SF.nMax - Integer(SF.nPage) + 1; - ScrollPos := System.Math.EnsureRange(ListPos + (OverrideMax - SF.nMin) * ((Mouse.CursorPos.Y - PrevScrollPos) / (VertTrackRect.Height - VertSliderRect.Height)), - SF.nMin, OverrideMax); - SF.fMask := SIF_POS; - SF.nPos := Round(ScrollPos); - SetScrollInfo(Handle, SB_VERT, SF, False); - PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); - - PaintScroll; - Handled := True; - Exit; - end else - if VertSliderState = tsThumbBtnVertHot then - begin - VertSliderState := tsThumbBtnVertNormal; - PaintScroll; - end; - - if HorzSliderState = tsThumbBtnHorzPressed then - begin - SF.fMask := SIF_ALL; - SF.cbSize := SizeOf(SF); - GetScrollInfo(Handle, SB_HORZ, SF); - - OverrideMax := SF.nMax; - if 0 < SF.nPage then - OverrideMax := SF.nMax - Integer(SF.nPage) + 1; - ScrollPos := System.Math.EnsureRange(ListPos + (OverrideMax - SF.nMin) * ((Mouse.CursorPos.X - PrevScrollPos) / (HorzTrackRect.Width - HorzSliderRect.Width)), - SF.nMin, OverrideMax); - SF.fMask := SIF_POS; - SF.nPos := Round(ScrollPos); - SetScrollInfo(Handle, SB_HORZ, SF, False); - PostMessage(Handle, WM_HSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); - - PaintScroll; - Handled := True; - Exit; - end else - if HorzSliderState = tsThumbBtnHorzHot then - begin - HorzSliderState := tsThumbBtnHorzNormal; - PaintScroll; - end; - - if (HorzUpState <> tsArrowBtnLeftPressed) and (HorzUpState = tsArrowBtnLeftHot) then - begin - HorzUpState := tsArrowBtnLeftNormal; - PaintScroll; - end; - - if (HorzDownState <> tsArrowBtnRightPressed) and (HorzDownState = tsArrowBtnRightHot) then - begin - HorzDownState := tsArrowBtnRightNormal; - PaintScroll; - end; - - if (VertUpState <> tsArrowBtnUpPressed) and (VertUpState = tsArrowBtnUpHot) then - begin - VertUpState := tsArrowBtnUpNormal; - PaintScroll; - end; - - if (VertDownState <> tsArrowBtnDownPressed) and (VertDownState = tsArrowBtnDownHot) then - begin - VertDownState := tsArrowBtnDownNormal; - PaintScroll; - end; - - CallDefaultProc(TMessage(Msg)); - if FLeftMouseButtonDown then - PaintScroll; - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMMouseWheel(var Msg: TMessage); -begin - CallDefaultProc(TMessage(Msg)); - CalcScrollBarsRect; - PaintScroll; - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMNCLButtonDblClk(var Msg: TWMMouse); -begin - WMNCLButtonDown(Msg); -end; - -procedure TVclStyleScrollBarsHook.WMNCLButtonDown(var Msg: TWMMouse); -var - P: TPoint; - SF: TScrollInfo; -begin - P := NCMousePosToClient(Point(Msg.XPos, Msg.YPos)); - if not PointInTreeHeader(P) then - begin - if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then - begin - if PtInRect(VertSliderRect, P) then - begin - FLeftMouseButtonDown := True; - SF.fMask := SIF_ALL; - SF.cbSize := SizeOf(SF); - GetScrollInfo(Handle, SB_VERT, SF); - ListPos := SF.nPos; - ScrollPos := SF.nPos; - PrevScrollPos := Mouse.CursorPos.Y; - VertSliderState := tsThumbBtnVertPressed; - PaintScroll; - Mouse.Capture := Handle; - Handled := True; - Exit; - end else - if PtInRect(VertDownButtonRect, P) then - VertDownState := tsArrowBtnDownPressed - else if PtInRect(VertUpButtonRect, P) then - VertUpState := tsArrowBtnUpPressed; - end; - - if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then - begin - if PtInRect(HorzSliderRect, P) then - begin - FLeftMouseButtonDown := True; - SF.fMask := SIF_ALL; - SF.cbSize := SizeOf(SF); - GetScrollInfo(Handle, SB_HORZ, SF); - ListPos := SF.nPos; - ScrollPos := SF.nPos; - PrevScrollPos := Mouse.CursorPos.X; - HorzSliderState := tsThumbBtnHorzPressed; - PaintScroll; - Mouse.Capture := Handle; - Handled := True; - Exit; - end else - if PtInRect(HorzDownButtonRect, P) then - HorzDownState := tsArrowBtnRightPressed - else if PtInRect(HorzUpButtonRect, P) then - HorzUpState := tsArrowBtnLeftPressed; - end; - FLeftMouseButtonDown := True; - PaintScroll; - end; -end; - -procedure TVclStyleScrollBarsHook.WMNCLButtonUp(var Msg: TWMMouse); -var - P: TPoint; -begin - P := NCMousePosToClient(Point(Msg.XPos, Msg.YPos)); - if not PointInTreeHeader(P) then - begin - if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then - begin - if VertSliderState = tsThumbBtnVertPressed then - begin - FLeftMouseButtonDown := False; - VertSliderState := tsThumbBtnVertNormal; - PaintScroll; - Handled := True; - Exit; - end; - - if PtInRect(VertDownButtonRect, P) then - VertDownState := tsArrowBtnDownHot - else - VertDownState := tsArrowBtnDownNormal; - - if PtInRect(VertUpButtonRect, P) then - VertUpState := tsArrowBtnUpHot - else - VertUpState := tsArrowBtnUpNormal; - end; - - if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then - begin - if HorzSliderState = tsThumbBtnHorzPressed then - begin - FLeftMouseButtonDown := False; - HorzSliderState := tsThumbBtnHorzNormal; - PaintScroll; - Handled := True; - Exit; - end; - - if PtInRect(HorzDownButtonRect, P) then - HorzDownState := tsArrowBtnRightHot - else - HorzDownState := tsArrowBtnRightNormal; - - if PtInRect(HorzUpButtonRect, P) then - HorzUpState := tsArrowBtnLeftHot - else - HorzUpState := tsArrowBtnLeftNormal; - end; - - CallDefaultProc(TMessage(Msg)); - if (FHorzScrollWnd.Visible) or (FVertScrollWnd.Visible) then - PaintScroll; - end; - - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMNCMouseMove(var Msg: TWMMouse); -var - P: TPoint; - MustUpdateScroll: Boolean; - B: Boolean; -begin - inherited; - P := NCMousePosToClient(Point(Msg.XPos, Msg.YPos)); - if PointInTreeHeader(P) then - begin - CallDefaultProc(TMessage(Msg)); - PaintScroll; - Handled := True; - Exit; - end; - - MustUpdateScroll := False; - if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then - begin - B := PtInRect(VertSliderRect, P); - if B and (VertSliderState = tsThumbBtnVertNormal) then - begin - VertSliderState := tsThumbBtnVertHot; - MustUpdateScroll := True; - end else - if not B and (VertSliderState = tsThumbBtnVertHot) then - begin - VertSliderState := tsThumbBtnVertNormal; - MustUpdateScroll := True; - end; - - B := PtInRect(VertDownButtonRect, P); - if B and (VertDownState = tsArrowBtnDownNormal) then - begin - VertDownState := tsArrowBtnDownHot; - MustUpdateScroll := True; - end else - if not B and (VertDownState = tsArrowBtnDownHot) then - begin - VertDownState := tsArrowBtnDownNormal; - MustUpdateScroll := True; - end; - - B := PtInRect(VertUpButtonRect, P); - if B and (VertUpState = tsArrowBtnUpNormal) then - begin - VertUpState := tsArrowBtnUpHot; - MustUpdateScroll := True; - end else - if not B and (VertUpState = tsArrowBtnUpHot) then - begin - VertUpState := tsArrowBtnUpNormal; - MustUpdateScroll := True; - end; - end; - - if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then - begin - B := PtInRect(HorzSliderRect, P); - if B and (HorzSliderState = tsThumbBtnHorzNormal) then - begin - HorzSliderState := tsThumbBtnHorzHot; - MustUpdateScroll := True; - end else - if not B and (HorzSliderState = tsThumbBtnHorzHot) then - begin - HorzSliderState := tsThumbBtnHorzNormal; - MustUpdateScroll := True; - end; - - B := PtInRect(HorzDownButtonRect, P); - if B and (HorzDownState = tsArrowBtnRightNormal) then - begin - HorzDownState := tsArrowBtnRightHot; - MustUpdateScroll := True; - end else - if not B and (HorzDownState = tsArrowBtnRightHot) then - begin - HorzDownState := tsArrowBtnRightNormal; - MustUpdateScroll := True; - end; - - B := PtInRect(HorzUpButtonRect, P); - if B and (HorzUpState = tsArrowBtnLeftNormal) then - begin - HorzUpState := tsArrowBtnLeftHot; - MustUpdateScroll := True; - end else - if not B and (HorzUpState = tsArrowBtnLeftHot) then - begin - HorzUpState := tsArrowBtnLeftNormal; - MustUpdateScroll := True; - end; - end; - - if MustUpdateScroll then - PaintScroll; -end; - -procedure TVclStyleScrollBarsHook.WMNCPaint(var Msg: TMessage); -begin - //if (tsWindowCreating in TBaseVirtualTree(Control).TreeStates) then - // UpdateScrollBarWindow; - //inherited; -end; - -procedure TVclStyleScrollBarsHook.WMSize(var Msg: TMessage); -begin - CallDefaultProc(TMessage(Msg)); - UpdateScroll; - PaintScroll; - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMMove(var Msg: TMessage); -begin - CallDefaultProc(TMessage(Msg)); - if not(tsWindowCreating in TBaseVirtualTree(Control).TreeStates) then - begin - UpdateScroll; - PaintScroll; - end; - Handled := True; -end; - -procedure TVclStyleScrollBarsHook.WMPosChanged(var Msg: TMessage); -begin - WMMove(Msg); -end; - -procedure TVclStyleScrollBarsHook.WMVScroll(var Msg: TWMVScroll); -begin - CallDefaultProc(TMessage(Msg)); - if not (Msg.ScrollCode in [SB_THUMBTRACK, SB_THUMBPOSITION]) then - UpdateScroll - else - PaintScroll; - Handled := True; -end; - -{ TVclStyleScrollBarsHook.TVclStyleScrollBarWindow } - -constructor TVclStyleScrollBarsHook.TScrollWindow.Create(AOwner: TComponent); -begin - inherited; - ControlStyle := ControlStyle + [csOverrideStylePaint]; - FStyleHook := nil; - FVertical := False; -end; - -procedure TVclStyleScrollBarsHook.TScrollWindow.WMEraseBkgnd(var Msg: TMessage); -begin - Msg.Result := 1; -end; - -procedure TVclStyleScrollBarsHook.TScrollWindow.WMNCHitTest(var Msg: TWMNCHitTest); -begin - Msg.Result := HTTRANSPARENT; -end; - -procedure TVclStyleScrollBarsHook.TScrollWindow.WMPaint(var Msg: TWMPaint); -var - PS: TPaintStruct; - DC: HDC; - R: TRect; -begin - BeginPaint(Handle, PS); - try - if FStyleHook <> nil then - begin - DC := GetWindowDC(Handle); - try - if FVertical then - begin - R := FStyleHook.VertScrollRect; - MoveWindowOrg(DC, -R.Left, -R.Top); - FStyleHook.DrawVertScrollBar(DC); - end else - begin - R := FStyleHook.HorzScrollRect; - MoveWindowOrg(DC, -R.Left, -R.Top); - FStyleHook.DrawHorzScrollBar(DC); - end; - finally - ReleaseDC(Handle, DC); - end; - end; - finally - EndPaint(Handle, PS); - end; -end; - -initialization - TCustomStyleEngine.RegisterStyleHook(TVirtualStringTree, TVclStyleScrollBarsHook); - TCustomStyleEngine.RegisterStyleHook(TVirtualDrawTree, TVclStyleScrollBarsHook); - -finalization - TCustomStyleEngine.UnRegisterStyleHook(TVirtualStringTree, TVclStyleScrollBarsHook); - TCustomStyleEngine.UnRegisterStyleHook(TVirtualDrawTree, TVclStyleScrollBarsHook); - -end. - + + // ScrollBarWindow Visible/Enabled Control + CalcScrollBarsRect; + + HeaderHeight := 0; + if (hoVisible in TBaseVirtualTree(Control).Header.Options) then + Inc(HeaderHeight, TBaseVirtualTree(Control).Header.Height); + + PaddingSize := TBaseVirtualTreeCracker(Control).BorderWidth; + if TBaseVirtualTreeCracker(Control).BevelKind <> bkNone then + begin + if TBaseVirtualTreeCracker(Control).BevelInner <> bvNone then + Inc(PaddingSize, TBaseVirtualTreeCracker(Control).BevelWidth); + if TBaseVirtualTreeCracker(Control).BevelOuter <> bvNone then + Inc(PaddingSize, TBaseVirtualTreeCracker(Control).BevelWidth); + end; + + BorderSize := 0; + if HasBorder then + Inc(BorderSize, GetSystemMetrics(SM_CYEDGE)); + + // VertScrollBarWindow + if Control.HandleAllocated then + begin + if FVertScrollWnd.Visible then + begin + R := VertScrollRect; + if Control.UseRightToLeftScrollBar then + OffsetRect(R, -R.Left + BorderSize, 0); + + ShowWindow(FVertScrollWnd.Handle, SW_SHOW); + SetWindowPos(FVertScrollWnd.Handle, HWND_TOP, + Control.Left + R.Left + PaddingSize, + Control.Top + R.Top + HeaderHeight + PaddingSize, + R.Width, + Control.Height - HeaderHeight - ((PaddingSize + BorderSize) * 2), // <> R.Height + SWP_SHOWWINDOW); + end else + ShowWindow(FVertScrollWnd.Handle, SW_HIDE); + end;// if FVertScrollWnd + + // HorzScrollBarWindow + if Control.HandleAllocated then + begin + if FHorzScrollWnd.Visible then + begin + R := HorzScrollRect; + if Control.UseRightToLeftScrollBar then + OffsetRect(R, VertScrollRect.Width, 0); + + ShowWindow(FHorzScrollWnd.Handle, SW_SHOW); + SetWindowPos(FHorzScrollWnd.Handle, HWND_TOP, + Control.Left + R.Left + PaddingSize, + Control.Top + R.Top + HeaderHeight + PaddingSize, + R.Width, R.Height, SWP_SHOWWINDOW); + end else + ShowWindow(FHorzScrollWnd.Handle, SW_HIDE); + end;// if FHorzScrollWnd +end; + +procedure TVclStyleScrollBarsHook.WMCaptureChanged(var Msg: TMessage); +begin + if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then + begin + if VertUpState = tsArrowBtnUpPressed then + begin + VertUpState := tsArrowBtnUpNormal; + PaintScroll; + end; + + if VertDownState = tsArrowBtnDownPressed then + begin + VertDownState := tsArrowBtnDownNormal; + PaintScroll; + end; + end; + + if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then + begin + if HorzUpState = tsArrowBtnLeftPressed then + begin + HorzUpState := tsArrowBtnLeftNormal; + PaintScroll; + end; + + if HorzDownState = tsArrowBtnRightPressed then + begin + HorzDownState := tsArrowBtnRightNormal; + PaintScroll; + end; + end; + + CallDefaultProc(TMessage(Msg)); + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMEraseBkgnd(var Msg: TWMEraseBkgnd); +begin + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMHScroll(var Msg: TWMHScroll); +begin + CallDefaultProc(TMessage(Msg)); + if not (Msg.ScrollCode in [SB_THUMBTRACK, SB_THUMBPOSITION]) then + UpdateScroll + else + PaintScroll; + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.CMUpdateVclStyleScrollbars(var Msg: TMessage); +begin + CalcScrollBarsRect; + PaintScroll; +end; + +procedure TVclStyleScrollBarsHook.WMKeyDown(var Msg: TMessage); +begin + CallDefaultProc(TMessage(Msg)); + UpdateScroll; + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMKeyUp(var Msg: TMessage); +begin + CallDefaultProc(TMessage(Msg)); + PaintScroll; + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMLButtonDown(var Msg: TWMMouse); +begin + CallDefaultProc(TMessage(Msg)); + UpdateScroll; + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMLButtonUp(var Msg: TWMMouse); +var + P: TPoint; +begin + P := Point(Msg.XPos, Msg.YPos); + ScreenToClient(Handle, P); + if not PointInTreeHeader(P) then + begin + if FVertScrollWnd.Visible then + begin + if VertSliderState = tsThumbBtnVertPressed then + begin + PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_ENDSCROLL, 0)), 0); + FLeftMouseButtonDown := False; + VertSliderState := tsThumbBtnVertNormal; + PaintScroll; + Handled := True; + Mouse.Capture := 0; + Exit; + end else + if VertUpState = tsArrowBtnUpPressed then + VertUpState := tsArrowBtnUpNormal + else if VertDownState = tsArrowBtnDownPressed then + VertDownState := tsArrowBtnDownNormal; + end; + + if FHorzScrollWnd.Visible then + begin + if HorzSliderState = tsThumbBtnHorzPressed then + begin + PostMessage(Handle, WM_HSCROLL, Integer(SmallPoint(SB_ENDSCROLL, 0)), 0); + FLeftMouseButtonDown := False; + HorzSliderState := tsThumbBtnHorzNormal; + PaintScroll; + Handled := True; + Mouse.Capture := 0; + Exit; + end else + if HorzUpState = tsArrowBtnLeftPressed then + HorzUpState := tsArrowBtnLeftNormal + else if HorzDownState = tsArrowBtnRightPressed then + HorzDownState := tsArrowBtnRightNormal; + end; + PaintScroll; + end; + FLeftMouseButtonDown := False; +end; + +procedure TVclStyleScrollBarsHook.WMMouseMove(var Msg: TWMMouse); +var + SF: TScrollInfo; + OverrideMax: Integer; +begin + if VertSliderState = tsThumbBtnVertPressed then + begin + SF.fMask := SIF_ALL; + SF.cbSize := SizeOf(SF); + GetScrollInfo(Handle, SB_VERT, SF); + + OverrideMax := SF.nMax; + if 0 < SF.nPage then + OverrideMax := SF.nMax - Integer(SF.nPage) + 1; + ScrollPos := System.Math.EnsureRange(ListPos + (OverrideMax - SF.nMin) * ((Mouse.CursorPos.Y - PrevScrollPos) / (VertTrackRect.Height - VertSliderRect.Height)), + SF.nMin, OverrideMax); + SF.fMask := SIF_POS; + SF.nPos := Round(ScrollPos); + SetScrollInfo(Handle, SB_VERT, SF, False); + PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); + + PaintScroll; + Handled := True; + Exit; + end else + if VertSliderState = tsThumbBtnVertHot then + begin + VertSliderState := tsThumbBtnVertNormal; + PaintScroll; + end; + + if HorzSliderState = tsThumbBtnHorzPressed then + begin + SF.fMask := SIF_ALL; + SF.cbSize := SizeOf(SF); + GetScrollInfo(Handle, SB_HORZ, SF); + + OverrideMax := SF.nMax; + if 0 < SF.nPage then + OverrideMax := SF.nMax - Integer(SF.nPage) + 1; + ScrollPos := System.Math.EnsureRange(ListPos + (OverrideMax - SF.nMin) * ((Mouse.CursorPos.X - PrevScrollPos) / (HorzTrackRect.Width - HorzSliderRect.Width)), + SF.nMin, OverrideMax); + SF.fMask := SIF_POS; + SF.nPos := Round(ScrollPos); + SetScrollInfo(Handle, SB_HORZ, SF, False); + PostMessage(Handle, WM_HSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); + + PaintScroll; + Handled := True; + Exit; + end else + if HorzSliderState = tsThumbBtnHorzHot then + begin + HorzSliderState := tsThumbBtnHorzNormal; + PaintScroll; + end; + + if (HorzUpState <> tsArrowBtnLeftPressed) and (HorzUpState = tsArrowBtnLeftHot) then + begin + HorzUpState := tsArrowBtnLeftNormal; + PaintScroll; + end; + + if (HorzDownState <> tsArrowBtnRightPressed) and (HorzDownState = tsArrowBtnRightHot) then + begin + HorzDownState := tsArrowBtnRightNormal; + PaintScroll; + end; + + if (VertUpState <> tsArrowBtnUpPressed) and (VertUpState = tsArrowBtnUpHot) then + begin + VertUpState := tsArrowBtnUpNormal; + PaintScroll; + end; + + if (VertDownState <> tsArrowBtnDownPressed) and (VertDownState = tsArrowBtnDownHot) then + begin + VertDownState := tsArrowBtnDownNormal; + PaintScroll; + end; + + CallDefaultProc(TMessage(Msg)); + if FLeftMouseButtonDown then + PaintScroll; + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMMouseWheel(var Msg: TMessage); +begin + CallDefaultProc(TMessage(Msg)); + CalcScrollBarsRect; + PaintScroll; + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMNCLButtonDblClk(var Msg: TWMMouse); +begin + WMNCLButtonDown(Msg); +end; + +procedure TVclStyleScrollBarsHook.WMNCLButtonDown(var Msg: TWMMouse); +var + P: TPoint; + SF: TScrollInfo; +begin + P := NCMousePosToClient(Point(Msg.XPos, Msg.YPos)); + if not PointInTreeHeader(P) then + begin + if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then + begin + if PtInRect(VertSliderRect, P) then + begin + FLeftMouseButtonDown := True; + SF.fMask := SIF_ALL; + SF.cbSize := SizeOf(SF); + GetScrollInfo(Handle, SB_VERT, SF); + ListPos := SF.nPos; + ScrollPos := SF.nPos; + PrevScrollPos := Mouse.CursorPos.Y; + VertSliderState := tsThumbBtnVertPressed; + PaintScroll; + Mouse.Capture := Handle; + Handled := True; + Exit; + end else + if PtInRect(VertDownButtonRect, P) then + VertDownState := tsArrowBtnDownPressed + else if PtInRect(VertUpButtonRect, P) then + VertUpState := tsArrowBtnUpPressed; + end; + + if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then + begin + if PtInRect(HorzSliderRect, P) then + begin + FLeftMouseButtonDown := True; + SF.fMask := SIF_ALL; + SF.cbSize := SizeOf(SF); + GetScrollInfo(Handle, SB_HORZ, SF); + ListPos := SF.nPos; + ScrollPos := SF.nPos; + PrevScrollPos := Mouse.CursorPos.X; + HorzSliderState := tsThumbBtnHorzPressed; + PaintScroll; + Mouse.Capture := Handle; + Handled := True; + Exit; + end else + if PtInRect(HorzDownButtonRect, P) then + HorzDownState := tsArrowBtnRightPressed + else if PtInRect(HorzUpButtonRect, P) then + HorzUpState := tsArrowBtnLeftPressed; + end; + FLeftMouseButtonDown := True; + PaintScroll; + end; +end; + +procedure TVclStyleScrollBarsHook.WMNCLButtonUp(var Msg: TWMMouse); +var + P: TPoint; +begin + P := NCMousePosToClient(Point(Msg.XPos, Msg.YPos)); + if not PointInTreeHeader(P) then + begin + if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then + begin + if VertSliderState = tsThumbBtnVertPressed then + begin + FLeftMouseButtonDown := False; + VertSliderState := tsThumbBtnVertNormal; + PaintScroll; + Handled := True; + Exit; + end; + + if PtInRect(VertDownButtonRect, P) then + VertDownState := tsArrowBtnDownHot + else + VertDownState := tsArrowBtnDownNormal; + + if PtInRect(VertUpButtonRect, P) then + VertUpState := tsArrowBtnUpHot + else + VertUpState := tsArrowBtnUpNormal; + end; + + if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then + begin + if HorzSliderState = tsThumbBtnHorzPressed then + begin + FLeftMouseButtonDown := False; + HorzSliderState := tsThumbBtnHorzNormal; + PaintScroll; + Handled := True; + Exit; + end; + + if PtInRect(HorzDownButtonRect, P) then + HorzDownState := tsArrowBtnRightHot + else + HorzDownState := tsArrowBtnRightNormal; + + if PtInRect(HorzUpButtonRect, P) then + HorzUpState := tsArrowBtnLeftHot + else + HorzUpState := tsArrowBtnLeftNormal; + end; + + CallDefaultProc(TMessage(Msg)); + if (FHorzScrollWnd.Visible) or (FVertScrollWnd.Visible) then + PaintScroll; + end; + + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMNCMouseMove(var Msg: TWMMouse); +var + P: TPoint; + MustUpdateScroll: Boolean; + B: Boolean; +begin + inherited; + P := NCMousePosToClient(Point(Msg.XPos, Msg.YPos)); + if PointInTreeHeader(P) then + begin + CallDefaultProc(TMessage(Msg)); + PaintScroll; + Handled := True; + Exit; + end; + + MustUpdateScroll := False; + if FVertScrollWnd.Visible and FVertScrollWnd.Enabled then + begin + B := PtInRect(VertSliderRect, P); + if B and (VertSliderState = tsThumbBtnVertNormal) then + begin + VertSliderState := tsThumbBtnVertHot; + MustUpdateScroll := True; + end else + if not B and (VertSliderState = tsThumbBtnVertHot) then + begin + VertSliderState := tsThumbBtnVertNormal; + MustUpdateScroll := True; + end; + + B := PtInRect(VertDownButtonRect, P); + if B and (VertDownState = tsArrowBtnDownNormal) then + begin + VertDownState := tsArrowBtnDownHot; + MustUpdateScroll := True; + end else + if not B and (VertDownState = tsArrowBtnDownHot) then + begin + VertDownState := tsArrowBtnDownNormal; + MustUpdateScroll := True; + end; + + B := PtInRect(VertUpButtonRect, P); + if B and (VertUpState = tsArrowBtnUpNormal) then + begin + VertUpState := tsArrowBtnUpHot; + MustUpdateScroll := True; + end else + if not B and (VertUpState = tsArrowBtnUpHot) then + begin + VertUpState := tsArrowBtnUpNormal; + MustUpdateScroll := True; + end; + end; + + if FHorzScrollWnd.Visible and FHorzScrollWnd.Enabled then + begin + B := PtInRect(HorzSliderRect, P); + if B and (HorzSliderState = tsThumbBtnHorzNormal) then + begin + HorzSliderState := tsThumbBtnHorzHot; + MustUpdateScroll := True; + end else + if not B and (HorzSliderState = tsThumbBtnHorzHot) then + begin + HorzSliderState := tsThumbBtnHorzNormal; + MustUpdateScroll := True; + end; + + B := PtInRect(HorzDownButtonRect, P); + if B and (HorzDownState = tsArrowBtnRightNormal) then + begin + HorzDownState := tsArrowBtnRightHot; + MustUpdateScroll := True; + end else + if not B and (HorzDownState = tsArrowBtnRightHot) then + begin + HorzDownState := tsArrowBtnRightNormal; + MustUpdateScroll := True; + end; + + B := PtInRect(HorzUpButtonRect, P); + if B and (HorzUpState = tsArrowBtnLeftNormal) then + begin + HorzUpState := tsArrowBtnLeftHot; + MustUpdateScroll := True; + end else + if not B and (HorzUpState = tsArrowBtnLeftHot) then + begin + HorzUpState := tsArrowBtnLeftNormal; + MustUpdateScroll := True; + end; + end; + + if MustUpdateScroll then + PaintScroll; +end; + +procedure TVclStyleScrollBarsHook.WMNCPaint(var Msg: TMessage); +begin + //if (tsWindowCreating in TBaseVirtualTree(Control).TreeStates) then + // UpdateScrollBarWindow; + //inherited; +end; + +procedure TVclStyleScrollBarsHook.WMSize(var Msg: TMessage); +begin + CallDefaultProc(TMessage(Msg)); + UpdateScroll; + PaintScroll; + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMMove(var Msg: TMessage); +begin + CallDefaultProc(TMessage(Msg)); + if not(tsWindowCreating in TBaseVirtualTree(Control).TreeStates) then + begin + UpdateScroll; + PaintScroll; + end; + Handled := True; +end; + +procedure TVclStyleScrollBarsHook.WMPosChanged(var Msg: TMessage); +begin + WMMove(Msg); +end; + +procedure TVclStyleScrollBarsHook.WMVScroll(var Msg: TWMVScroll); +begin + CallDefaultProc(TMessage(Msg)); + if not (Msg.ScrollCode in [SB_THUMBTRACK, SB_THUMBPOSITION]) then + UpdateScroll + else + PaintScroll; + Handled := True; +end; + +{ TVclStyleScrollBarsHook.TVclStyleScrollBarWindow } + +constructor TVclStyleScrollBarsHook.TScrollWindow.Create(AOwner: TComponent); +begin + inherited; + ControlStyle := ControlStyle + [csOverrideStylePaint]; + FStyleHook := nil; + FVertical := False; +end; + +procedure TVclStyleScrollBarsHook.TScrollWindow.WMEraseBkgnd(var Msg: TMessage); +begin + Msg.Result := 1; +end; + +procedure TVclStyleScrollBarsHook.TScrollWindow.WMNCHitTest(var Msg: TWMNCHitTest); +begin + Msg.Result := HTTRANSPARENT; +end; + +procedure TVclStyleScrollBarsHook.TScrollWindow.WMPaint(var Msg: TWMPaint); +var + PS: TPaintStruct; + DC: HDC; + R: TRect; +begin + BeginPaint(Handle, PS); + try + if FStyleHook <> nil then + begin + DC := GetWindowDC(Handle); + try + if FVertical then + begin + R := FStyleHook.VertScrollRect; + MoveWindowOrg(DC, -R.Left, -R.Top); + FStyleHook.DrawVertScrollBar(DC); + end else + begin + R := FStyleHook.HorzScrollRect; + MoveWindowOrg(DC, -R.Left, -R.Top); + FStyleHook.DrawHorzScrollBar(DC); + end; + finally + ReleaseDC(Handle, DC); + end; + end; + finally + EndPaint(Handle, PS); + end; +end; + +initialization + TCustomStyleEngine.RegisterStyleHook(TVirtualStringTree, TVclStyleScrollBarsHook); + TCustomStyleEngine.RegisterStyleHook(TVirtualDrawTree, TVclStyleScrollBarsHook); + +finalization + TCustomStyleEngine.UnRegisterStyleHook(TVirtualStringTree, TVclStyleScrollBarsHook); + TCustomStyleEngine.UnRegisterStyleHook(TVirtualDrawTree, TVclStyleScrollBarsHook); + +end. + From ea305c00dbf05c2be80f5a5f4811844d29d47b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20J=C3=A4nicke?= Date: Mon, 11 Jan 2021 07:39:42 +0100 Subject: [PATCH 175/681] Support for hottrack highlighting by column (e.g. setting Brush.Color in OnBeforeCellPaint) --- Source/VirtualTrees.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 26e905c57..88ebb4039 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -3216,6 +3216,7 @@ TBaseVirtualTree = class(TCustomControl) property HasChildren[Node: PVirtualNode]: Boolean read GetHasChildren write SetHasChildren; property Header: TVTHeader read FHeader write SetHeader; property HotNode: PVirtualNode read FCurrentHotNode write SetHotNode; + property HotColumn: TColumnIndex read FCurrentHotColumn; property IsDisabled[Node: PVirtualNode]: Boolean read GetDisabled write SetDisabled; property IsEffectivelyFiltered[Node: PVirtualNode]: Boolean read GetEffectivelyFiltered; property IsEffectivelyVisible[Node: PVirtualNode]: Boolean read GetEffectivelyVisible; From 9f6e2bb5d564cdd14aea0c6d864aa98f80cdf2bf Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 12 Jan 2021 11:56:08 +0100 Subject: [PATCH 176/681] Comment about exception. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 26e905c57..068fb4963 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18519,7 +18519,7 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; Run.NodeHeight := MulDiv(Run.NodeHeight, M, D); // The next three lines fix issue #1000 lNewNodeTotalHeight := MulDiv(Run.TotalHeight, M, D); - FRoot.TotalHeight := FRoot.TotalHeight + lNewNodeTotalHeight - Run.TotalHeight; + FRoot.TotalHeight := FRoot.TotalHeight + lNewNodeTotalHeight - Run.TotalHeight; // 1 EIntOverflow exception seen here in debug build in 01/2021 Run.TotalHeight := lNewNodeTotalHeight; end; Run := GetNextNoInit(Run); From 00ab168b36da3ff7b45b983c03b794d28200e14e Mon Sep 17 00:00:00 2001 From: Igor Kaplya Date: Wed, 13 Jan 2021 15:48:52 +0300 Subject: [PATCH 177/681] Added solution from attached issue (#749) Delphi 10.3 CE doesn't put $(BDSCOMMONDIR)\dcp on library path. So specified that explicitly, and only after that was able to install components. $(BDSCOMMONDIR)\dcp --- Packages/RAD Studio 10.3/VirtualTreesD.dproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Packages/RAD Studio 10.3/VirtualTreesD.dproj b/Packages/RAD Studio 10.3/VirtualTreesD.dproj index 0b0468d20..0f72d77d7 100644 --- a/Packages/RAD Studio 10.3/VirtualTreesD.dproj +++ b/Packages/RAD Studio 10.3/VirtualTreesD.dproj @@ -1,4 +1,4 @@ - + {A34BA07B-19B6-4C21-9DEE-65FCA52D00AB} VirtualTreesD.dpk @@ -38,7 +38,7 @@ All 26 true - ..\..\source;.\$(Platform)\$(Config);$(DCC_UnitSearchPath) + ..\..\source;.\$(Platform)\$(Config);$(BDSCOMMONDIR)\dcp;$(DCC_UnitSearchPath) System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) 1053 false From 123fd150a880e3188c40ac5daaf26b7a971d5017 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 28 Jan 2021 20:44:57 +0100 Subject: [PATCH 178/681] Improved fix for #1002: Fix mem leak --- Source/VirtualTrees.pas | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 128d00f27..41b147d14 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -12215,9 +12215,7 @@ destructor TBaseVirtualTree.Destroy; FDragImage.Free; FColors.Free; FBackground.Free; - FImageChangeLink.Free; - FStateChangeLink.Free; - FCustomCheckChangeLink.Free; + if CheckImageKind = ckSystemDefault then FCheckImages.Free; FScrollBarOptions.Free; @@ -12249,6 +12247,10 @@ destructor TBaseVirtualTree.Destroy; StateImages := nil; CustomCheckImages := nil; + FImageChangeLink.Free; + FStateChangeLink.Free; + FCustomCheckChangeLink.Free; + inherited; end; From 661886c9bff3806ab40ed5119cbbc7db50a45599 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 8 Feb 2021 21:04:50 +0100 Subject: [PATCH 179/681] Improved fix for issue #390: move the code that recreates the scroll wnds inside CalcScrollBarsRect() so that it is called in other call stacks as well. --- Source/VirtualTrees.StyleHooks.pas | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index ea6afc5f3..59d2b3287 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -131,7 +131,7 @@ TBaseVirtualTreeCracker = class(TBaseVirtualTree) // XE2+ VCL Style { TVclStyleScrollBarsHook } -procedure TVclStyleScrollBarsHook.CalcScrollBarsRect; +procedure TVclStyleScrollBarsHook.CalcScrollBarsRect(); procedure CalcVerticalRects; var @@ -156,6 +156,17 @@ procedure TVclStyleScrollBarsHook.CalcScrollBarsRect; end; begin + if ((FVertScrollWnd <> nil) and not FVertScrollWnd.HandleAllocated) or + ((FHorzScrollWnd <> nil) and not FHorzScrollWnd.HandleAllocated) then + begin // Fixes issue #390 + if FVertScrollWnd <> nil then + FreeAndNil(FVertScrollWnd); + if FHorzScrollWnd <> nil then + FreeAndNil(FHorzScrollWnd); + + InitScrollBars; + end; + CalcVerticalRects; CalcHorizontalRects; end; @@ -355,18 +366,6 @@ procedure TVclStyleScrollBarsHook.UpdateScroll; PaddingSize: Integer; BorderSize: Integer; begin - if ((FVertScrollWnd <> nil) and not FVertScrollWnd.HandleAllocated) or - ((FHorzScrollWnd <> nil) and not FHorzScrollWnd.HandleAllocated) - then - begin - if FVertScrollWnd <> nil then - FreeAndNil(FVertScrollWnd); - if FHorzScrollWnd <> nil then - FreeAndNil(FHorzScrollWnd); - - InitScrollBars; - end; - // ScrollBarWindow Visible/Enabled Control CalcScrollBarsRect; From 87806b806bcd989c77d1498a5a63a86f71f1d2f9 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 23 Feb 2021 08:59:13 +0100 Subject: [PATCH 180/681] Added assertion to TBaseVirtualTree.GetTotalCount(), fixed typos --- Source/VirtualTrees.pas | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 41b147d14..ae1933e1d 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -8473,7 +8473,7 @@ procedure TVirtualTreeColumns.UpdatePositions(Force: Boolean = False); function TVirtualTreeColumns.Add: TVirtualTreeColumn; begin - Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be chnaged in UI thread.'); + Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be changed in UI thread.'); Result := TVirtualTreeColumn(inherited Add); end; @@ -13514,9 +13514,10 @@ function TBaseVirtualTree.GetTopNode: PVirtualNode; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetTotalCount: Cardinal; +function TBaseVirtualTree.GetTotalCount(): Cardinal; begin + Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be used in UI thread.'); // FUpdateCount is not thread-safe! So do not write it in non-UI threads. Inc(FUpdateCount); try ValidateNode(FRoot, True); @@ -14513,7 +14514,7 @@ procedure TBaseVirtualTree.SetChildCount(Node: PVirtualNode; NewChildCount: Card if Node = nil then Node := FRoot; - Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be chnaged in UI thread.'); + Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be changed in UI thread.'); if NewChildCount = 0 then DeleteChildren(Node) else From 8f5bd5e1fd68610285de757195d3f15d2a2fe111 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sat, 6 Mar 2021 09:30:10 +0100 Subject: [PATCH 181/681] Fixed issue #1024: Improved default values for "Touch" property --- Source/VirtualTrees.pas | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ae1933e1d..92bb0b65d 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -12173,6 +12173,11 @@ constructor TBaseVirtualTree.Create(AOwner: TComponent); FClipboardFormats := TClipboardFormats.Create(Self); FOptions := GetOptionsClass.Create(Self); + Touch.InteractiveGestures := [igPan, igPressAndTap]; + Touch.InteractiveGestureOptions := [igoPanInertia, + igoPanSingleFingerHorizontal, igoPanSingleFingerVertical, + igoPanGutter, igoParentPassthrough]; + if not (csDesigning in ComponentState) then //Don't create worker thread in IDE, there is no use for it TWorkerThread.AddThreadReference(); end; From e67bf379c7b089d900cae19d135a0f97ae6b5c51 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 15 Mar 2021 22:02:51 +0100 Subject: [PATCH 182/681] Fixed issue #1023 by extracting new method HandleCheckboxClick() from HandleMouseDown() and call it in HandleMouseDblClick() too. --- Source/VirtualTrees.pas | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 92bb0b65d..8f45e0af1 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -2399,6 +2399,7 @@ TBaseVirtualTree = class(TCustomControl) function GetVisible(Node: PVirtualNode): Boolean; function GetVisiblePath(Node: PVirtualNode): Boolean; function HandleDrawSelection(X, Y: Integer): Boolean; + procedure HandleCheckboxClick(pHitNode: PVirtualNode; pKeys: LongInt); function HasVisibleNextSibling(Node: PVirtualNode): Boolean; function HasVisiblePreviousSibling(Node: PVirtualNode): Boolean; procedure ImageListChange(Sender: TObject); @@ -22399,7 +22400,6 @@ procedure TBaseVirtualTree.HandleIncrementalSearch(CharCode: Word); procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitInfo: THitInfo); var - NewCheckState: TCheckState; Node: PVirtualNode; MayEdit: Boolean; @@ -22442,12 +22442,8 @@ procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitI else if hiOnItemCheckBox in HitInfo.HitPositions then begin - NewCheckState := DetermineNextCheckState(HitInfo.HitNode.CheckType, HitInfo.HitNode.CheckState); - if (ssLeft in KeysToShiftState(Message.Keys)) and DoChecking(HitInfo.HitNode, NewCheckState) then - begin - SetCheckStateForAll(NewCheckState, True); - MayEdit := False; - end; + HandleCheckboxClick(HitInfo.HitNode, Message.Keys); + MayEdit := False; end// if hiOnItemCheckBox else begin @@ -22482,6 +22478,22 @@ procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitI //---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.HandleCheckboxClick(pHitNode: PVirtualNode; pKeys: LongInt); +var + NewCheckState: TCheckState; +begin + NewCheckState := DetermineNextCheckState(pHitNode.CheckType, pHitNode.CheckState); + if (ssLeft in KeysToShiftState(pKeys)) and DoChecking(pHitNode, NewCheckState) then + begin + if (Self.SelectedCount > 1) and (Selected[pHitNode]) and not (toSyncCheckboxesWithSelection in TreeOptions.SelectionOptions) then + SetCheckStateForAll(NewCheckState, True) + else + DoCheckClick(pHitNode, NewCheckState); + end;//if ssLeft +end; + +//---------------------------------------------------------------------------------------------------------------------- + procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: THitInfo); // centralized mouse button down handling @@ -22504,7 +22516,6 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T NewNode: Boolean; // Node changed. NeedChangeEvent: Boolean; // change event is required for selection change CanClear: Boolean; - NewCheckState: TCheckState; AltPressed: Boolean; // Pressing the Alt key enables special processing for selection. FullRowDrag: Boolean; // Start dragging anywhere within a node's bound. NodeRect: TRect; @@ -22683,14 +22694,7 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T // check event if hiOnItemCheckBox in HitInfo.HitPositions then begin - NewCheckState := DetermineNextCheckState(HitInfo.HitNode.CheckType, HitInfo.HitNode.CheckState); - if (ssLeft in KeysToShiftState(Message.Keys)) and DoChecking(HitInfo.HitNode, NewCheckState) then - begin - if (Self.SelectedCount > 1) and (Selected[HitInfo.HitNode]) and not (toSyncCheckboxesWithSelection in TreeOptions.SelectionOptions) then - SetCheckStateForAll(NewCheckState, True) - else - DoCheckClick(HitInfo.HitNode, NewCheckState); - end;//if ssLeft + HandleCheckboxClick(HitInfo.HitNode, Message.Keys); Exit; end; From 0a67daca169bd789e9e1c59ef8257a6f7e0f6bb8 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 25 Mar 2021 11:40:59 +0100 Subject: [PATCH 183/681] added comment about application freeze --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 8f45e0af1..2e85625b1 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -32682,7 +32682,7 @@ procedure TBaseVirtualTree.UpdateHorizontalScrollBar(DoRepaint: Boolean); ScrollInfo.nPage := Max(0, ClientWidth + 1); ScrollInfo.fMask := SIF_ALL or ScrollMasks[FScrollBarOptions.AlwaysVisible]; - SetScrollInfo(Handle, SB_HORZ, ScrollInfo, DoRepaint); + SetScrollInfo(Handle, SB_HORZ, ScrollInfo, DoRepaint); // 1 app freeze seen here in TreeSize 8.1.0 after ScaleForPpi() if DoRepaint then RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 end From a7760850f76bcc0e4c5bf3fc0cdc3ce6e7669db9 Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Mon, 29 Mar 2021 17:09:08 +1100 Subject: [PATCH 184/681] Initial dpm support. --- Virtual-TreeView.dspec | 162 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 Virtual-TreeView.dspec diff --git a/Virtual-TreeView.dspec b/Virtual-TreeView.dspec new file mode 100644 index 000000000..a8826551b --- /dev/null +++ b/Virtual-TreeView.dspec @@ -0,0 +1,162 @@ +{ + "metadata": { + "id": "JAM.VirtualTreeView", + "version": "7.4.0", + "description": "Virtual TreeView VCL Component", + "authors": "Joachim Marder", + "projectUrl": "https://github.com/Virtual-TreeView/Virtual-TreeView", + "license": "MPL-1.1, LGPL-2.0+", + "copyright": "Various, See project page", + "tags": "VCL, TreeView" + }, + "targetPlatforms": [ + { + "compiler": "XE3", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "17" + } + }, + { + "compiler": "XE4", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "18" + } + }, + { + "compiler": "XE5", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "19" + } + }, + { + "compiler": "XE6", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "20" + } + }, + { + "compiler": "XE7", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "21" + } + }, + { + "compiler": "XE8", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "22" + } + }, + { + "compiler": "10.0", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "23", + "compiler" : "$compilerNoPoint$" + } + }, + { + "compiler": "10.1", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "24" + } + }, + { + "compiler": "10.2", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "25" + } + }, + { + "compiler": "10.3", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "26" + } + }, + { + "compiler": "10.4", + "platforms": "Win32, Win64", + "template": "default", + "variables" : { + "libsuffix" : "27" + } + } + ], + "templates": [ + { + "comment": "all other compiler versions follow normal folder naming", + "name": "default", + "source": [ + { + "src": ".\\Source\\*.pas", + "dest": "Source" + }, + { + "src": ".\\Source\\*.res", + "dest": "Source" + }, + { + "src": ".\\Design\\*.*", + "dest": "Design" + }, + { + "src": ".\\packages\\Rad Studio $compiler$\\**", + "flatten": false, + "dest": "packages\\Rad Studio $compiler$" + } + ], + "searchPaths": [ + { + "path": "Source", + "source": true + } + ], + "build": [ + { + "id": "VirtualTreesR", + "project": ".\\Packages\\Rad Studio $compiler$\\VirtualTreesR.dproj", + "buildForDesign": true, + "buildForDesignComment" : "when true, will also build win32 if the platform is not win32, so that other packages that need this for design will work" + }, + { + "id": "VirtualTreesD", + "project": ".\\Packages\\Rad Studio $compiler$\\VirtualTreesD.dproj", + "designOnly" : true, + "designOnlyComment" : "designOnly forces compilation with win32 compiler" + } + ], + "runtime": [ + { + "buildId": "VirtualTreesR", + "src": "bin\\VirtualTreesR$libsuffix$.bpl", + "copyLocal": true + } + ], + "design": [ + { + "buildId": "VirtualTreesD", + "src": "bin\\VirtualTreesD$libsuffix$.bpl", + "install": true + } + ] + } + ] +} From 52f64d2fda4bcf21e970482adab18039cda3b452 Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Tue, 30 Mar 2021 14:59:12 +1100 Subject: [PATCH 185/681] Possible fix for #1027 Don't call AutoScale from header changescale when changescale called for dpi change. --- Source/VirtualTrees.pas | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 2e85625b1..058d877a1 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1349,7 +1349,7 @@ TVTHeader = class(TPersistent) procedure AutoScale(); virtual; function CanSplitterResize(P: TPoint): Boolean; function CanWriteColumns: Boolean; virtual; - procedure ChangeScale(M, D: TDimension); virtual; + procedure ChangeScale(M, D: TDimension; isDpiChange: Boolean); virtual; function DetermineSplitterIndex(P: TPoint): Boolean; virtual; procedure DoAfterAutoFitColumn(Column: TColumnIndex); virtual; procedure DoAfterColumnWidthTracking(Column: TColumnIndex); virtual; @@ -9984,11 +9984,13 @@ function TVTHeader.CanWriteColumns: Boolean; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.ChangeScale(M, D: Integer); +procedure TVTHeader.ChangeScale(M, D: Integer; isDpiChange: Boolean); var I: Integer; begin // This method is only executed if toAutoChangeScale is set + FMinHeight := MulDiv(FMinHeight, M, D); + FMaxHeight := MulDiv(FMaxHeight, M, D); Self.Height := MulDiv(FHeight, M, D); if not ParentFont then Font.Height := MulDiv(Font.Height, M, D); @@ -9997,7 +9999,8 @@ procedure TVTHeader.ChangeScale(M, D: Integer); begin Self.FColumns[I].Width := MulDiv(Self.FColumns[I].Width, M, D); end;//for I - AutoScale(); + if not isDpiChange then + AutoScale(); end; //---------------------------------------------------------------------------------------------------------------------- @@ -18503,7 +18506,7 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; else Flags := DefaultScalingFlags; // Important for #677 if (sfHeight in Flags) then begin - FHeader.ChangeScale(M, D); + FHeader.ChangeScale(M, D, {$if CompilerVersion >= 31}isDpiChange{$ELSE} M <> D{$ifend}); SetDefaultNodeHeight(MulDiv(FDefaultNodeHeight, M, D)); Indent := MulDiv(Indent, M, D); FTextMargin := MulDiv(FTextMargin, M, D); From b30a416f1f92d1c4a9f576fbb7e9c88288d901c1 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 30 Mar 2021 19:20:05 +0200 Subject: [PATCH 186/681] Issue #1025: Now using LibSuffix Auto for RAD Studio 10.4 to have standard libsuffix. Some IDE and Project Magician induced changes. --- .../RAD Studio 10.4/VirtualTreeView.groupproj | 96 +++++++++---------- Packages/RAD Studio 10.4/VirtualTreesD.dpk | 3 +- Packages/RAD Studio 10.4/VirtualTreesD.dproj | 48 +++++----- Packages/RAD Studio 10.4/VirtualTreesR.dpk | 4 +- Packages/RAD Studio 10.4/VirtualTreesR.dproj | 54 ++++------- 5 files changed, 92 insertions(+), 113 deletions(-) diff --git a/Packages/RAD Studio 10.4/VirtualTreeView.groupproj b/Packages/RAD Studio 10.4/VirtualTreeView.groupproj index b0f33e510..980342db8 100644 --- a/Packages/RAD Studio 10.4/VirtualTreeView.groupproj +++ b/Packages/RAD Studio 10.4/VirtualTreeView.groupproj @@ -1,48 +1,48 @@ - - - {CC6A9541-DD5C-4BCD-8914-016D8D2EAB3B} - - - - - - - VirtualTreesR.dproj - - - - Default.Personality.12 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + {CC6A9541-DD5C-4BCD-8914-016D8D2EAB3B} + + + + + + + VirtualTreesR.dproj + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Packages/RAD Studio 10.4/VirtualTreesD.dpk b/Packages/RAD Studio 10.4/VirtualTreesD.dpk index 92f96d9a1..da5428de5 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesD.dpk +++ b/Packages/RAD Studio 10.4/VirtualTreesD.dpk @@ -27,7 +27,7 @@ package VirtualTreesD; {$DEFINE RELEASE} {$ENDIF IMPLICITBUILDING} {$DESCRIPTION 'VirtualTreeView Controls'} -{$LIBSUFFIX '27'} +{$LIBSUFFIX AUTO} {$DESIGNONLY} {$IMPLICITBUILD OFF} @@ -39,4 +39,3 @@ contains VirtualTreesReg in '..\..\Design\VirtualTreesReg.pas'; end. - diff --git a/Packages/RAD Studio 10.4/VirtualTreesD.dproj b/Packages/RAD Studio 10.4/VirtualTreesD.dproj index a571731f8..0a6e6033d 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesD.dproj +++ b/Packages/RAD Studio 10.4/VirtualTreesD.dproj @@ -1,14 +1,14 @@  - {A34BA07B-19B6-4C21-9DEE-65FCA52D00AB} - VirtualTreesD.dpk True - Release Package - VCL + Release DCC32 - 19.0 + VCL + VirtualTreesD.dpk Win32 + {A34BA07B-19B6-4C21-9DEE-65FCA52D00AB} + 19.2 1 @@ -31,38 +31,33 @@ VirtualTreesD - ..\..\Source + All .\$(Platform)\$(Config) - true VirtualTreeView Controls - All - 27 - true - ..\..\source;.\$(Platform)\$(Config);$(DCC_UnitSearchPath) + ..\..\Source + 00400000 System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) - 1053 - false + true + ..\..\source;.\$(Platform)\$(Config);$(DCC_UnitSearchPath) + true + $(Auto) + true true - 00400000 CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - false - true - false - false - false + 1053 - $(BDS)\BIN\Bds.exe Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) vcl;VirtualTreesR;$(DCC_UsePackage) + $(BDS)\BIN\Bds.exe RELEASE;$(DCC_Define) DEBUG;$(DCC_Define) - false true + false @@ -72,10 +67,6 @@ - - Cfg_2 - Base - Base @@ -83,6 +74,10 @@ Cfg_1 Base + + Cfg_2 + Base + Delphi.Personality.12 @@ -92,6 +87,7 @@ VirtualTreesD.dpk + True False @@ -119,10 +115,10 @@ 1.0.0.0 - True + False 12 diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dpk b/Packages/RAD Studio 10.4/VirtualTreesR.dpk index 1bf4fd9af..a9e3dd7a1 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dpk @@ -25,7 +25,7 @@ package VirtualTreesR; {$IMAGEBASE $400000} {$DEFINE RELEASE} {$ENDIF IMPLICITBUILDING} -{$LIBSUFFIX '27'} +{$LIBSUFFIX AUTO} {$RUNONLY} {$IMPLICITBUILD OFF} @@ -47,5 +47,3 @@ contains VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas'; end. - - diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dproj b/Packages/RAD Studio 10.4/VirtualTreesR.dproj index fd94a4367..e889d06a1 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dproj @@ -1,14 +1,14 @@  - {B62F3689-96E1-47D5-9FB2-2A2718281FDB} - VirtualTreesR.dpk True - Release Package - VCL + Release DCC32 - 19.0 + VCL + VirtualTreesR.dpk Win32 + {B62F3689-96E1-47D5-9FB2-2A2718281FDB} + 19.2 3 @@ -29,49 +29,35 @@ Base true - - true - Cfg_2 - true - true - - All VirtualTreesR + All .\$(Platform)\$(Config) ..\..\Source + 00400000 + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) true - 27 - true ..\..\source;.\$(Platform)\$(Config);$(DCC_UnitSearchPath) - System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) - 1053 - false + $(Auto) + true true - 00400000 + true CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - false - true - false - false - false + 1053 Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - false + 0 RELEASE;$(DCC_Define) + false 0 - 0 DEBUG;$(DCC_Define) - false true - - - true + false @@ -90,10 +76,6 @@ - - Cfg_2 - Base - Base @@ -101,6 +83,10 @@ Cfg_1 Base + + Cfg_2 + Base + Delphi.Personality.12 @@ -110,6 +96,7 @@ VirtualTreesR.dpk + True False @@ -137,7 +124,6 @@ 1.0.0.0 - True From 409445e7c6fa1060eff1d8dfca3b53be58ca1f71 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 30 Mar 2021 21:21:31 +0200 Subject: [PATCH 187/681] Fix for issue #1027: We need to scale FMinHeight and FMaxHeight of the header as well. --- Source/VirtualTrees.pas | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 2e85625b1..d286b77f8 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -9989,6 +9989,8 @@ procedure TVTHeader.ChangeScale(M, D: Integer); I: Integer; begin // This method is only executed if toAutoChangeScale is set + FMinHeight := MulDiv(FMinHeight, M, D); + FMaxHeight := MulDiv(FMaxHeight, M, D); Self.Height := MulDiv(FHeight, M, D); if not ParentFont then Font.Height := MulDiv(Font.Height, M, D); From 964cfc5339c82a7ba0119f29840b40f3ac00c732 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 30 Mar 2021 21:28:42 +0200 Subject: [PATCH 188/681] Improved fix for issue #981 --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index d286b77f8..4cc977120 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -34636,7 +34636,7 @@ procedure TCustomVirtualStringTree.AddToSelection(Node: PVirtualNode; NotifySync end; if Self.SelectedCount = 1 then FPreviouslySelected.Clear(); - Self.OnGetText(Self, Node, 0, ttNormal, lSelectedNodeCaption); + Self.OnGetText(Self, Node, Header.RestoreSelectionColumnIndex, ttNormal, lSelectedNodeCaption); FPreviouslySelected.Add(lSelectedNodeCaption); end;//if end; @@ -34655,7 +34655,7 @@ procedure TCustomVirtualStringTree.RemoveFromSelection(Node: PVirtualNode); FPreviouslySelected.Clear() else begin - Self.OnGetText(Self, Node, 0, ttNormal, lSelectedNodeCaption); + Self.OnGetText(Self, Node, Header.RestoreSelectionColumnIndex, ttNormal, lSelectedNodeCaption); if FPreviouslySelected.Find(lSelectedNodeCaption, lIndex) then FPreviouslySelected.Delete(lIndex); end;//else From 0cc563029a6e9f9f930964ce4db9377595706be4 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 30 Mar 2021 21:32:10 +0200 Subject: [PATCH 189/681] Fixed issue #1030: TVTHeaderPopupMenu.OnMenuItemClick: DoColumnChange is never called --- Source/VirtualTrees.HeaderPopup.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/VirtualTrees.HeaderPopup.pas b/Source/VirtualTrees.HeaderPopup.pas index e5eed0041..6d003bca1 100644 --- a/Source/VirtualTrees.HeaderPopup.pas +++ b/Source/VirtualTrees.HeaderPopup.pas @@ -156,6 +156,7 @@ procedure TVTHeaderPopupMenu.OnMenuItemClick(Sender: TObject); Options := Options - [coVisible] else Options := Options + [coVisible]; + DoColumnChange(TVTMenuItem(Sender).Tag, coVisible in Options); end; end; end; From 475f919fc8b4d219c254c1f609053ba3cb6391ce Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 31 Mar 2021 14:45:29 +0200 Subject: [PATCH 190/681] Fixed issue #1031: Use cast to WPARAM instead of Integer in PostMessage() --- Source/VirtualTrees.StyleHooks.pas | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index 59d2b3287..de7e5e38e 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -515,7 +515,7 @@ procedure TVclStyleScrollBarsHook.WMLButtonUp(var Msg: TWMMouse); begin if VertSliderState = tsThumbBtnVertPressed then begin - PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_ENDSCROLL, 0)), 0); + PostMessage(Handle, WM_VSCROLL, WPARAM(SmallPoint(SB_ENDSCROLL, 0)), 0); FLeftMouseButtonDown := False; VertSliderState := tsThumbBtnVertNormal; PaintScroll; @@ -533,7 +533,7 @@ procedure TVclStyleScrollBarsHook.WMLButtonUp(var Msg: TWMMouse); begin if HorzSliderState = tsThumbBtnHorzPressed then begin - PostMessage(Handle, WM_HSCROLL, Integer(SmallPoint(SB_ENDSCROLL, 0)), 0); + PostMessage(Handle, WM_HSCROLL, WPARAM(SmallPoint(SB_ENDSCROLL, 0)), 0); FLeftMouseButtonDown := False; HorzSliderState := tsThumbBtnHorzNormal; PaintScroll; @@ -570,7 +570,7 @@ procedure TVclStyleScrollBarsHook.WMMouseMove(var Msg: TWMMouse); SF.fMask := SIF_POS; SF.nPos := Round(ScrollPos); SetScrollInfo(Handle, SB_VERT, SF, False); - PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); + PostMessage(Handle, WM_VSCROLL, WPARAM(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); PaintScroll; Handled := True; @@ -596,7 +596,7 @@ procedure TVclStyleScrollBarsHook.WMMouseMove(var Msg: TWMMouse); SF.fMask := SIF_POS; SF.nPos := Round(ScrollPos); SetScrollInfo(Handle, SB_HORZ, SF, False); - PostMessage(Handle, WM_HSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); + PostMessage(Handle, WM_HSCROLL, WPARAM(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); PaintScroll; Handled := True; From 702cffd87b3680dfcc23c38a768209f7120d09c3 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 31 Mar 2021 16:01:17 +0200 Subject: [PATCH 191/681] Fixed type cast error in Win64 builds after fixing issue #1031. --- Source/VirtualTrees.StyleHooks.pas | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index de7e5e38e..89736440b 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -515,7 +515,7 @@ procedure TVclStyleScrollBarsHook.WMLButtonUp(var Msg: TWMMouse); begin if VertSliderState = tsThumbBtnVertPressed then begin - PostMessage(Handle, WM_VSCROLL, WPARAM(SmallPoint(SB_ENDSCROLL, 0)), 0); + PostMessage(Handle, WM_VSCROLL, WPARAM(UInt32(SmallPoint(SB_ENDSCROLL, 0))), 0); FLeftMouseButtonDown := False; VertSliderState := tsThumbBtnVertNormal; PaintScroll; @@ -533,7 +533,7 @@ procedure TVclStyleScrollBarsHook.WMLButtonUp(var Msg: TWMMouse); begin if HorzSliderState = tsThumbBtnHorzPressed then begin - PostMessage(Handle, WM_HSCROLL, WPARAM(SmallPoint(SB_ENDSCROLL, 0)), 0); + PostMessage(Handle, WM_HSCROLL, WPARAM(UInt32(SmallPoint(SB_ENDSCROLL, 0))), 0); FLeftMouseButtonDown := False; HorzSliderState := tsThumbBtnHorzNormal; PaintScroll; @@ -570,7 +570,7 @@ procedure TVclStyleScrollBarsHook.WMMouseMove(var Msg: TWMMouse); SF.fMask := SIF_POS; SF.nPos := Round(ScrollPos); SetScrollInfo(Handle, SB_VERT, SF, False); - PostMessage(Handle, WM_VSCROLL, WPARAM(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); + PostMessage(Handle, WM_VSCROLL, WPARAM(UInt32(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt))))), 0); PaintScroll; Handled := True; @@ -596,7 +596,7 @@ procedure TVclStyleScrollBarsHook.WMMouseMove(var Msg: TWMMouse); SF.fMask := SIF_POS; SF.nPos := Round(ScrollPos); SetScrollInfo(Handle, SB_HORZ, SF, False); - PostMessage(Handle, WM_HSCROLL, WPARAM(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt)))), 0); + PostMessage(Handle, WM_HSCROLL, WPARAM(UInt32(SmallPoint(SB_THUMBPOSITION, Min(SF.nPos, High(SmallInt))))), 0); PaintScroll; Handled := True; From 3c226c1dcd662f7673642edccb8c93def4f2680a Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 31 Mar 2021 16:43:01 +0200 Subject: [PATCH 192/681] Fixed typos in comments --- Source/VirtualTrees.WorkerThread.pas | 2 +- Source/VirtualTrees.pas | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.WorkerThread.pas b/Source/VirtualTrees.WorkerThread.pas index 679180098..b58d0afbb 100644 --- a/Source/VirtualTrees.WorkerThread.pas +++ b/Source/VirtualTrees.WorkerThread.pas @@ -65,7 +65,7 @@ class procedure TWorkerThread.Dispose(CanBlock: Boolean); WorkerThread.Terminate(); SetEvent(WorkerThread.FWorkEvent); LRef := WorkerThread; - WorkerThread := nil; //Will be freed usinf TThreaf.FreeOnTerminate + WorkerThread := nil; //Will be freed usinf TThread.FreeOnTerminate if CanBlock then LRef.Free; end; diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index a5318ae26..ea6e79bb4 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -9654,11 +9654,12 @@ procedure TVTHeader.AutoScale(); begin if toAutoChangeScale in Treeview.TreeOptions.AutoOptions then begin - // Find the largest Columns[].Spacing + // Ensure a minimum header size based on the font, so that all text is visible. + // First find the largest Columns[].Spacing lMaxHeight := 0; for I := 0 to Self.Columns.Count - 1 do lMaxHeight := Max(lMaxHeight, Columns[I].Spacing); - // Calculate the required size based on the font, this is important as the use migth just vave increased the size of the icon font + // Calculate the required height based on the font, this is important as the user might just have increased the size of the system icon font. with TBitmap.Create do try Canvas.Font.Assign(FFont); @@ -9666,7 +9667,7 @@ procedure TVTHeader.AutoScale(); finally Free; end; - // Get the maximum of the scaled original value an + // Get the maximum of the scaled original value and the minimum needed header height. lMaxHeight := Max(lMaxHeight, FHeight); // Set the calculated size Self.SetHeight(lMaxHeight); From 7feb3820e84a3b1c4d15eaccd5e9a8817fc2077c Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 1 Apr 2021 09:33:28 +0200 Subject: [PATCH 193/681] Increased VTVersion --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index ea6e79bb4..f2eab9480 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -86,7 +86,7 @@ interface {$ENDIF} const - VTVersion = '7.4.0' deprecated 'This const is going to be removed in a future version'; + VTVersion = '7.5.0' deprecated 'This const is going to be removed in a future version'; const VTTreeStreamVersion = 3; From 92c905c769f8eee5c676bf322685794b7f4b8363 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 1 Apr 2021 09:47:02 +0200 Subject: [PATCH 194/681] Updated release instructions. --- MAKEFILE | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/MAKEFILE b/MAKEFILE index 2ad074fbd..ca7dc65dd 100644 --- a/MAKEFILE +++ b/MAKEFILE @@ -56,6 +56,7 @@ _release: #Download e.g. from: ftp://ftp.info-zip.org/pub/infozip/win32/ ZIP -9 -r .\VirtualTreeView.zip INSTALL.txt Changes.txt Source Design Packages Demos Contributions Help\VirtualTreeview.chm -i *.pas -i *.dpk -i *.groupproj -i *.dproj -i *.cbproj -i *.hlp -i *.rc -i *.res -i *.cfg -i *.dpr -i *.dof -i *.bpr -i *.dfm -i *.cpp -i *.inc -i *.dcr -i *.chm -i *.png -i *.js -i *.txt -i *.bmp -i *.uni ECHO Source code zip archive "VirtualTreeView.zip" created. - ECHO !!!Please ensure that the const TVTVersion is correct!!! - ECHO !!!Please add version number to ZIP file name!!! - ECHO !!!Please create release at: https://github.com/Virtual-TreeView/Virtual-TreeView/releases + ECHO !!! Please ensure that the const TVTVersion is correct or remove const!!! + ECHO !!! Please add version number to ZIP file name!!! + ECHO !!! Please create release at: https://github.com/Virtual-TreeView/Virtual-TreeView/releases + ECHO !!! Let JAM web-team upload the file to our server at https://www.jam-software.com/virtual-treeview From a3f5d19ae498d323cc7aec7fd686403101963a60 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 1 Apr 2021 13:38:32 +0200 Subject: [PATCH 195/681] V7.5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eec3bb083..36dde966a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Virtual Treeview is a Delphi treeview control built from ground up. Many years o I don't use C++ Builder and my experience with it is very limited. This makes it difficult to take care about bugs that are reported in C++ Builder and to maintain the C++ Builder packages. I would be great if someone would volunteer to do this. Please contact me at joachim.marder+CPP@gmail.com. ### Downloads -**V7.4** official release for **RAD Studio XE3 to 10.4 Rio**: [JAM Software](https://www.jam-software.com/virtual-treeview/VirtualTreeView.zip) ([Changes](https://github.com/Virtual-TreeView/Virtual-TreeView/milestone/16?closed=1)) +**V7.5** official release for **RAD Studio XE3 to 10.4.2 Rio**: [JAM Software](https://www.jam-software.com/virtual-treeview/VirtualTreeView.zip) ([Changes](https://github.com/JAM-Software/Virtual-TreeView/issues?q=is%3Aissue+milestone%3AV7.5+is%3Aclosed)) An experimental **FireMonkey** port can be found here: [livius2/Virtual-TreeView](https://github.com/livius2/Virtual-TreeView) From 4f269ce39cce40b7b09c99124039ee39e918133e Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 11 Apr 2021 18:13:36 +0200 Subject: [PATCH 196/681] Fixed issue #1032: Updated copyright string. --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index f2eab9480..c387da97e 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -4063,7 +4063,7 @@ implementation // Do not modify the copyright in any way! Usage of this unit is prohibited without the copyright notice // in the compiled binary file. - Copyright: string = 'Virtual Treeview © 1999, 2010, 2016 Mike Lischke, Joachim Marder'; + Copyright: string = 'Virtual Treeview © 1999-2021 Mike Lischke, Joachim Marder'; var StandardOLEFormat: TFormatEtc = ( From 549b7e5ddf0c81c6029c4a93ab8e2fafd84373df Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 12 Apr 2021 21:36:50 +0200 Subject: [PATCH 197/681] We only need one package for C++ Builder 10.X, they are identical. --- .../CBuilder 10.X/VirtualTreeView.groupproj | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Packages/CBuilder 10.X/VirtualTreeView.groupproj diff --git a/Packages/CBuilder 10.X/VirtualTreeView.groupproj b/Packages/CBuilder 10.X/VirtualTreeView.groupproj new file mode 100644 index 000000000..8214df0fc --- /dev/null +++ b/Packages/CBuilder 10.X/VirtualTreeView.groupproj @@ -0,0 +1,48 @@ + + + {85AC5363-0DD3-4B39-8709-5987A7DA817E} + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 4cf33e48445a41139a039ee143301477afaedbb7 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 12 Apr 2021 21:37:39 +0200 Subject: [PATCH 198/681] We only need one package for C++ Builder 10.X, they are identical. --- Packages/CBuilder 10.X/VirtualTreesCD.cbproj | 261 ++++++ Packages/CBuilder 10.X/VirtualTreesCD.cpp | 17 + Packages/CBuilder 10.X/VirtualTreesCR.cbproj | 806 +++++++++++++++++++ Packages/CBuilder 10.X/VirtualTreesCR.cpp | 17 + 4 files changed, 1101 insertions(+) create mode 100644 Packages/CBuilder 10.X/VirtualTreesCD.cbproj create mode 100644 Packages/CBuilder 10.X/VirtualTreesCD.cpp create mode 100644 Packages/CBuilder 10.X/VirtualTreesCR.cbproj create mode 100644 Packages/CBuilder 10.X/VirtualTreesCR.cpp diff --git a/Packages/CBuilder 10.X/VirtualTreesCD.cbproj b/Packages/CBuilder 10.X/VirtualTreesCD.cbproj new file mode 100644 index 000000000..d1573f0a6 --- /dev/null +++ b/Packages/CBuilder 10.X/VirtualTreesCD.cbproj @@ -0,0 +1,261 @@ + + + {DE1FB54C-6852-4F59-B4A5-7718E6069FE8} + VirtualTreesCD.cpp + 18.4 + Release + None + True + Win32 + 1 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + 240 + VirtualTree CBuilder designtime package + $(BDSCOMMONDIR)\hpp\$(Platform)\ + VirtualTreesCD + true + ..\..\source;$(DCC_UnitSearchPath) + 4108 + System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace) + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 6 + -LUDesignIDE + true + bpl + CppPackage + true + true + true + All + true + ..\..\Source\;..\..\Design\;$(BDS)\include;$(BDS)\include\windows;$(BDS)\include\windows\rtl;$(BDS)\include\windows\vcl;$(BDS)\include\windows\crtl;$(BDS)\include\windows\sdk;$(IncludePath) + ..\..\Design\;$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;..\..\source;$(ILINK_LibraryPath) + false + true + true + 128 + + + true + true + $(BDS)\bin\default_app.manifest + 1033 + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + + + 1033 + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + + + Debug\;$(IncludePath) + false + true + false + true + false + Debug + None + DEBUG + true + true + true + $(BDS)\lib\debug;$(ILINK_LibraryPath) + true + Full + true + + + _DEBUG;$(Defines) + + + Release\;$(IncludePath) + Release + $(BDS)\lib\release;$(ILINK_LibraryPath) + None + + + 1033 + NDEBUG;$(Defines) + + + + 3 + + + 10 + + + 5 + + + 1 + + + 4 + + + 7 + + + 8 + + + 0 + + + 9 + + + 6 + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + + CPlusPlusBuilder.Personality.12 + CppPackage + + + + VirtualTreesCD.cpp + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 4108 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + + + + + + + False + + False + + True + False + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + False + True + True + False + + + + True + False + + + 12 + + + diff --git a/Packages/CBuilder 10.X/VirtualTreesCD.cpp b/Packages/CBuilder 10.X/VirtualTreesCD.cpp new file mode 100644 index 000000000..37aacb559 --- /dev/null +++ b/Packages/CBuilder 10.X/VirtualTreesCD.cpp @@ -0,0 +1,17 @@ +//--------------------------------------------------------------------------- + +#include +#pragma hdrstop +#pragma package(smart_init) +//--------------------------------------------------------------------------- + +// Package source. +//--------------------------------------------------------------------------- + + +#pragma argsused +int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) +{ + return 1; +} +//--------------------------------------------------------------------------- diff --git a/Packages/CBuilder 10.X/VirtualTreesCR.cbproj b/Packages/CBuilder 10.X/VirtualTreesCR.cbproj new file mode 100644 index 000000000..66402b8c0 --- /dev/null +++ b/Packages/CBuilder 10.X/VirtualTreesCR.cbproj @@ -0,0 +1,806 @@ + + + {FE6B0D67-74B6-4E30-8AED-CB2B3E77A51F} + VirtualTreesCR.cpp + 18.4 + Release + VCL + True + Win64 + 3 + Package + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + 240 + true + VirtualTree CBuilder runtime package + $(BDSCOMMONDIR)\hpp\$(Platform) + VirtualTreesCR + Shell32.dll;$(ILINK_DelayLoadDll) + $(BDS)\lib;$(BDS)\lib\$(Platform);$(BDS)\lib\$(Platform)\$(Config);$(DCC_UnitSearchPath) + true + 4108 + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + true + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + 6 + bpl + CppPackage + true + true + true + All + true + ..\..\Source\;$(BDS)\include;$(BDS)\include\windows;$(BDS)\include\windows\rtl;$(BDS)\include\windows\vcl;$(BDS)\include\windows\crtl;$(BDS)\include\windows\sdk;$(IncludePath) + ..\..\Source\;$(BDS)\lib;$(BDS)\lib\$(platform);$(BDS)\lib\$(Platform)\$(Config);$(ILINK_LibraryPath) + false + true + true + 128 + + + $(BDS)\Bin\BDS.EXE + true + $(BDSINCLUDE)\windows\vcl;$(IncludePath) + C:\Program Files (x86)\Embarcadero\DelphiXE5\lib\win32\release\psdk\;$(ILINK_LibraryPath) + true + $(BDS)\bin\default_app.manifest + 1033 + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + vclwinx;$(PackageImports) + + + $(BDSINCLUDE)\windows\vcl;$(IncludePath) + 1033 + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + vclwinx;$(PackageImports) + + + Debug\;$(IncludePath) + false + true + false + true + false + Debug + None + DEBUG + true + true + true + $(BDS)\lib\debug;$(ILINK_LibraryPath) + true + Full + true + + + _DEBUG;$(Defines) + + + Release\;$(IncludePath) + Release + $(BDS)\lib\release;$(ILINK_LibraryPath) + None + + + 1033 + NDEBUG;$(Defines) + + + 1033 + + + + 9 + true + + + 9 + true + + + 3 + + + 4 + + + 8 + + + 0 + + + 14 + + + 15 + + + 16 + + + 16 + + + 16 + + + 17 + + + 16 + + + 17 + + + 17 + + + 17 + + + 17 + + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + + + + + CPlusPlusBuilder.Personality.12 + CppPackage + + + + VirtualTreesCR.cpp + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 4108 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + + + + + + + False + + False + + True + False + + + Embarcadero C++Builder Office 2000 Servers Package + Embarcadero C++Builder Office XP Servers Package + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + False + True + True + False + + + + True + True + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + VirtualTreesCR.bpl + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + 1 + + + Contents\MacOS + 0 + + + + + classes + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + + + library\lib\mips + 1 + + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + + + res\values + 1 + + + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + 12 + + + + diff --git a/Packages/CBuilder 10.X/VirtualTreesCR.cpp b/Packages/CBuilder 10.X/VirtualTreesCR.cpp new file mode 100644 index 000000000..37aacb559 --- /dev/null +++ b/Packages/CBuilder 10.X/VirtualTreesCR.cpp @@ -0,0 +1,17 @@ +//--------------------------------------------------------------------------- + +#include +#pragma hdrstop +#pragma package(smart_init) +//--------------------------------------------------------------------------- + +// Package source. +//--------------------------------------------------------------------------- + + +#pragma argsused +int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) +{ + return 1; +} +//--------------------------------------------------------------------------- From e55eb9d7b0eeeccde42576f7cb8bc9f3a69a4d85 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 12 Apr 2021 21:38:11 +0200 Subject: [PATCH 199/681] We only need one package for C++ Builder 10.X, they are identical. --- .../CBuilder 10.1/VirtualTreeView.groupproj | 48 -- Packages/CBuilder 10.1/VirtualTreesCD.cbproj | 261 ------ Packages/CBuilder 10.1/VirtualTreesCD.cpp | 17 - Packages/CBuilder 10.1/VirtualTreesCR.cbproj | 776 ----------------- Packages/CBuilder 10.1/VirtualTreesCR.cpp | 17 - .../CBuilder 10.2/VirtualTreeView.groupproj | 48 -- Packages/CBuilder 10.2/VirtualTreesCD.cbproj | 261 ------ Packages/CBuilder 10.2/VirtualTreesCD.cpp | 17 - Packages/CBuilder 10.2/VirtualTreesCR.cbproj | 806 ------------------ Packages/CBuilder 10.2/VirtualTreesCR.cpp | 17 - 10 files changed, 2268 deletions(-) delete mode 100644 Packages/CBuilder 10.1/VirtualTreeView.groupproj delete mode 100644 Packages/CBuilder 10.1/VirtualTreesCD.cbproj delete mode 100644 Packages/CBuilder 10.1/VirtualTreesCD.cpp delete mode 100644 Packages/CBuilder 10.1/VirtualTreesCR.cbproj delete mode 100644 Packages/CBuilder 10.1/VirtualTreesCR.cpp delete mode 100644 Packages/CBuilder 10.2/VirtualTreeView.groupproj delete mode 100644 Packages/CBuilder 10.2/VirtualTreesCD.cbproj delete mode 100644 Packages/CBuilder 10.2/VirtualTreesCD.cpp delete mode 100644 Packages/CBuilder 10.2/VirtualTreesCR.cbproj delete mode 100644 Packages/CBuilder 10.2/VirtualTreesCR.cpp diff --git a/Packages/CBuilder 10.1/VirtualTreeView.groupproj b/Packages/CBuilder 10.1/VirtualTreeView.groupproj deleted file mode 100644 index 8214df0fc..000000000 --- a/Packages/CBuilder 10.1/VirtualTreeView.groupproj +++ /dev/null @@ -1,48 +0,0 @@ - - - {85AC5363-0DD3-4B39-8709-5987A7DA817E} - - - - - - - - - - - Default.Personality.12 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Packages/CBuilder 10.1/VirtualTreesCD.cbproj b/Packages/CBuilder 10.1/VirtualTreesCD.cbproj deleted file mode 100644 index 1db5fd5f0..000000000 --- a/Packages/CBuilder 10.1/VirtualTreesCD.cbproj +++ /dev/null @@ -1,261 +0,0 @@ - - - {DE1FB54C-6852-4F59-B4A5-7718E6069FE8} - VirtualTreesCD.cpp - 18.2 - Release - None - True - Win32 - 1 - Package - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - true - Cfg_2 - true - true - - - 240 - VirtualTree CBuilder designtime package - $(BDSCOMMONDIR)\hpp\$(Platform)\ - VirtualTreesCD - true - ..\..\source;$(DCC_UnitSearchPath) - 4108 - System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace) - true - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - 6 - -LUDesignIDE - true - bpl - CppPackage - true - true - true - All - true - ..\..\Source\;..\..\Design\;$(BDS)\include;$(BDS)\include\windows;$(BDS)\include\windows\rtl;$(BDS)\include\windows\vcl;$(BDS)\include\windows\crtl;$(BDS)\include\windows\sdk;$(IncludePath) - ..\..\Design\;$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;..\..\source;$(ILINK_LibraryPath) - false - true - true - 128 - - - true - true - $(BDS)\bin\default_app.manifest - 1033 - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - - - 1033 - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) - - - Debug\;$(IncludePath) - false - true - false - true - false - Debug - None - DEBUG - true - true - true - $(BDS)\lib\debug;$(ILINK_LibraryPath) - true - Full - true - - - _DEBUG;$(Defines) - - - Release\;$(IncludePath) - Release - $(BDS)\lib\release;$(ILINK_LibraryPath) - None - - - 1033 - NDEBUG;$(Defines) - - - - 3 - - - 10 - - - 5 - - - 1 - - - 4 - - - 7 - - - 8 - - - 0 - - - 9 - - - 6 - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - - CPlusPlusBuilder.Personality.12 - CppPackage - - - - VirtualTreesCD.cpp - - - False - False - 1 - 0 - 0 - 0 - False - False - False - False - False - 4108 - 1252 - - - - - 1.0.0.0 - - - - - - 1.0.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - - - - - - - False - - False - - True - False - - - Embarcadero C++Builder Office 2000 Servers Package - Embarcadero C++Builder Office XP Servers Package - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - - - False - True - True - False - - - - True - False - - - 12 - - - diff --git a/Packages/CBuilder 10.1/VirtualTreesCD.cpp b/Packages/CBuilder 10.1/VirtualTreesCD.cpp deleted file mode 100644 index 37aacb559..000000000 --- a/Packages/CBuilder 10.1/VirtualTreesCD.cpp +++ /dev/null @@ -1,17 +0,0 @@ -//--------------------------------------------------------------------------- - -#include -#pragma hdrstop -#pragma package(smart_init) -//--------------------------------------------------------------------------- - -// Package source. -//--------------------------------------------------------------------------- - - -#pragma argsused -int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) -{ - return 1; -} -//--------------------------------------------------------------------------- diff --git a/Packages/CBuilder 10.1/VirtualTreesCR.cbproj b/Packages/CBuilder 10.1/VirtualTreesCR.cbproj deleted file mode 100644 index 6545bb08e..000000000 --- a/Packages/CBuilder 10.1/VirtualTreesCR.cbproj +++ /dev/null @@ -1,776 +0,0 @@ - - - {FE6B0D67-74B6-4E30-8AED-CB2B3E77A51F} - VirtualTreesCR.cpp - 18.2 - Release - VCL - True - Win32 - 3 - Package - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - true - Cfg_2 - true - true - - - true - Cfg_2 - true - true - - - 240 - true - VirtualTree CBuilder runtime package - $(BDSCOMMONDIR)\hpp\$(Platform) - VirtualTreesCR - Shell32.dll;$(ILINK_DelayLoadDll) - $(BDS)\lib;$(BDS)\lib\$(Platform);$(BDS)\lib\$(Platform)\$(Config);$(DCC_UnitSearchPath) - true - 4108 - System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) - true - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - 6 - bpl - CppPackage - true - true - true - All - true - ..\..\Source\;$(BDS)\include;$(BDS)\include\windows;$(BDS)\include\windows\rtl;$(BDS)\include\windows\vcl;$(BDS)\include\windows\crtl;$(BDS)\include\windows\sdk;$(IncludePath) - ..\..\Source\;$(BDS)\lib;$(BDS)\lib\$(platform);$(BDS)\lib\$(Platform)\$(Config);$(ILINK_LibraryPath) - false - true - true - 128 - - - $(BDS)\Bin\BDS.EXE - true - $(BDSINCLUDE)\windows\vcl;$(IncludePath) - C:\Program Files (x86)\Embarcadero\DelphiXE5\lib\win32\release\psdk\;$(ILINK_LibraryPath) - true - $(BDS)\bin\default_app.manifest - 1033 - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - - - $(BDSINCLUDE)\windows\vcl;$(IncludePath) - 1033 - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) - - - Debug\;$(IncludePath) - false - true - false - true - false - Debug - None - DEBUG - true - true - true - $(BDS)\lib\debug;$(ILINK_LibraryPath) - true - Full - true - - - _DEBUG;$(Defines) - - - Release\;$(IncludePath) - Release - $(BDS)\lib\release;$(ILINK_LibraryPath) - None - - - 1033 - NDEBUG;$(Defines) - - - 1033 - - - - 1 - - - 9 - true - - - 4 - - - 7 - - - 8 - - - 0 - - - 6 - - - 16 - - - 16 - - - 16 - - - 17 - - - 17 - - - 17 - - - 17 - - - 17 - - - 17 - - - 17 - - - 17 - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - - CPlusPlusBuilder.Personality.12 - CppPackage - - - - VirtualTreesCR.cpp - - - False - False - 1 - 0 - 0 - 0 - False - False - False - False - False - 4108 - 1252 - - - - - 1.0.0.0 - - - - - - 1.0.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - - - - - - - False - - False - - True - False - - - Embarcadero C++Builder-Package für Office 2000-Server - Embarcadero C++Builder-Package für Office XP-Server - Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver - Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server - - - False - True - True - False - - - - True - True - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - VirtualTreesCR.bpl - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - 0 - .dll;.bpl - - - 1 - .dylib - - - - - Contents\Resources - 1 - - - - - classes - 1 - - - - - res\drawable-xxhdpi - 1 - - - - - Contents\MacOS - 0 - - - 1 - - - - - library\lib\mips - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 0 - - - 1 - - - 1 - - - library\lib\armeabi-v7a - 1 - - - 1 - - - - - 0 - - - 1 - .framework - - - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - - library\lib\armeabi - 1 - - - - - 0 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - res\drawable-normal - 1 - - - - - res\drawable-xhdpi - 1 - - - - - res\drawable-large - 1 - - - - - 1 - - - 1 - - - 1 - - - - - - library\lib\armeabi-v7a - 1 - - - - - res\drawable-hdpi - 1 - - - - - - - Assets - 1 - - - Assets - 1 - - - - - 1 - - - 1 - - - 1 - - - - - res\values - 1 - - - - - res\drawable-small - 1 - - - - - res\drawable - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - - - res\drawable - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - library\lib\armeabi-v7a - 1 - - - - - 0 - .bpl - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - - - res\drawable-mdpi - 1 - - - - - res\drawable-xlarge - 1 - - - - - res\drawable-ldpi - 1 - - - - - - - - - - - - - - 12 - - - - diff --git a/Packages/CBuilder 10.1/VirtualTreesCR.cpp b/Packages/CBuilder 10.1/VirtualTreesCR.cpp deleted file mode 100644 index 37aacb559..000000000 --- a/Packages/CBuilder 10.1/VirtualTreesCR.cpp +++ /dev/null @@ -1,17 +0,0 @@ -//--------------------------------------------------------------------------- - -#include -#pragma hdrstop -#pragma package(smart_init) -//--------------------------------------------------------------------------- - -// Package source. -//--------------------------------------------------------------------------- - - -#pragma argsused -int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) -{ - return 1; -} -//--------------------------------------------------------------------------- diff --git a/Packages/CBuilder 10.2/VirtualTreeView.groupproj b/Packages/CBuilder 10.2/VirtualTreeView.groupproj deleted file mode 100644 index 8214df0fc..000000000 --- a/Packages/CBuilder 10.2/VirtualTreeView.groupproj +++ /dev/null @@ -1,48 +0,0 @@ - - - {85AC5363-0DD3-4B39-8709-5987A7DA817E} - - - - - - - - - - - Default.Personality.12 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Packages/CBuilder 10.2/VirtualTreesCD.cbproj b/Packages/CBuilder 10.2/VirtualTreesCD.cbproj deleted file mode 100644 index d1573f0a6..000000000 --- a/Packages/CBuilder 10.2/VirtualTreesCD.cbproj +++ /dev/null @@ -1,261 +0,0 @@ - - - {DE1FB54C-6852-4F59-B4A5-7718E6069FE8} - VirtualTreesCD.cpp - 18.4 - Release - None - True - Win32 - 1 - Package - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - true - Cfg_2 - true - true - - - 240 - VirtualTree CBuilder designtime package - $(BDSCOMMONDIR)\hpp\$(Platform)\ - VirtualTreesCD - true - ..\..\source;$(DCC_UnitSearchPath) - 4108 - System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace) - true - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - 6 - -LUDesignIDE - true - bpl - CppPackage - true - true - true - All - true - ..\..\Source\;..\..\Design\;$(BDS)\include;$(BDS)\include\windows;$(BDS)\include\windows\rtl;$(BDS)\include\windows\vcl;$(BDS)\include\windows\crtl;$(BDS)\include\windows\sdk;$(IncludePath) - ..\..\Design\;$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;..\..\source;$(ILINK_LibraryPath) - false - true - true - 128 - - - true - true - $(BDS)\bin\default_app.manifest - 1033 - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - - - 1033 - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) - - - Debug\;$(IncludePath) - false - true - false - true - false - Debug - None - DEBUG - true - true - true - $(BDS)\lib\debug;$(ILINK_LibraryPath) - true - Full - true - - - _DEBUG;$(Defines) - - - Release\;$(IncludePath) - Release - $(BDS)\lib\release;$(ILINK_LibraryPath) - None - - - 1033 - NDEBUG;$(Defines) - - - - 3 - - - 10 - - - 5 - - - 1 - - - 4 - - - 7 - - - 8 - - - 0 - - - 9 - - - 6 - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - - CPlusPlusBuilder.Personality.12 - CppPackage - - - - VirtualTreesCD.cpp - - - False - False - 1 - 0 - 0 - 0 - False - False - False - False - False - 4108 - 1252 - - - - - 1.0.0.0 - - - - - - 1.0.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - - - - - - - False - - False - - True - False - - - Embarcadero C++Builder Office 2000 Servers Package - Embarcadero C++Builder Office XP Servers Package - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - - - False - True - True - False - - - - True - False - - - 12 - - - diff --git a/Packages/CBuilder 10.2/VirtualTreesCD.cpp b/Packages/CBuilder 10.2/VirtualTreesCD.cpp deleted file mode 100644 index 37aacb559..000000000 --- a/Packages/CBuilder 10.2/VirtualTreesCD.cpp +++ /dev/null @@ -1,17 +0,0 @@ -//--------------------------------------------------------------------------- - -#include -#pragma hdrstop -#pragma package(smart_init) -//--------------------------------------------------------------------------- - -// Package source. -//--------------------------------------------------------------------------- - - -#pragma argsused -int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) -{ - return 1; -} -//--------------------------------------------------------------------------- diff --git a/Packages/CBuilder 10.2/VirtualTreesCR.cbproj b/Packages/CBuilder 10.2/VirtualTreesCR.cbproj deleted file mode 100644 index 66402b8c0..000000000 --- a/Packages/CBuilder 10.2/VirtualTreesCR.cbproj +++ /dev/null @@ -1,806 +0,0 @@ - - - {FE6B0D67-74B6-4E30-8AED-CB2B3E77A51F} - VirtualTreesCR.cpp - 18.4 - Release - VCL - True - Win64 - 3 - Package - - - true - - - true - Base - true - - - true - Base - true - - - true - Base - true - - - true - Cfg_1 - true - true - - - true - Base - true - - - true - Cfg_2 - true - true - - - true - Cfg_2 - true - true - - - 240 - true - VirtualTree CBuilder runtime package - $(BDSCOMMONDIR)\hpp\$(Platform) - VirtualTreesCR - Shell32.dll;$(ILINK_DelayLoadDll) - $(BDS)\lib;$(BDS)\lib\$(Platform);$(BDS)\lib\$(Platform)\$(Config);$(DCC_UnitSearchPath) - true - 4108 - System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) - true - CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - 6 - bpl - CppPackage - true - true - true - All - true - ..\..\Source\;$(BDS)\include;$(BDS)\include\windows;$(BDS)\include\windows\rtl;$(BDS)\include\windows\vcl;$(BDS)\include\windows\crtl;$(BDS)\include\windows\sdk;$(IncludePath) - ..\..\Source\;$(BDS)\lib;$(BDS)\lib\$(platform);$(BDS)\lib\$(Platform)\$(Config);$(ILINK_LibraryPath) - false - true - true - 128 - - - $(BDS)\Bin\BDS.EXE - true - $(BDSINCLUDE)\windows\vcl;$(IncludePath) - C:\Program Files (x86)\Embarcadero\DelphiXE5\lib\win32\release\psdk\;$(ILINK_LibraryPath) - true - $(BDS)\bin\default_app.manifest - 1033 - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - vclwinx;$(PackageImports) - - - $(BDSINCLUDE)\windows\vcl;$(IncludePath) - 1033 - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) - vclwinx;$(PackageImports) - - - Debug\;$(IncludePath) - false - true - false - true - false - Debug - None - DEBUG - true - true - true - $(BDS)\lib\debug;$(ILINK_LibraryPath) - true - Full - true - - - _DEBUG;$(Defines) - - - Release\;$(IncludePath) - Release - $(BDS)\lib\release;$(ILINK_LibraryPath) - None - - - 1033 - NDEBUG;$(Defines) - - - 1033 - - - - 9 - true - - - 9 - true - - - 3 - - - 4 - - - 8 - - - 0 - - - 14 - - - 15 - - - 16 - - - 16 - - - 16 - - - 17 - - - 16 - - - 17 - - - 17 - - - 17 - - - 17 - - - Cfg_2 - Base - - - Base - - - Cfg_1 - Base - - - - - CPlusPlusBuilder.Personality.12 - CppPackage - - - - VirtualTreesCR.cpp - - - False - False - 1 - 0 - 0 - 0 - False - False - False - False - False - 4108 - 1252 - - - - - 1.0.0.0 - - - - - - 1.0.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - - - - - - - False - - False - - True - False - - - Embarcadero C++Builder Office 2000 Servers Package - Embarcadero C++Builder Office XP Servers Package - Microsoft Office 2000 Sample Automation Server Wrapper Components - Microsoft Office XP Sample Automation Server Wrapper Components - - - False - True - True - False - - - - True - True - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - VirtualTreesCR.bpl - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - true - - - - - 1 - - - Contents\MacOS - 0 - - - - - classes - 1 - - - - - library\lib\armeabi-v7a - 1 - - - - - library\lib\armeabi - 1 - - - - - library\lib\mips - 1 - - - - - - library\lib\armeabi-v7a - 1 - - - - - res\drawable - 1 - - - - - res\values - 1 - - - - - res\drawable - 1 - - - - - res\drawable-xxhdpi - 1 - - - - - res\drawable-ldpi - 1 - - - - - res\drawable-mdpi - 1 - - - - - res\drawable-hdpi - 1 - - - - - res\drawable-xhdpi - 1 - - - - - res\drawable-small - 1 - - - - - res\drawable-normal - 1 - - - - - res\drawable-large - 1 - - - - - res\drawable-xlarge - 1 - - - - - 1 - - - 1 - - - 0 - - - - - 1 - .framework - - - 0 - - - - - 1 - .dylib - - - 0 - .dll;.bpl - - - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 1 - .dylib - - - 0 - .bpl - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - 1 - - - 1 - - - - - 1 - - - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF - 1 - - - - - - - - 1 - - - 1 - - - 1 - - - - - - - Contents\Resources - 1 - - - - - library\lib\armeabi-v7a - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - 0 - - - - - 1 - - - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - Assets - 1 - - - Assets - 1 - - - - - - - - - - - - - 12 - - - - diff --git a/Packages/CBuilder 10.2/VirtualTreesCR.cpp b/Packages/CBuilder 10.2/VirtualTreesCR.cpp deleted file mode 100644 index 37aacb559..000000000 --- a/Packages/CBuilder 10.2/VirtualTreesCR.cpp +++ /dev/null @@ -1,17 +0,0 @@ -//--------------------------------------------------------------------------- - -#include -#pragma hdrstop -#pragma package(smart_init) -//--------------------------------------------------------------------------- - -// Package source. -//--------------------------------------------------------------------------- - - -#pragma argsused -int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) -{ - return 1; -} -//--------------------------------------------------------------------------- From 70d06a75ccb42dd2b29aa38f6eb0587f07566089 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 12 Apr 2021 21:39:57 +0200 Subject: [PATCH 200/681] Fixed C++ Builder issue #1033 by adding a link demand for "uxtheme" . --- Source/VirtualTrees.pas | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index c387da97e..4ffa486f1 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -71,6 +71,7 @@ interface {$HPPEMIT '#pragma comment(lib, "VirtualTreesR")'} {$endif} {$HPPEMIT '#pragma comment(lib, "Shell32")'} +{$HPPEMIT '#pragma comment(lib, "uxtheme")'} {$HPPEMIT '#pragma link "VirtualTrees.Accessibility"'} uses From c585f2552828a5808ab177324879579d0b73faf3 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 13 Apr 2021 22:58:49 +0200 Subject: [PATCH 201/681] Fixed C++ Builder issue #1033 by adding a link demand for "uxtheme". --- Packages/CBuilder 10.X/VirtualTreesCR.cbproj | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Packages/CBuilder 10.X/VirtualTreesCR.cbproj b/Packages/CBuilder 10.X/VirtualTreesCR.cbproj index 66402b8c0..9c96d1b27 100644 --- a/Packages/CBuilder 10.X/VirtualTreesCR.cbproj +++ b/Packages/CBuilder 10.X/VirtualTreesCR.cbproj @@ -57,7 +57,7 @@ VirtualTree CBuilder runtime package $(BDSCOMMONDIR)\hpp\$(Platform) VirtualTreesCR - Shell32.dll;$(ILINK_DelayLoadDll) + Shell32.dll;uxtheme.dll;$(ILINK_DelayLoadDll) $(BDS)\lib;$(BDS)\lib\$(Platform);$(BDS)\lib\$(Platform)\$(Config);$(DCC_UnitSearchPath) true 4108 @@ -184,6 +184,14 @@ 17 + + 18 + true + + + 18 + true + Cfg_2 Base From 257f26927736bcbdb1db2fd9b09c24c045583b8c Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Thu, 15 Apr 2021 21:50:36 +0200 Subject: [PATCH 202/681] Workaround for issue #1025: Incorrect size of expando images for many Vcl Styles --- Source/VirtualTrees.pas | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 4ffa486f1..04c70022d 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -14083,7 +14083,10 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); begin if NeedButtons then begin if StyleServices.GetElementSize(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), TElementSize.esActual, Size) then + begin + Size.cx := Max(Size.cx, 10); // Use min size of 10, see issue #1035 Size.cx := ScaledPixels(Size.cx) // I would have expected that the returned value is dpi-sclaed, but this is not the case in RAD Studio 10.4.1. See issue #984 + end else Size.cx := ScaledPixels(11); Size.cy := Size.cx; From 80304a6d622d9f6a72f3726f00f5458f4a2f9b14 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 19 Apr 2021 07:58:17 +0200 Subject: [PATCH 203/681] Workaround for issue #1025: Incorrect size of expando images for many Vcl Styles. We need to use a min size of 11 pixels. Thx @sglienke for pointing this out. --- Source/VirtualTrees.pas | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 04c70022d..86a7a59aa 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -14078,17 +14078,19 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); //--------------- end local function ---------------------------------------- +const + cMinExpandoHeight = 11; // pixels @100% begin if VclStyleEnabled and (seClient in StyleElements) then begin if NeedButtons then begin if StyleServices.GetElementSize(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), TElementSize.esActual, Size) then begin - Size.cx := Max(Size.cx, 10); // Use min size of 10, see issue #1035 + Size.cx := Max(Size.cx, cMinExpandoHeight); // Use min size of 11, see issue #1035 / RSP-33715 Size.cx := ScaledPixels(Size.cx) // I would have expected that the returned value is dpi-sclaed, but this is not the case in RAD Studio 10.4.1. See issue #984 end else - Size.cx := ScaledPixels(11); + Size.cx := ScaledPixels(cMinExpandoHeight); Size.cy := Size.cx; FillBitmap(FPlusBM); FillBitmap(FHotPlusBM); From 0dcb3ad111d4a51933f1c4cc3b1a55ff27806f65 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Tue, 27 Apr 2021 15:24:58 +0200 Subject: [PATCH 204/681] Issue #1036: Added VirtualTrees.StyleHooks.TVclStyleScrollBarsHook.DrawExpandArrow() --- Source/VirtualTrees.StyleHooks.pas | 33 ++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index 89736440b..e62b1d3ce 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -33,8 +33,9 @@ interface Winapi.Windows, Winapi.Messages, Winapi.UxTheme, - System.Classes, + System.UITypes, + Vcl.Graphics, Vcl.Themes, Vcl.Forms, Vcl.Controls; @@ -99,6 +100,9 @@ TScrollWindow = class(TWinControl) public constructor Create(AControl: TWinControl); override; destructor Destroy; override; + /// Draws an expand arrow like used in the RAD Studio IDE. + /// The code is not yet dpi-aware. + class procedure DrawExpandArrow(pBitmap: TBitmap; pExpanded: Boolean; pColor: TColor = clNone); property HorzScrollRect; property VertScrollRect; end; @@ -120,7 +124,6 @@ implementation System.SysUtils, System.Math, System.Types, - Vcl.Graphics, VirtualTrees; type @@ -203,6 +206,32 @@ destructor TVclStyleScrollBarsHook.Destroy; inherited; end; +class procedure TVclStyleScrollBarsHook.DrawExpandArrow(pBitmap: TBitmap; pExpanded: Boolean; pColor: TColor); +const + Size: TRect = (Left: 0; Top: 0; Right: 12; Bottom: 12); + ArrowPoints: array[Boolean, 0..5] of TPoint = ( + ((X:3; Y:1), (X:8; Y:6), (X:3; Y:11), (X:4; Y:11), (X:9; Y:6), (X:3; Y:0)), + ((X:1; Y:3), (X:6; Y:8), (X:11; Y:3), (X:11; Y:4), (X:6; Y:9), (X:0; Y:3)) + ); +var + canvas: TCanvas; +begin + pBitmap.SetSize(Size.Width, Size.Height); + canvas := pBitmap.Canvas; + canvas.FillRect(Size); + if pColor = clNone then + begin + if Assigned(VTStyleServicesFunc) then + canvas.Pen.Color := VTStyleServicesFunc.GetSystemColor(clGrayText) + else + canvas.Pen.Color := Vcl.Themes.StyleServices.GetSystemColor(clGrayText) + end + else + canvas.Pen.Color := pColor; + canvas.Pen.Width := 1; + canvas.Polyline(ArrowPoints[pExpanded]); +end; + procedure TVclStyleScrollBarsHook.DrawHorzScrollBar(DC: HDC); var B: TBitmap; From dfc8275f432b03215848508859bbb6c1a1a2be0d Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Thu, 29 Apr 2021 16:55:28 +1000 Subject: [PATCH 205/681] Fixed column widths not scaling when min and max width are the same Scaling did not take co;lumn minwidth/maxwith into account --- Source/VirtualTrees.pas | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 86a7a59aa..72af7fd9e 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1045,6 +1045,7 @@ TVirtualTreeColumn = class(TCollectionItem) procedure SetWidth(Value: TDimension); protected FLeft: TDimension; + procedure ChangeScale(M, D: TDimension; isDpiChange: Boolean); virtual; procedure ComputeHeaderLayout(var PaintInfo: THeaderPaintInfo; DrawFormat: Cardinal; CalculateTextRect: Boolean = False); procedure DefineProperties(Filer: TFiler); override; procedure GetAbsoluteBounds(var Left, Right: TDimension); @@ -7087,6 +7088,14 @@ procedure TVirtualTreeColumn.SetWidth(Value: Integer); //---------------------------------------------------------------------------------------------------------------------- +procedure TVirtualTreeColumn.ChangeScale(M, D: TDimension; isDpiChange: Boolean); +begin + FMinWidth := MulDiv(FMinWidth, M, D); + FMaxWidth := MulDiv(FMaxWidth, M, D); + FSpacing := MulDiv(FSpacing, M, D); + Self.Width := MulDiv(Self.Width, M, D); +end; + procedure TVirtualTreeColumn.ComputeHeaderLayout(var PaintInfo: THeaderPaintInfo; DrawFormat: Cardinal; CalculateTextRect: Boolean = False); // The layout of a column header is determined by a lot of factors. This method takes them all into account and @@ -9998,9 +10007,7 @@ procedure TVTHeader.ChangeScale(M, D: Integer; isDpiChange: Boolean); Font.Height := MulDiv(Font.Height, M, D); // Scale the columns widths too for I := 0 to FColumns.Count - 1 do - begin - Self.FColumns[I].Width := MulDiv(Self.FColumns[I].Width, M, D); - end;//for I + Self.FColumns[I].ChangeScale(M, D, isDpiChange); if not isDpiChange then AutoScale(); end; From fcc324c5af6f278b0ff4c504833af632136efec0 Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Fri, 30 Apr 2021 20:18:31 +1000 Subject: [PATCH 206/681] Removed most if not all friend usage of private fields. --- Source/VirtualTrees.pas | 1183 ++++++++++++++++++++------------------- 1 file changed, 612 insertions(+), 571 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 72af7fd9e..0fef7fddd 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -924,7 +924,8 @@ TVTDragImage = class procedure HideDragImage; procedure PrepareDrag(DragImage: TBitmap; ImagePosition, HotSpot: TPoint; const DataObject: IDataObject); procedure ShowDragImage; - + property ImagePosition : TPoint read FImagePosition; + property LastPosition : TPoint read FLastPosition; property MoveRestriction: TVTDragMoveRestriction read FRestriction write FRestriction default dmrNone; end; @@ -1045,7 +1046,6 @@ TVirtualTreeColumn = class(TCollectionItem) procedure SetWidth(Value: TDimension); protected FLeft: TDimension; - procedure ChangeScale(M, D: TDimension; isDpiChange: Boolean); virtual; procedure ComputeHeaderLayout(var PaintInfo: THeaderPaintInfo; DrawFormat: Cardinal; CalculateTextRect: Boolean = False); procedure DefineProperties(Filer: TFiler); override; procedure GetAbsoluteBounds(var Left, Right: TDimension); @@ -1056,8 +1056,6 @@ TVirtualTreeColumn = class(TCollectionItem) procedure ReadHint(Reader: TReader); procedure ReadText(Reader: TReader); procedure SetCollection(Value: TCollection); override; - property HasImage: Boolean read FHasImage; - property ImageRect: TRect read FImageRect; public constructor Create(Collection: TCollection); override; destructor Destroy; override; @@ -1065,6 +1063,8 @@ TVirtualTreeColumn = class(TCollectionItem) procedure Assign(Source: TPersistent); override; function Equals(OtherColumnObj: TObject): Boolean; override; function GetRect: TRect; virtual; + property HasImage: Boolean read FHasImage; + property ImageRect: TRect read FImageRect; procedure LoadFromStream(const Stream: TStream; Version: Integer); procedure ParentBiDiModeChanged; procedure ParentColorChanged; @@ -1073,9 +1073,11 @@ TVirtualTreeColumn = class(TCollectionItem) procedure SaveToStream(const Stream: TStream); function UseRightToLeftReading: Boolean; + property BonusPixel: Boolean read FBonusPixel write FBonusPixel; property CaptionText: string read FCaptionText; property Left: TDimension read GetLeft; property Owner: TVirtualTreeColumns read GetOwner; + property SpringRest: Single read FSpringRest write FSpringRest; published property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify; property BiDiMode: TBiDiMode read FBiDiMode write SetBiDiMode stored IsBiDiModeStored; @@ -1156,14 +1158,15 @@ TVirtualTreeColumns = class(TCollection) procedure InitializePositionArray; procedure Notify(Item: TCollectionItem; Action: System.Classes.TCollectionNotification); override; procedure ReorderColumns(RTL: Boolean); + procedure SetHoverIndex(index : TColumnIndex); procedure Update(Item: TCollectionItem); override; procedure UpdatePositions(Force: Boolean = False); property HeaderBitmap: TBitmap read FHeaderBitmap; property PositionToIndex: TIndexArray read FPositionToIndex; - property HoverIndex: TColumnIndex read FHoverIndex; - property DownIndex: TColumnIndex read FDownIndex; - property CheckBoxHit: Boolean read FCheckBoxHit; + property HoverIndex: TColumnIndex read FHoverIndex write FHoverIndex; + property DownIndex: TColumnIndex read FDownIndex write FDownIndex; + property CheckBoxHit: Boolean read FCheckBoxHit write FCheckBoxHit; // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) function StyleServices(AControl: TControl = nil): TCustomStyleServices; public @@ -1200,9 +1203,12 @@ TVirtualTreeColumns = class(TCollection) property Count: Integer read GetCount; property ClickIndex: TColumnIndex read FClickIndex; property DefaultWidth: TDimension read FDefaultWidth write SetDefaultWidth; + property DragIndex : TColumnIndex read FDragIndex write FDragIndex; + property DropBefore : boolean read FDropBefore write FDropBefore; + property DropTarget : TColumnIndex read FDropTarget write FDropTarget; property Items[Index: TColumnIndex]: TVirtualTreeColumn read GetItem write SetItem; default; property Header: TVTHeader read FHeader; - property TrackIndex: TColumnIndex read FTrackIndex; + property TrackIndex: TColumnIndex read FTrackIndex write FTrackIndex; end; TVirtualTreeColumnsClass = class of TVirtualTreeColumns; @@ -2411,7 +2417,6 @@ TBaseVirtualTree = class(TCustomControl) function IsLastVisibleChild(Parent, Node: PVirtualNode): Boolean; function MakeNewNode: PVirtualNode; function PackArray({*}const TheArray: TNodeArray; Count: Integer): Integer; - procedure PrepareBitmaps(NeedButtons, NeedLines: Boolean); procedure FakeReadIdent(Reader: TReader); procedure SetAlignment(const Value: TAlignment); procedure SetAnimationDuration(const Value: Cardinal); @@ -2469,7 +2474,6 @@ TBaseVirtualTree = class(TCustomControl) procedure PrepareBackGroundPicture(Source: TPicture; DrawBitmap: TBitmap; DrawBitmapWidth: Integer; DrawBitMapHeight: Integer; ABkgcolor: TColor); procedure StaticBackground(Source: TPicture; Target: TCanvas; OffsetPosition: TPoint; R: TRect; aBkgColor: TColor); procedure StopTimer(ID: Integer); - procedure SetWindowTheme(const Theme: string); procedure TileBackground(Source: TPicture; Target: TCanvas; Offset: TPoint; R: TRect; aBkgColor: TColor); function ToggleCallback(Step, StepSize: Integer; Data: Pointer): Boolean; @@ -2550,6 +2554,7 @@ TBaseVirtualTree = class(TCustomControl) procedure ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirtualTreeStates); procedure ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; isDpiChange: Boolean{$ifend}); override; function CheckParentCheckState(Node: PVirtualNode; NewCheckState: TCheckState): Boolean; virtual; + procedure ClearDragManager; procedure ClearSelection(pFireChangeEvent: Boolean); overload; virtual; procedure ClearTempCache; virtual; function ColumnIsEmpty(Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; @@ -2756,6 +2761,7 @@ TBaseVirtualTree = class(TCustomControl) procedure PaintSelectionRectangle(Target: TCanvas; WindowOrgX: Integer; const SelectionRect: TRect; TargetRect: TRect); virtual; procedure PanningWindowProc(var Message: TMessage); virtual; + procedure PrepareBitmaps(NeedButtons, NeedLines: Boolean); procedure PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, MaxWidth: Integer); virtual; function ReadChunk(Stream: TStream; Version: Integer; Node: PVirtualNode; ChunkType, ChunkSize: Integer): Boolean; virtual; @@ -2770,6 +2776,8 @@ TBaseVirtualTree = class(TCustomControl) procedure SetChildCount(Node: PVirtualNode; NewChildCount: Cardinal); virtual; procedure SetFocusedNodeAndColumn(Node: PVirtualNode; Column: TColumnIndex); virtual; procedure SetRangeX(value: Cardinal); + procedure SetWindowTheme(const Theme: string); + procedure SetVisibleCount(value : Cardinal); procedure SkipNode(Stream: TStream); virtual; procedure StartOperation(OperationKind: TVTOperationKind); procedure StartWheelPanning(Position: TPoint); virtual; @@ -3314,7 +3322,8 @@ TVTEdit = class(TCustomEdit) procedure KeyPress(var Key: Char); override; public constructor Create(Link: TStringEditLink); reintroduce; - + procedure ClearLink; + procedure ClearRefLink; procedure Release; virtual; property AutoSelect; @@ -3341,8 +3350,9 @@ TStringEditLink = class(TInterfacedObject, IVTEditLink) public constructor Create; virtual; destructor Destroy; override; + property Alignment : TAlignment read FAlignment; property Node : PVirtualNode read FNode; // [IPK] Make FNode accessible - property Column: TColumnIndex read FColumn; // [IPK] Make Column(Index) accessible + property Column: TColumnIndex read FColumn; // [IPK] Make Column(Index) accessible function BeginEdit: Boolean; virtual; stdcall; function CancelEdit: Boolean; virtual; stdcall; @@ -3352,6 +3362,8 @@ TStringEditLink = class(TInterfacedObject, IVTEditLink) function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall; procedure ProcessMessage(var Message: TMessage); virtual; stdcall; procedure SetBounds(R: TRect); virtual; stdcall; + property Stopping : boolean read FStopping; + property Tree : TCustomVirtualStringTree read FTree; end; // Describes the type of text to return in the text and draw info retrival events. @@ -4852,7 +4864,7 @@ destructor TVTDataObject.Destroy; begin // Cancel a pending clipboard operation if this data object was created for the clipboard and // is freed because something else is placed there. - if FForClipboard and not (tsClipboardFlushing in FOwner.FStates) then + if FForClipboard and not (tsClipboardFlushing in FOwner.TreeStates) then FOwner.CancelCutOrCopy; // Release any internal clipboard formats @@ -5140,7 +5152,7 @@ function TVTDataObject.GetData(const FormatEtcIn: TFormatEtc; out Medium: TStgMe begin // Note: this format is not used while flushing the clipboard to avoid a dangling reference // when the owner tree is destroyed before the clipboard data is replaced with something else. - if tsClipboardFlushing in FOwner.FStates then + if tsClipboardFlushing in FOwner.TreeStates then Result := E_FAIL else begin @@ -5304,7 +5316,7 @@ destructor TVTDragManager.Destroy; begin // Set the owner's reference to us to nil otherwise it will access an invalid pointer // after our desctruction is complete. - Pointer(FOwner.FDragManager) := nil; + FOwner.ClearDragManager; inherited; end; @@ -6591,16 +6603,16 @@ function TVirtualTreeColumn.GetCaptionWidth: TDimension; UseText: Boolean; R: TRect; begin - AdvancedOwnerDraw := (hoOwnerDraw in Owner.FHeader.FOptions) and Assigned(Owner.FHeader.Treeview.FOnAdvancedHeaderDraw) and Assigned(Owner.FHeader.Treeview.FOnHeaderDrawQueryElements) and - not(csDesigning in Owner.FHeader.Treeview.ComponentState); + AdvancedOwnerDraw := (hoOwnerDraw in Owner.Header.Options) and Assigned(Owner.Header.Treeview.FOnAdvancedHeaderDraw) and Assigned(Owner.Header.Treeview.FOnHeaderDrawQueryElements) and + not(csDesigning in Owner.Header.Treeview.ComponentState); PaintInfo.Column := Self; PaintInfo.TargetCanvas := Owner.FHeaderBitmap.Canvas; with PaintInfo, Column do begin - ShowHeaderGlyph := (hoShowImages in Owner.FHeader.FOptions) and ((Assigned(Owner.FHeader.FImages) and (FImageIndex > -1)) or FCheckBox); - ShowSortGlyph := ((Owner.FHeader.FSortColumn > -1) and (Self = Owner.Items[Owner.FHeader.FSortColumn])) and (hoShowSortGlyphs in Owner.FHeader.FOptions); + ShowHeaderGlyph := (hoShowImages in Owner.Header.Options) and ((Assigned(Owner.Header.Images) and (FImageIndex > -1)) or FCheckBox); + ShowSortGlyph := ((Owner.Header.FSortColumn > -1) and (Self = Owner.Items[Owner.Header.SortColumn])) and (hoShowSortGlyphs in Owner.Header.Options); // This path for text columns or advanced owner draw. // See if the application wants to draw part of the header itself. @@ -6608,7 +6620,7 @@ function TVirtualTreeColumn.GetCaptionWidth: TDimension; if AdvancedOwnerDraw then begin PaintInfo.Column := Self; - Owner.FHeader.Treeview.DoHeaderDrawQueryElements(PaintInfo, RequestedElements); + Owner.Header.Treeview.DoHeaderDrawQueryElements(PaintInfo, RequestedElements); end; end; @@ -7034,7 +7046,7 @@ procedure TVirtualTreeColumn.SetWidth(Value: Integer); I: TColumnIndex; begin - if not (hsScaling in Owner.FHeader.FStates) then + if not (hsScaling in Owner.Header.States) then if ([coVisible, coFixed] * FOptions = [coVisible, coFixed]) then begin with Owner, FHeader, FFixedAreaConstraints, TreeView do @@ -7042,10 +7054,10 @@ procedure TVirtualTreeColumn.SetWidth(Value: Integer); TotalFixedMinWidth := 0; TotalFixedMaxWidth := 0; for I := 0 to FColumns.Count - 1 do - if ([coVisible, coFixed] * FColumns[I].FOptions = [coVisible, coFixed]) then + if ([coVisible, coFixed] * FColumns[I].Options = [coVisible, coFixed]) then begin - Inc(TotalFixedMaxWidth, FColumns[I].FMaxWidth); - Inc(TotalFixedMinWidth, FColumns[I].FMinWidth); + Inc(TotalFixedMaxWidth, FColumns[I].MaxWidth); + Inc(TotalFixedMinWidth, FColumns[I].MinWidth); end; // The percentage values have precedence over the pixel values. @@ -7088,13 +7100,6 @@ procedure TVirtualTreeColumn.SetWidth(Value: Integer); //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.ChangeScale(M, D: TDimension; isDpiChange: Boolean); -begin - FMinWidth := MulDiv(FMinWidth, M, D); - FMaxWidth := MulDiv(FMaxWidth, M, D); - FSpacing := MulDiv(FSpacing, M, D); - Self.Width := MulDiv(Self.Width, M, D); -end; procedure TVirtualTreeColumn.ComputeHeaderLayout(var PaintInfo: THeaderPaintInfo; DrawFormat: Cardinal; CalculateTextRect: Boolean = False); @@ -7844,7 +7849,7 @@ function TVirtualTreeColumns.GetNewIndex(P: TPoint; var OldIndex: TColumnIndex): begin Result := False; // convert to local coordinates - Inc(P.Y, FHeader.FHeight); + Inc(P.Y, FHeader.Height); NewIndex := ColumnFromPosition(P); if NewIndex <> OldIndex then begin @@ -7900,7 +7905,7 @@ procedure TVirtualTreeColumns.AdjustAutoSize(CurrentIndex: TColumnIndex; Force: // Determine index to be used for auto resizing. This is usually given by the owner's AutoSizeIndex, but // could be different if the column whose resize caused the invokation here is either the auto column itself // or visually to the right of the auto size column. - AutoIndex := FHeader.FAutoSizeIndex; + AutoIndex := FHeader.AutoSizeIndex; if (AutoIndex < 0) or (AutoIndex >= Count) then AutoIndex := Count - 1; @@ -7916,7 +7921,7 @@ procedure TVirtualTreeColumns.AdjustAutoSize(CurrentIndex: TColumnIndex; Force: // Go through all columns and calculate the rest space remaining. for Index := 0 to Count - 1 do - if (Index <> AutoIndex) and (coVisible in Items[Index].FOptions) then + if (Index <> AutoIndex) and (coVisible in Items[Index].Options) then Dec(RestWidth, Items[Index].Width); with Items[AutoIndex] do @@ -7950,15 +7955,15 @@ function TVirtualTreeColumns.AdjustDownColumn(P: TPoint): TColumnIndex; begin // Convert to local coordinates. - Inc(P.Y, FHeader.FHeight); + Inc(P.Y, FHeader.Height); Result := ColumnFromPosition(P); - if (Result > NoColumn) and (Result <> FDownIndex) and (coAllowClick in Items[Result].FOptions) and - (coEnabled in Items[Result].FOptions) then + if (Result > NoColumn) and (Result <> FDownIndex) and (coAllowClick in Items[Result].Options) and + (coEnabled in Items[Result].Options) then begin if FDownIndex > NoColumn then FHeader.Invalidate(Items[FDownIndex]); FDownIndex := Result; - FCheckBoxHit := Items[Result].FHasImage and PtInRect(Items[Result].FImageRect, P) and Items[Result].CheckBox; + FCheckBoxHit := Items[Result].HasImage and PtInRect(Items[Result].ImageRect, P) and Items[Result].CheckBox; FHeader.Invalidate(Items[FDownIndex]); end; end; @@ -8005,7 +8010,7 @@ procedure TVirtualTreeColumns.AdjustPosition(Column: TVirtualTreeColumn; Positio function TVirtualTreeColumns.CanSplitterResize(P: TPoint; Column: TColumnIndex): Boolean; begin - Result := (Column > NoColumn) and ([coResizable, coVisible] * Items[Column].FOptions = [coResizable, coVisible]); + Result := (Column > NoColumn) and ([coResizable, coVisible] * Items[Column].Options = [coResizable, coVisible]); DoCanSplitterResize(P, Column, Result); end; @@ -8149,7 +8154,7 @@ function TVirtualTreeColumns.HandleClick(P: TPoint; Button: TMouseButton; Force, if (csDesigning in Header.Treeview.ComponentState) then exit; // Convert vertical position to local coordinates. - Inc(P.Y, FHeader.FHeight); + Inc(P.Y, FHeader.Height); NewClickIndex := ColumnFromPosition(P); with HitInfo do begin @@ -8161,14 +8166,14 @@ function TVirtualTreeColumns.HandleClick(P: TPoint; Button: TMouseButton; Force, end; HitInfo.Button := Button; - if (NewClickIndex > NoColumn) and (coAllowClick in Items[NewClickIndex].FOptions) and + if (NewClickIndex > NoColumn) and (coAllowClick in Items[NewClickIndex].Options) and ((NewClickIndex = FDownIndex) or Force) then begin FClickIndex := NewClickIndex; HitInfo.Column := NewClickIndex; HitInfo.HitPosition := [hhiOnColumn]; - if Items[NewClickIndex].FHasImage and PtInRect(Items[NewClickIndex].FImageRect, P) then + if Items[NewClickIndex].HasImage and PtInRect(Items[NewClickIndex].ImageRect, P) then begin Include(HitInfo.HitPosition, hhiOnIcon); if Items[NewClickIndex].CheckBox then @@ -8210,7 +8215,7 @@ function TVirtualTreeColumns.HandleClick(P: TPoint; Button: TMouseButton; Force, if (Button = mbRight) then begin - Dec(P.Y, FHeader.FHeight); // popup menus at actual clicked point + Dec(P.Y, FHeader.Height); // popup menus at actual clicked point FreeAndNil(fColumnPopupMenu);// Attention: Do not free the TVTHeaderPopupMenu at the end of this method, otherwise the clikc events of the menu item will not be fired. Self.FDownIndex := NoColumn; Self.FTrackIndex := NoColumn; @@ -8220,7 +8225,7 @@ function TVirtualTreeColumns.HandleClick(P: TPoint; Button: TMouseButton; Force, begin Header.Treeview.StopTimer(ScrollTimer); Header.Treeview.StopTimer(HeaderTimer); - Header.FColumns.FHoverIndex := NoColumn; + Header.Columns.SetHoverIndex(NoColumn); Header.Treeview.DoStateChange([], [tsScrollPending, tsScrolling]); Menu.PopupComponent := Header.Treeview; @@ -8404,6 +8409,13 @@ procedure TVirtualTreeColumns.ReorderColumns(RTL: Boolean); //---------------------------------------------------------------------------------------------------------------------- +procedure TVirtualTreeColumns.SetHoverIndex(index : TColumnIndex); +begin + FHoverIndex := index; +end; + +//---------------------------------------------------------------------------------------------------------------------- + procedure TVirtualTreeColumns.EndUpdate; begin InitializePositionArray(); @@ -8426,9 +8438,9 @@ procedure TVirtualTreeColumns.Update(Item: TCollectionItem); // The first column which is created is by definition also the main column. if (Count > 0) and (Header.FMainColumn < 0) then - FHeader.FMainColumn := 0; + FHeader.MainColumn := 0; - if not (csLoading in Header.Treeview.ComponentState) and not (hsLoading in FHeader.FStates) then + if not (csLoading in FHeader.Treeview.ComponentState) and not (hsLoading in FHeader.States) then begin with FHeader do begin @@ -8512,10 +8524,10 @@ procedure TVirtualTreeColumns.AnimatedResize(Column: TColumnIndex; NewWidth: Int Exit; // Just in case. // Make sure the width constrains are considered. - if NewWidth < Items[Column].FMinWidth then - NewWidth := Items[Column].FMinWidth; - if NewWidth > Items[Column].FMaxWidth then - NewWidth := Items[Column].FMaxWidth; + if NewWidth < Items[Column].MinWidth then + NewWidth := Items[Column].MinWidth; + if NewWidth > Items[Column].MaxWidth then + NewWidth := Items[Column].MaxWidth; OldWidth := Items[Column].Width; // Nothing to do if the width is the same. @@ -8655,7 +8667,7 @@ function TVirtualTreeColumns.ColumnFromPosition(P: TPoint; Relative: Boolean = T Inc(Sum, ComputeRTLOffset(True)); for I := 0 to Count - 1 do - if coVisible in Items[FPositionToIndex[I]].FOptions then + if coVisible in Items[FPositionToIndex[I]].Options then begin Inc(Sum, Items[FPositionToIndex[I]].Width); if P.X < Sum then @@ -8790,9 +8802,9 @@ function TVirtualTreeColumns.GetFirstVisibleColumn(ConsiderAllowFocus: Boolean = if (UpdateCount > 0) or (csLoading in Header.TreeView.ComponentState) then exit; // See issue #760 for I := 0 to Count - 1 do - if (coVisible in Items[FPositionToIndex[I]].FOptions) and + if (coVisible in Items[FPositionToIndex[I]].Options) and ( (not ConsiderAllowFocus) or - (coAllowFocus in Items[FPositionToIndex[I]].FOptions) + (coAllowFocus in Items[FPositionToIndex[I]].Options) ) then begin Result := FPositionToIndex[I]; @@ -8816,9 +8828,9 @@ function TVirtualTreeColumns.GetLastVisibleColumn(ConsiderAllowFocus: Boolean = if (UpdateCount > 0) or (csLoading in Header.TreeView.ComponentState) then exit; // See issue #760 for I := Count - 1 downto 0 do - if (coVisible in Items[FPositionToIndex[I]].FOptions) and + if (coVisible in Items[FPositionToIndex[I]].Options) and ( (not ConsiderAllowFocus) or - (coAllowFocus in Items[FPositionToIndex[I]].FOptions) + (coAllowFocus in Items[FPositionToIndex[I]].Options) ) then begin Result := FPositionToIndex[I]; @@ -8873,9 +8885,9 @@ function TVirtualTreeColumns.GetNextVisibleColumn(Column: TColumnIndex; Consider repeat Result := GetNextColumn(Result); until (Result = InvalidColumn) or - ( (coVisible in Items[Result].FOptions) and + ( (coVisible in Items[Result].Options) and ( (not ConsiderAllowFocus) or - (coAllowFocus in Items[Result].FOptions) + (coAllowFocus in Items[Result].Options) ) ); end; @@ -8914,9 +8926,9 @@ function TVirtualTreeColumns.GetPreviousVisibleColumn(Column: TColumnIndex; Cons repeat Result := GetPreviousColumn(Result); until (Result = InvalidColumn) or - ( (coVisible in Items[Result].FOptions) and + ( (coVisible in Items[Result].Options) and ( (not ConsiderAllowFocus) or - (coAllowFocus in Items[Result].FOptions) + (coAllowFocus in Items[Result].Options) ) ); end; @@ -8935,7 +8947,7 @@ function TVirtualTreeColumns.GetVisibleColumns: TColumnsArray; Counter := 0; for I := 0 to Count - 1 do - if coVisible in Items[FPositionToIndex[I]].FOptions then + if coVisible in Items[FPositionToIndex[I]].Options then begin Result[Counter] := Items[FPositionToIndex[I]]; Inc(Counter); @@ -9139,7 +9151,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const end else begin - if (FHeader.Treeview.VclStyleEnabled and (seClient in FHeader.FOwner.StyleElements)) then + if (FHeader.Treeview.VclStyleEnabled and (seClient in FHeader.Treeview.StyleElements)) then begin Details := StyleServices.GetElementDetails(thHeaderItemRightNormal); StyleServices.DrawElement(Handle, Details, BackgroundRect, @BackgroundRect {$IF CompilerVersion >= 34}, FHeader.Treeview.FCurrentPPI{$IFEND}); @@ -9153,7 +9165,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const end else begin - Brush.Color := FHeader.FBackgroundColor; + Brush.Color := FHeader.Background; FillRect(BackgroundRect); end; end; @@ -9185,7 +9197,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const PaintInfo.Column := Items[AColumn]; with PaintInfo, Column do begin - IsHoverIndex := (AColumn = FHoverIndex) and (hoHotTrack in FHeader.FOptions) and (coEnabled in FOptions); + IsHoverIndex := (AColumn = FHoverIndex) and (hoHotTrack in FHeader.Options) and (coEnabled in FOptions); IsDownIndex := (AColumn = FDownIndex) and not FCheckBoxHit; if (coShowDropMark in FOptions) and (AColumn = FDropTarget) and (AColumn <> FDragIndex) then @@ -9212,8 +9224,8 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const DropMark := dmmNone; IsEnabled := (coEnabled in FOptions) and (FHeader.Treeview.Enabled); - ShowHeaderGlyph := (hoShowImages in FHeader.FOptions) and ((Assigned(Images) and (FImageIndex > -1)) or FCheckBox); - ShowSortGlyph := (AColumn = FHeader.FSortColumn) and (hoShowSortGlyphs in FHeader.FOptions); + ShowHeaderGlyph := (hoShowImages in FHeader.Options) and ((Assigned(Images) and (FImageIndex > -1)) or FCheckBox); + ShowSortGlyph := (AColumn = FHeader.SortColumn) and (hoShowSortGlyphs in FHeader.Options); WrapCaption := coWrapCaption in FOptions; PaintRectangle := ATargetRect; @@ -9238,7 +9250,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const FHeader.Treeview.DoAdvancedHeaderDraw(PaintInfo, [hpeBackground]) else begin - if FHeader.Treeview.VclStyleEnabled and (seClient in FHeader.FOwner.StyleElements) then + if FHeader.Treeview.VclStyleEnabled and (seClient in FHeader.Treeview.StyleElements) then begin if IsDownIndex then Details := StyleServices.GetElementDetails(thHeaderItemPressed) @@ -9352,7 +9364,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const if IsHoverIndex and FHeader.Treeview.VclStyleEnabled then DrawHot := True else - DrawHot := (IsHoverIndex and (hoHotTrack in FHeader.FOptions) and not(tsUseThemes in FHeader.Treeview.FStates)); + DrawHot := (IsHoverIndex and (hoHotTrack in FHeader.Options) and not(tsUseThemes in FHeader.Treeview.FStates)); if not(hpeText in ActualElements) and (Length(Text) > 0) then DrawButtonText(TargetCanvas.Handle, ColCaptionText, TextRectangle, IsEnabled, DrawHot, DrawFormat, WrapCaption); @@ -9364,17 +9376,17 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const Pos.TopLeft := SortGlyphPos; Pos.Right := Pos.Left + SortGlyphSize.cx; Pos.Bottom := Pos.Top + SortGlyphSize.cy; - if FHeader.FSortDirection = sdAscending then + if FHeader.SortDirection = sdAscending then Glyph := thHeaderSortArrowSortedUp else Glyph := thHeaderSortArrowSortedDown; Details := StyleServices.GetElementDetails(Glyph); if not StyleServices.DrawElement(TargetCanvas.Handle, Details, Pos, @Pos {$IF CompilerVersion >= 34}, FHeader.TreeView.FCurrentPPI {$IFEND}) then - PaintInfo.DrawSortArrow(FHeader.FSortDirection); + PaintInfo.DrawSortArrow(FHeader.SortDirection); end else begin - PaintInfo.DrawSortArrow(FHeader.FSortDirection); + PaintInfo.DrawSortArrow(FHeader.SortDirection); end; end; @@ -9408,9 +9420,9 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const Exit; // If both draw posibillities are specified then prefer the advanced way. - AdvancedOwnerDraw := (hoOwnerDraw in FHeader.FOptions) and Assigned(FHeader.Treeview.FOnAdvancedHeaderDraw) and + AdvancedOwnerDraw := (hoOwnerDraw in FHeader.Options) and Assigned(FHeader.Treeview.OnAdvancedHeaderDraw) and Assigned(FHeader.Treeview.FOnHeaderDrawQueryElements) and not (csDesigning in FHeader.Treeview.ComponentState); - OwnerDraw := (hoOwnerDraw in FHeader.FOptions) and Assigned(FHeader.Treeview.FOnHeaderDraw) and + OwnerDraw := (hoOwnerDraw in FHeader.Options) and Assigned(FHeader.Treeview.OnHeaderDraw) and not (csDesigning in FHeader.Treeview.ComponentState) and not AdvancedOwnerDraw; ZeroMemory(@PaintInfo, SizeOf(PaintInfo)); @@ -9419,8 +9431,8 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const with PaintInfo, TargetCanvas do begin // Use shortcuts for the images and the font. - Images := FHeader.FImages; - Font := FHeader.FFont; + Images := FHeader.Images; + Font := FHeader.Font; PrepareButtonStyles; @@ -9451,16 +9463,16 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const TargetRect.Top := Target.Y; TargetRect.Bottom := Target.Y + R.Bottom - R.Top; - TargetRect.Left := Target.X - R.Left + Items[Run].FLeft + RTLOffset; + TargetRect.Left := Target.X - R.Left + Items[Run].Left + RTLOffset; // TargetRect.Right will be set in the loop - ShowRightBorder := (FHeader.Style = hsThickButtons) or not (hoAutoResize in FHeader.FOptions) or + ShowRightBorder := (FHeader.Style = hsThickButtons) or not (hoAutoResize in FHeader.Options) or (FHeader.Treeview.BevelKind = bkNone); // Now go for each button. while (Run > NoColumn) and (TargetRect.Left < MaxX) do begin - TargetRect.Right := TargetRect.Left + Items[Run].FWidth; + TargetRect.Right := TargetRect.Left + Items[Run].Width; // create a clipping rect to limit painting to button area ClipCanvas(TargetCanvas, Rect(Max(TargetRect.Left, Target.X), Target.Y + R.Top, @@ -9510,7 +9522,7 @@ function TVirtualTreeColumns.TotalWidth: Integer; if (Count > 0) and (Length(FPositionToIndex) > 0) then begin LastColumn := FPositionToIndex[Count - 1]; - if not (coVisible in Items[LastColumn].FOptions) then + if not (coVisible in Items[LastColumn].Options) then LastColumn := GetPreviousVisibleColumn(LastColumn); if LastColumn > NoColumn then with Items[LastColumn] do @@ -9853,7 +9865,7 @@ procedure TVTHeader.SetMainColumn(Value: TColumnIndex); if not (csLoading in Treeview.ComponentState) then begin Treeview.MainColumnChanged; - if not (toExtendedFocus in Treeview.FOptions.FSelectionOptions) then + if not (toExtendedFocus in Treeview.TreeOptions.SelectionOptions) then Treeview.FocusedColumn := FMainColumn; Treeview.Invalidate; end; @@ -9951,7 +9963,7 @@ procedure TVTHeader.SetSortDirection(const Value: TSortDirection); begin FSortDirection := Value; Invalidate(nil); - if ((toAutoSort in Treeview.FOptions.FAutoOptions) or (hoHeaderClickAutoSort in Options)) and (Treeview.FUpdateCount = 0) then + if ((toAutoSort in Treeview.TreeOptions.AutoOptions) or (hoHeaderClickAutoSort in Options)) and (Treeview.UpdateCount = 0) then Treeview.SortTree(FSortColumn, FSortDirection, True); end; end; @@ -10007,7 +10019,9 @@ procedure TVTHeader.ChangeScale(M, D: Integer; isDpiChange: Boolean); Font.Height := MulDiv(Font.Height, M, D); // Scale the columns widths too for I := 0 to FColumns.Count - 1 do - Self.FColumns[I].ChangeScale(M, D, isDpiChange); + begin + Self.FColumns[I].Width := MulDiv(Self.FColumns[I].Width, M, D); + end;//for I if not isDpiChange then AutoScale(); end; @@ -10047,7 +10061,7 @@ function TVTHeader.DetermineSplitterIndex(P: TPoint): Boolean; if FColumns.Count > 0 then begin - FColumns.FTrackIndex := NoColumn; + FColumns.TrackIndex := NoColumn; VisibleFixedWidth := FColumns.GetVisibleFixedWidth; LeftTolerance := Round(SplitterHitTolerance * 0.6); if Treeview.UseRightToLeftAlignment then @@ -10252,7 +10266,7 @@ procedure TVTHeader.DoSetSortColumn(Value: TColumnIndex; pSortDirection: TSortDi FSortDirection := pSortDirection; if FSortColumn > NoColumn then Invalidate(Columns[FSortColumn]); - if ((toAutoSort in Treeview.FOptions.FAutoOptions) or (hoHeaderClickAutoSort in Options)) and (Treeview.FUpdateCount = 0) then + if ((toAutoSort in Treeview.TreeOptions.AutoOptions) or (hoHeaderClickAutoSort in Options)) and (Treeview.UpdateCount = 0) then Treeview.SortTree(FSortColumn, FSortDirection, True); end; end; @@ -10279,30 +10293,30 @@ procedure TVTHeader.DragTo(P: TPoint); // Make coordinates relative to (0, 0) of the non-client area. Inc(ClientP.Y, FHeight); NewTarget := FColumns.ColumnFromPosition(ClientP); - NeedRepaint := (NewTarget <> InvalidColumn) and (NewTarget <> FColumns.FDropTarget); + NeedRepaint := (NewTarget <> InvalidColumn) and (NewTarget <> FColumns.DropTarget); if NewTarget >= 0 then begin FColumns.GetColumnBounds(NewTarget, Left, Right); - if (ClientP.X < ((Left + Right) div 2)) <> FColumns.FDropBefore then + if (ClientP.X < ((Left + Right) div 2)) <> FColumns.DropBefore then begin NeedRepaint := True; - FColumns.FDropBefore := not FColumns.FDropBefore; + FColumns.DropBefore := not FColumns.DropBefore; end; end; if NeedRepaint then begin // Invalidate columns which need a repaint. - if FColumns.FDropTarget > NoColumn then + if FColumns.DropTarget > NoColumn then begin - I := FColumns.FDropTarget; - FColumns.FDropTarget := NoColumn; + I := FColumns.DropTarget; + FColumns.DropTarget := NoColumn; Invalidate(FColumns.Items[I]); end; - if (NewTarget > NoColumn) and (NewTarget <> FColumns.FDropTarget) then + if (NewTarget > NoColumn) and (NewTarget <> FColumns.DropTarget) then begin Invalidate(FColumns.Items[NewTarget]); - FColumns.FDropTarget := NewTarget; + FColumns.DropTarget := NewTarget; end; end; @@ -10317,8 +10331,8 @@ procedure TVTHeader.DragTo(P: TPoint); // drag image is over the header. if // determine the case when the drag image is or was on the header area - (InHeader(FOwner.ScreenToClient(FDragImage.FLastPosition)) - or InHeader(FOwner.ScreenToClient(FDragImage.FImagePosition)) + (InHeader(FOwner.ScreenToClient(FDragImage.LastPosition)) + or InHeader(FOwner.ScreenToClient(FDragImage.ImagePosition)) ) then begin GDIFlush; @@ -10419,29 +10433,29 @@ function TVTHeader.HandleHeaderMouseMove(var Message: TWMMouseMove): Boolean; else if hsColumnWidthTracking in FStates then begin - if DoColumnWidthTracking(FColumns.FTrackIndex, GetShiftState, FTrackPoint, P) then + if DoColumnWidthTracking(FColumns.TrackIndex, GetShiftState, FTrackPoint, P) then begin if Treeview.UseRightToLeftAlignment then begin NewWidth := FTrackPoint.X - XPos; - NextColumn := FColumns.GetPreviousVisibleColumn(FColumns.FTrackIndex); + NextColumn := FColumns.GetPreviousVisibleColumn(FColumns.TrackIndex); end else begin NewWidth := XPos - FTrackPoint.X; - NextColumn := FColumns.GetNextVisibleColumn(FColumns.FTrackIndex); + NextColumn := FColumns.GetNextVisibleColumn(FColumns.TrackIndex); end; // The autosized column cannot be resized using the mouse normally. Instead we resize the next // visible column, so it look as we directly resize the autosized column. - if (hoAutoResize in FOptions) and (FColumns.FTrackIndex = FAutoSizeIndex) and - (NextColumn > NoColumn) and (coResizable in FColumns[NextColumn].FOptions) and - (FColumns[FColumns.FTrackIndex].FMinWidth < NewWidth) and - (FColumns[FColumns.FTrackIndex].FMaxWidth > NewWidth) then + if (hoAutoResize in FOptions) and (FColumns.TrackIndex = FAutoSizeIndex) and + (NextColumn > NoColumn) and (coResizable in FColumns[NextColumn].Options) and + (FColumns[FColumns.TrackIndex].MinWidth < NewWidth) and + (FColumns[FColumns.TrackIndex].MaxWidth > NewWidth) then FColumns[NextColumn].Width := FColumns[NextColumn].Width - NewWidth - + FColumns[FColumns.FTrackIndex].Width + + FColumns[FColumns.TrackIndex].Width else - FColumns[FColumns.FTrackIndex].Width := NewWidth; // 1 EListError seen here (List index out of bounds (-1)) since 10/2013 + FColumns[FColumns.TrackIndex].Width := NewWidth; // 1 EListError seen here (List index out of bounds (-1)) since 10/2013 end; HandleHeaderMouseMove := True; Result := 0; @@ -10460,15 +10474,15 @@ function TVTHeader.HandleHeaderMouseMove(var Message: TWMMouseMove): Boolean; begin P := Treeview.ClientToScreen(P); // start actual dragging if allowed - if (hoDrag in FOptions) and Treeview.DoHeaderDragging(FColumns.FDownIndex) then + if (hoDrag in FOptions) and Treeview.DoHeaderDragging(FColumns.DownIndex) then begin if ((Abs(FDragStart.X - P.X) > Mouse.DragThreshold) or (Abs(FDragStart.Y - P.Y) > Mouse.DragThreshold)) then begin Treeview.StopTimer(HeaderTimer); - I := FColumns.FDownIndex; - FColumns.FDownIndex := NoColumn; - FColumns.FHoverIndex := NoColumn; + I := FColumns.DownIndex; + FColumns.DownIndex := NoColumn; + FColumns.HoverIndex := NoColumn; if I > NoColumn then Invalidate(FColumns[I]); PrepareDrag(P, FDragStart); @@ -10522,8 +10536,8 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; Result := (hoColumnResize in FOptions) and DetermineSplitterIndex(P); if Result and not InHeader(P) then begin - NextCol := FColumns.GetNextVisibleColumn(FColumns.FTrackIndex); - if not (coFixed in FColumns[FColumns.FTrackIndex].Options) or (NextCol <= NoColumn) or + NextCol := FColumns.GetNextVisibleColumn(FColumns.TrackIndex); + if not (coFixed in FColumns[FColumns.TrackIndex].Options) or (NextCol <= NoColumn) or (coFixed in FColumns[NextCol].Options) or (P.Y > Integer(Treeview.FRangeY)) then Result := False; end; @@ -10536,7 +10550,7 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; case Message.Msg of WM_SIZE: begin - if not (tsWindowCreating in FOwner.FStates) then + if not (tsWindowCreating in FOwner.TreeStates) then if (hoAutoResize in FOptions) and not (hsAutoSizing in FStates) then begin FColumns.AdjustAutoSize(InvalidColumn); @@ -10554,7 +10568,7 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; FFont.Assign(FOwner.Font); CM_BIDIMODECHANGED: for I := 0 to FColumns.Count - 1 do - if coParentBiDiMode in FColumns[I].FOptions then + if coParentBiDiMode in FColumns[I].Options then FColumns[I].ParentBiDiModeChanged; WM_NCMBUTTONDOWN: begin @@ -10571,8 +10585,8 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; begin FColumns.HandleClick(P, mbMiddle, True, False); FOwner.DoHeaderMouseUp(mbMiddle, GetShiftState, P.X, P.Y + Integer(FHeight)); - FColumns.FDownIndex := NoColumn; - FColumns.FCheckBoxHit := False; + FColumns.DownIndex := NoColumn; + FColumns.CheckBoxHit := False; end; end; WM_LBUTTONDBLCLK, @@ -10595,12 +10609,12 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; end else if HSplitterHit and ((Message.Msg = WM_NCLBUTTONDBLCLK) or (Message.Msg = WM_LBUTTONDBLCLK)) and - (hoDblClickResize in FOptions) and (FColumns.FTrackIndex > NoColumn) then + (hoDblClickResize in FOptions) and (FColumns.TrackIndex > NoColumn) then begin // If the click was on a splitter then resize column to smallest width. - if DoColumnWidthDblClickResize(FColumns.FTrackIndex, P, GetShiftState) then - AutoFitColumns(True, smaUseColumnOption, FColumns[FColumns.FTrackIndex].FPosition, - FColumns[FColumns.FTrackIndex].FPosition); + if DoColumnWidthDblClickResize(FColumns.TrackIndex, P, GetShiftState) then + AutoFitColumns(True, smaUseColumnOption, FColumns[FColumns.TrackIndex].Position, + FColumns[FColumns.TrackIndex].Position); Message.Result := 0; Result := True; end @@ -10666,7 +10680,7 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; if IsVSplitterHit or IsHSplitterHit then begin FTrackStart := P; - FColumns.FHoverIndex := NoColumn; + FColumns.HoverIndex := NoColumn; if IsVSplitterHit then begin if not (csDesigning in Treeview.ComponentState) then @@ -10676,7 +10690,7 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; else begin if not (csDesigning in Treeview.ComponentState) then - DoBeforeColumnWidthTracking(FColumns.FTrackIndex, GetShiftState); + DoBeforeColumnWidthTracking(FColumns.TrackIndex, GetShiftState); Include(FStates, hsColumnWidthTrackPending); end; @@ -10690,7 +10704,7 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; HitIndex := Columns.AdjustDownColumn(P); // in design-time header columns are always draggable if ((csDesigning in Treeview.ComponentState) and (HitIndex > NoColumn)) or - ((hoDrag in FOptions) and (HitIndex > NoColumn) and (coDraggable in FColumns[HitIndex].FOptions)) then + ((hoDrag in FOptions) and (HitIndex > NoColumn) and (coDraggable in FColumns[HitIndex].Options)) then begin // Show potential drag operation. // Disabled columns do not start a drag operation because they can't be clicked. @@ -10765,7 +10779,7 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; if (FDropTarget > -1) and (FDropTarget <> FDragIndex) and PtInRect(R, P) then begin OldPosition := FColumns[FDragIndex].Position; - if FColumns.FDropBefore then + if FColumns.DropBefore then begin if FColumns[FDragIndex].Position < FColumns[FDropTarget].Position then FColumns[FDragIndex].Position := Max(0, FColumns[FDropTarget].Position - 1) @@ -10795,7 +10809,7 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; WM_LBUTTONUP: with TWMLButtonUp(Message) do begin - if FColumns.FDownIndex > NoColumn then + if FColumns.DownIndex > NoColumn then FColumns.HandleClick(Point(XPos, YPos), mbLeft, False, False); if FStates <> [] then FOwner.DoHeaderMouseUp(mbLeft, KeysToShiftState(Keys), XPos, YPos); @@ -10809,17 +10823,17 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; end; end; - if FColumns.FTrackIndex > NoColumn then + if FColumns.TrackIndex > NoColumn then begin if hsColumnWidthTracking in FStates then - DoAfterColumnWidthTracking(FColumns.FTrackIndex); - Invalidate(Columns[FColumns.FTrackIndex]); - FColumns.FTrackIndex := NoColumn; + DoAfterColumnWidthTracking(FColumns.TrackIndex); + Invalidate(Columns[FColumns.TrackIndex]); + FColumns.TrackIndex := NoColumn; end; - if FColumns.FDownIndex > NoColumn then + if FColumns.DownIndex > NoColumn then begin - Invalidate(Columns[FColumns.FDownIndex]); - FColumns.FDownIndex := NoColumn; + Invalidate(Columns[FColumns.DownIndex]); + FColumns.DownIndex := NoColumn; end; if hsHeightTracking in FStates then DoAfterHeightTracking; @@ -10924,7 +10938,7 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; ReleaseCapture; FDragImage.EndDrag; Exclude(FStates, hsDragging); - FColumns.FDropTarget := NoColumn; + FColumns.DropTarget := NoColumn; Invalidate(nil); Result := True; Message.Result := 0; @@ -10935,7 +10949,7 @@ function TVTHeader.HandleMessage(var Message: TMessage): Boolean; begin ReleaseCapture; if hsColumnWidthTracking in FStates then - DoAfterColumnWidthTracking(FColumns.FTrackIndex); + DoAfterColumnWidthTracking(FColumns.TrackIndex); if hsHeightTracking in FStates then DoAfterHeightTracking; Result := True; @@ -10972,11 +10986,11 @@ procedure TVTHeader.PrepareDrag(P, Start: TPoint); begin // Determine initial position of drag image (screen coordinates). - FColumns.FDropTarget := NoColumn; + FColumns.DropTarget := NoColumn; Start := Treeview.ScreenToClient(Start); Inc(Start.Y, FHeight); - FColumns.FDragIndex := FColumns.ColumnFromPosition(Start); - DragColumn := FColumns[FColumns.FDragIndex]; + FColumns.DragIndex := FColumns.ColumnFromPosition(Start); + DragColumn := FColumns[FColumns.DragIndex]; Image := TBitmap.Create; with Image do @@ -11066,7 +11080,7 @@ procedure TVTHeader.RescaleHeader; while I > NoColumn do begin if (coFixed in FColumns[I].Options) and (FColumns[I].Width < FColumns[I].MinWidth) then - FColumns[I].FWidth := FColumns[I].FMinWidth; + FColumns[I].FWidth := FColumns[I].MinWidth; //TODO : SetWidth has side effects and this bypasses them, what to do here? I := GetNextVisibleColumn(I); end; FixedWidth := GetVisibleFixedWidth; @@ -11154,7 +11168,7 @@ procedure TVTHeader.UpdateSpringColumns; // Count how many columns have spring enabled. SpringCount := 0; for I := 0 to FColumns.Count-1 do - if [coVisible, coAutoSpring] * FColumns[I].FOptions = [coVisible, coAutoSpring] then + if [coVisible, coAutoSpring] * FColumns[I].Options = [coVisible, coAutoSpring] then Inc(SpringCount); if SpringCount > 0 then begin @@ -11162,15 +11176,15 @@ procedure TVTHeader.UpdateSpringColumns; Difference := ChangeBy / SpringCount; // Adjust the column's size accumulators and resize if the result is >= 1. for I := 0 to FColumns.Count - 1 do - if [coVisible, coAutoSpring] * FColumns[I].FOptions = [coVisible, coAutoSpring] then + if [coVisible, coAutoSpring] * FColumns[I].Options = [coVisible, coAutoSpring] then begin // Sum up rest changes from previous runs and the amount from this one and store it in the // column. If there is at least one pixel difference then do a resize and reset the accumulator. - NewAccumulator := FColumns[I].FSpringRest + Difference; + NewAccumulator := FColumns[I].SpringRest + Difference; // Set new width if at least one pixel size difference is reached. if NewAccumulator >= 1 then - FColumns[I].SetWidth(FColumns[I].FWidth + (Trunc(NewAccumulator) * Sign)); - FColumns[I].FSpringRest := Frac(NewAccumulator); + FColumns[I].SetWidth(FColumns[I].Width + (Trunc(NewAccumulator) * Sign)); + FColumns[I].SpringRest := Frac(NewAccumulator); // Keep track of the size count. ChangeBy := ChangeBy - Difference; @@ -11277,7 +11291,7 @@ procedure TVTHeader.AutoFitColumns(Animated: Boolean = True; SmartAutoFitType: T smaAllColumns: Result := True; smaUseColumnOption: - Result := coSmartResize in FColumns.Items[ColumnIndex].FOptions; + Result := coSmartResize in FColumns.Items[ColumnIndex].Options; else Result := False; end; @@ -11289,7 +11303,7 @@ procedure TVTHeader.AutoFitColumns(Animated: Boolean = True; SmartAutoFitType: T begin with FColumns do - if ([coResizable, coVisible] * Items[FPositionToIndex[Column]].FOptions = [coResizable, coVisible]) and + if ([coResizable, coVisible] * Items[FPositionToIndex[Column]].Options = [coResizable, coVisible]) and DoBeforeAutoFitColumn(FPositionToIndex[Column], SmartAutoFitType) and not TreeView.OperationCanceled then begin if Animated then @@ -11421,7 +11435,7 @@ procedure TVTHeader.Invalidate(Column: TVirtualTreeColumn; ExpandToBorder: Boole OffsetRect(R, ComputeRTLOffset, 0); if ExpandToBorder then begin - if (hoFullRepaintOnResize in FHeader.FOptions) then + if (hoFullRepaintOnResize in FHeader.Options) then begin R.Left := FHeaderRect.Left; R.Right := FHeaderRect.Right; @@ -11629,8 +11643,8 @@ function TVTHeader.ResizeColumns(ChangeBy: Integer; RangeStartCol: TColumnIndex; MaxWidth := 0; MaxReserveCol := NoColumn; for Column := RangeStartCol to RangeEndCol do - if (Options * FColumns[Column].FOptions = Options) and - (FColumns[Column].FWidth > MaxWidth) then + if (Options * FColumns[Column].Options = Options) and + (FColumns[Column].Width > MaxWidth) then begin MaxWidth := Widths[Column - RangeStartCol]; MaxReserveCol := Column; @@ -11658,7 +11672,7 @@ function TVTHeader.ResizeColumns(ChangeBy: Integer; RangeStartCol: TColumnIndex; SetLength(Constraints, RangeEndCol - RangeStartCol + 1); for I := RangeStartCol to RangeEndCol do begin - Widths[I - RangeStartCol] := FColumns[I].FWidth; + Widths[I - RangeStartCol] := FColumns[I].Width; Constraints[I - RangeStartCol] := IfThen(BonusPixel, FColumns[I].MaxWidth, FColumns[I].MinWidth); end; @@ -11667,7 +11681,7 @@ function TVTHeader.ResizeColumns(ChangeBy: Integer; RangeStartCol: TColumnIndex; MaxDelta := 0; ColCount := 0; for I := RangeStartCol to RangeEndCol do - if (Options * FColumns[I].FOptions = Options) and IsResizable(I) then + if (Options * FColumns[I].Options = Options) and IsResizable(I) then begin Inc(ColCount); IncDelta(I); @@ -11686,25 +11700,25 @@ function TVTHeader.ResizeColumns(ChangeBy: Integer; RangeStartCol: TColumnIndex; if Difference > 0 then for I := RangeStartCol to RangeEndCol do - if (Options * FColumns[I].FOptions = Options) and IsResizable(I) then + if (Options * FColumns[I].Options = Options) and IsResizable(I) then ChangeWidth(I, Difference * Sign); // Now distribute Rest. I := Start; while Rest > 0 do begin - if (Options * FColumns[I].FOptions = Options) and IsResizable(I) then - if FColumns[I].FBonusPixel <> BonusPixel then + if (Options * FColumns[I].Options = Options) and IsResizable(I) then + if FColumns[I].BonusPixel <> BonusPixel then begin Dec(Rest, ChangeWidth(I, Sign)); - FColumns[I].FBonusPixel := BonusPixel; + FColumns[I].BonusPixel := BonusPixel; end; Inc(I, Sign); if (BonusPixel and (I > RangeEndCol)) or (not BonusPixel and (I < RangeStartCol)) then begin for I := RangeStartCol to RangeEndCol do - if Options * FColumns[I].FOptions = Options then - FColumns[I].FBonusPixel := not FColumns[I].FBonusPixel; + if Options * FColumns[I].Options = Options then + FColumns[I].BonusPixel := not FColumns[I].BonusPixel; I := Start; end; end; @@ -11713,9 +11727,9 @@ function TVTHeader.ResizeColumns(ChangeBy: Integer; RangeStartCol: TColumnIndex; // Now set the computed widths. We also compute the result here. Include(FStates, hsResizing); for I := RangeStartCol to RangeEndCol do - if (Options * FColumns[I].FOptions = Options) then + if (Options * FColumns[I].Options = Options) then begin - Inc(Result, Widths[I - RangeStartCol] - FColumns[I].FWidth); + Inc(Result, Widths[I - RangeStartCol] - FColumns[I].Width); FColumns[I].SetWidth(Widths[I - RangeStartCol]); end; Exclude(FStates, hsResizing); @@ -11734,7 +11748,7 @@ procedure TVTHeader.RestoreColumns; begin with FColumns do for I := Count - 1 downto 0 do - if [coResizable, coVisible] * Items[FPositionToIndex[I]].FOptions = [coResizable, coVisible] then + if [coResizable, coVisible] * Items[FPositionToIndex[I]].Options = [coResizable, coVisible] then Items[I].RestoreLastWidth; end; @@ -11973,7 +11987,7 @@ function TVTColors.GetHeaderFontColor: TColor; if FOwner.VclStyleEnabled and (seFont in FOwner.StyleElements) then StyleServices.GetElementColor(StyleServices.GetElementDetails(thHeaderItemNormal), ecTextColor, Result) else - Result := FOwner.FHeader.Font.Color; + Result := FOwner.Header.Font.Color; end; //---------------------------------------------------------------------------------------------------------------------- @@ -11991,7 +12005,7 @@ function TVTColors.GetNodeFontColor: TColor; function TVTColors.GetSelectedNodeFontColor(Focused: boolean): TColor; begin if Focused then begin - if (tsUseExplorerTheme in FOwner.FStates) and not IsHighContrastEnabled then begin + if (tsUseExplorerTheme in FOwner.TreeStates) and not IsHighContrastEnabled then begin Result := NodeFontColor end else @@ -12042,7 +12056,7 @@ procedure TVTColors.Assign(Source: TPersistent); if Source is TVTColors then begin FColors := TVTColors(Source).FColors; - if FOwner.FUpdateCount = 0 then + if FOwner.UpdateCount = 0 then FOwner.Invalidate; end else @@ -12219,7 +12233,7 @@ destructor TBaseVirtualTree.Destroy; CheckSynchronize(); CheckSynchronize(); end;// if - Exclude(FOptions.FMiscOptions, toReadOnly); + FOptions.MiscOptions := FOptions.MiscOptions - [toReadOnly]; // Make sure there is no reference remaining to the releasing tree. TWorkerThread.ReleaseThreadReference(); StopWheelPanning; @@ -12373,7 +12387,7 @@ procedure TBaseVirtualTree.CalculateVerticalAlignments(var PaintInfo: TVTPaintIn VAlign := MulDiv((Integer(NodeHeight[Node]) - VAlign), Node.Align, 100) + VAlign div 2; end else - if toShowButtons in FOptions.FPaintOptions then + if toShowButtons in FOptions.PaintOptions then VAlign := MulDiv((Integer(NodeHeight[Node]) - FPlusBM.Height), Node.Align, 100) + FPlusBM.Height div 2 else VAlign := MulDiv(Integer(Node.NodeHeight), Node.Align, 100); @@ -12427,7 +12441,7 @@ function TBaseVirtualTree.ChangeCheckState(Node: PVirtualNode; Value: TCheckStat ctTriStateCheckBox: begin // Propagate state down to the children. - if toAutoTristateTracking in FOptions.FAutoOptions then + if toAutoTristateTracking in FOptions.AutoOptions then case Value of csUncheckedNormal: if Node.ChildCount > 0 then @@ -12534,7 +12548,7 @@ function TBaseVirtualTree.ChangeCheckState(Node: PVirtualNode; Value: TCheckStat // Propagate state up to the parent. if not (vsInitialized in Parent.States) then InitNode(Parent); - if (toAutoTristateTracking in FOptions.FAutoOptions) and ([vsChecking, vsDisabled] * Parent.States = []) and + if (toAutoTristateTracking in FOptions.AutoOptions) and ([vsChecking, vsDisabled] * Parent.States = []) and (CheckType in [ctCheckBox, ctTriStateCheckBox]) and (Parent <> FRoot) and (Parent.CheckType = ctTriStateCheckBox) then Result := CheckParentCheckState(Node, Value) @@ -12597,8 +12611,8 @@ function TBaseVirtualTree.CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRigh // Initialize short hand variables to speed up tests below. DoSwitch := ssCtrl in FDrawSelShiftState; - AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.FAutoOptions); - SimpleSelection := toSimpleDrawSelection in FOptions.FSelectionOptions; + AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.AutoOptions); + SimpleSelection := toSimpleDrawSelection in FOptions.SelectionOptions; // This is the node to start with. Run := GetNodeAt(0, MinY, False, CurrentTop); @@ -12616,7 +12630,7 @@ function TBaseVirtualTree.CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRigh // Simple selection allows to draw the selection rectangle anywhere. No intersection with node captions is // required. Only top and bottom bounds of the rectangle matter. - if SimpleSelection or (toFullRowSelect in FOptions.FSelectionOptions) then + if SimpleSelection or (toFullRowSelect in FOptions.SelectionOptions) then begin IsInOldRect := (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom) and ((FHeader.Columns.Count = 0) or (FHeader.Columns.TotalWidth > OldRect.Left)) and ((NodeLeft + LabelOffset) < OldRect.Right); @@ -12628,7 +12642,7 @@ function TBaseVirtualTree.CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRigh // The right column border might be extended if column spanning is enabled. if AutoSpan then begin - with FHeader.FColumns do + with FHeader.Columns do begin NextColumn := MainColumn; repeat @@ -12750,22 +12764,22 @@ function TBaseVirtualTree.CollectSelectedNodesRTL(MainColumn, NodeLeft, NodeRigh // Initialize short hand variables to speed up tests below. DoSwitch := ssCtrl in FDrawSelShiftState; - WithCheck := (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages); + WithCheck := (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages); // Don't check the events here as descendant trees might have overriden the DoGetImageIndex method. WithStateImages := Assigned(FStateImages) or Assigned(OnGetImageIndexEx); if WithCheck then CheckOffset := FCheckImages.Width + FImagesMargin else CheckOffset := 0; - AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.FAutoOptions); - SimpleSelection := toSimpleDrawSelection in FOptions.FSelectionOptions; + AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.AutoOptions); + SimpleSelection := toSimpleDrawSelection in FOptions.SelectionOptions; // This is the node to start with. Run := GetNodeAt(0, MinY, False, CurrentTop); if Assigned(Run) then begin // The initial minimal left border is determined by the identation level of the node and is dynamically adjusted. - if toShowRoot in FOptions.FPaintOptions then + if toShowRoot in FOptions.PaintOptions then Dec(NodeRight, Integer((GetNodeLevel(Run) + 1) * FIndent) + FMargin) else Dec(NodeRight, Integer(GetNodeLevel(Run) * FIndent) + FMargin); @@ -12796,16 +12810,16 @@ function TBaseVirtualTree.CollectSelectedNodesRTL(MainColumn, NodeLeft, NodeRigh begin NextColumn := MainColumn; repeat - Dummy := FHeader.FColumns.GetPreviousVisibleColumn(NextColumn); + Dummy := FHeader.Columns.GetPreviousVisibleColumn(NextColumn); if (Dummy = InvalidColumn) or not ColumnIsEmpty(Run, Dummy) or - (FHeader.FColumns[Dummy].BiDiMode = bdLeftToRight) then + (FHeader.Columns[Dummy].BiDiMode = bdLeftToRight) then Break; NextColumn := Dummy; until False; if NextColumn = MainColumn then CurrentLeft := NodeLeft else - FHeader.FColumns.GetColumnBounds(NextColumn, CurrentLeft, Dummy); + FHeader.Columns.GetColumnBounds(NextColumn, CurrentLeft, Dummy); end else CurrentLeft := NodeLeft; @@ -12923,7 +12937,7 @@ procedure TBaseVirtualTree.ClearNodeBackground(const PaintInfo: TVTPaintInfo; Us end else begin - if (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.FSelectionOptions) and + if (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.SelectionOptions) and (vsSelected in Node.States) and not (toUseBlendedSelection in FOptions.PaintOptions) and not (tsUseExplorerTheme in FStates) then begin @@ -12933,7 +12947,7 @@ procedure TBaseVirtualTree.ClearNodeBackground(const PaintInfo: TVTPaintInfo; Us FillRect(Rect(R.Left, R.Bottom - 1, R.Right, R.Bottom)); Dec(R.Bottom); end; - if Focused or (toPopupMode in FOptions.FPaintOptions) then + if Focused or (toPopupMode in FOptions.PaintOptions) then begin Brush.Color := FColors.FocusedSelectionColor; Pen.Color := FColors.FocusedSelectionBorderColor; @@ -12981,10 +12995,10 @@ function TBaseVirtualTree.CompareNodePositions(Node1, Node2: PVirtualNode; Consi else begin if HasAsParent(Node1, Node2) then - Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions), -1, 1) + Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions), -1, 1) else if HasAsParent(Node2, Node1) then - Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions), 1, -1) + Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions), 1, -1) else begin // the given nodes are neither equal nor are they parents of each other, so go up to FRoot @@ -13300,8 +13314,8 @@ function TBaseVirtualTree.GetDisabled(Node: PVirtualNode): Boolean; function TBaseVirtualTree.GetSyncCheckstateWithSelection(Node: PVirtualNode): Boolean; begin - Result := (toSyncCheckboxesWithSelection in FOptions.FSelectionOptions) - and (toCheckSupport in FOptions.FMiscOptions) + Result := (toSyncCheckboxesWithSelection in FOptions.SelectionOptions) + and (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages) and (Node.CheckType = ctCheckBox); ; end; @@ -13381,7 +13395,7 @@ function TBaseVirtualTree.GetNodeHeight(Node: PVirtualNode): Cardinal; begin if Assigned(Node) and (Node <> FRoot) then begin - if (toVariableNodeHeight in FOptions.FMiscOptions) and not (vsDeleting in Node.States) then + if (toVariableNodeHeight in FOptions.MiscOptions) and not (vsDeleting in Node.States) then begin if not (vsInitialized in Node.States) then InitNode(Node); @@ -13436,7 +13450,7 @@ procedure TBaseVirtualTree.GetOffsets(pNode: PVirtualNode; out pOffsets: TVTOffs if not (toFixedIndent in TreeOptions.PaintOptions) then begin // plus Indent lNodeLevel := GetNodeLevel(pNode); - if toShowRoot in FOptions.FPaintOptions then + if toShowRoot in FOptions.PaintOptions then Inc(lNodeLevel); end else @@ -13451,7 +13465,7 @@ procedure TBaseVirtualTree.GetOffsets(pNode: PVirtualNode; out pOffsets: TVTOffs exit; // right of checkbox, left of state image - if (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages) and (pNode.CheckType <> ctNone) and (pColumn = Header.MainColumn) then + if (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages) and (pNode.CheckType <> ctNone) and (pColumn = Header.MainColumn) then pOffsets[TVTElement.ofsStateImage] := pOffsets[TVTElement.ofsCheckBox] + FCheckImages.Width + fImagesMargin else pOffsets[TVTElement.ofsStateImage] := pOffsets[TVTElement.ofsCheckBox]; @@ -13689,8 +13703,8 @@ function TBaseVirtualTree.HandleDrawSelection(X, Y: Integer): Boolean; end else begin - CurrentBidiMode := FHeader.FColumns[MainColumn].BidiMode; - CurrentAlignment := FHeader.FColumns[MainColumn].Alignment; + CurrentBidiMode := FHeader.Columns[MainColumn].BidiMode; + CurrentAlignment := FHeader.Columns[MainColumn].Alignment; end; // Determine initial left border of first node (take column reordering into account). @@ -13698,8 +13712,8 @@ function TBaseVirtualTree.HandleDrawSelection(X, Y: Integer): Boolean; begin // The mouse coordinates don't include any horizontal scrolling hence take this also // out from the returned column position. - NodeLeft := FHeader.FColumns[MainColumn].Left + FEffectiveOffsetX; - NodeRight := NodeLeft + FHeader.FColumns[MainColumn].Width; + NodeLeft := FHeader.Columns[MainColumn].Left + FEffectiveOffsetX; + NodeRight := NodeLeft + FHeader.Columns[MainColumn].Width; end else begin @@ -13793,8 +13807,8 @@ procedure TBaseVirtualTree.InitializeFirstColumnValues(var PaintInfo: TVTPaintIn // Determines initial index, position and cell size of the first visible column. begin - PaintInfo.Column := FHeader.FColumns.GetFirstVisibleColumn; - with FHeader.FColumns, PaintInfo do + PaintInfo.Column := FHeader.Columns.GetFirstVisibleColumn; + with FHeader.Columns, PaintInfo do begin if Column > NoColumn then begin @@ -14066,10 +14080,10 @@ procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); begin SetSize(Size.cx, Size.cy); - if IsWinVistaOrAbove and (tsUseThemes in FStates) and (toUseExplorerTheme in FOptions.FPaintOptions) or VclStyleEnabled then + if IsWinVistaOrAbove and (tsUseThemes in FStates) and (toUseExplorerTheme in FOptions.PaintOptions) or VclStyleEnabled then begin if (FHeader.MainColumn > NoColumn) then - Brush.Color := FHeader.FColumns[FHeader.MainColumn].GetEffectiveColor + Brush.Color := FHeader.Columns[FHeader.MainColumn].GetEffectiveColor else Brush.Color := FColors.BackGroundColor; end @@ -14308,9 +14322,9 @@ procedure TBaseVirtualTree.SetAnimationDuration(const Value: Cardinal); begin FAnimationDuration := Value; if FAnimationDuration = 0 then - Exclude(FOptions.FAnimationOptions, toAnimatedToggle) + FOptions.AnimationOptions := FOptions.AnimationOptions - [toAnimatedToggle] else - Include(FOptions.FAnimationOptions, toAnimatedToggle); + FOptions.AnimationOptions := FOptions.AnimationOptions + [toAnimatedToggle] end; //---------------------------------------------------------------------------------------------------------------------- @@ -14495,13 +14509,13 @@ procedure TBaseVirtualTree.SetCheckStateForAll(aCheckState: TCheckState; pSelect procedure TBaseVirtualTree.SetCheckType(Node: PVirtualNode; Value: TCheckType); begin - if (Node.CheckType <> Value) and not (toReadOnly in FOptions.FMiscOptions) then + if (Node.CheckType <> Value) and not (toReadOnly in FOptions.MiscOptions) then begin Node.CheckType := Value; if (Value <> ctTriStateCheckBox) and (Node.CheckState in [csMixedNormal, csMixedPressed]) then Node.CheckState := csUncheckedNormal;// reset check state if it doesn't fit the new check type // For check boxes with tri-state check box parents we have to initialize differently. - if (toAutoTriStateTracking in FOptions.FAutoOptions) and (Value in [ctCheckBox, ctTriStateCheckBox]) and + if (toAutoTriStateTracking in FOptions.AutoOptions) and (Value in [ctCheckBox, ctTriStateCheckBox]) and (Node.Parent <> FRoot) then begin if not (vsInitialized in Node.Parent.States) then @@ -14532,7 +14546,7 @@ procedure TBaseVirtualTree.SetChildCount(Node: PVirtualNode; NewChildCount: Card Count: Integer; NewHeight: Integer; begin - if not (toReadOnly in FOptions.FMiscOptions) then + if not (toReadOnly in FOptions.MiscOptions) then begin if Node = nil then Node := FRoot; @@ -14580,7 +14594,7 @@ procedure TBaseVirtualTree.SetChildCount(Node: PVirtualNode; NewChildCount: Card Dec(Remaining); Inc(Index); - if (toVariableNodeHeight in FOptions.FMiscOptions) then + if (toVariableNodeHeight in FOptions.MiscOptions) then GetNodeHeight(Child); Inc(NewHeight, Child.TotalHeight); end; @@ -14590,8 +14604,8 @@ procedure TBaseVirtualTree.SetChildCount(Node: PVirtualNode; NewChildCount: Card AdjustTotalCount(Node, Count, True); Node.ChildCount := NewChildCount; - if (FUpdateCount = 0) and (toAutoSort in FOptions.FAutoOptions) and (FHeader.FSortColumn > InvalidColumn) then - Sort(Node, FHeader.FSortColumn, FHeader.FSortDirection, True); + if (FUpdateCount = 0) and (toAutoSort in FOptions.AutoOptions) and (FHeader.SortColumn > InvalidColumn) then + Sort(Node, FHeader.SortColumn, FHeader.SortDirection, True); InvalidateCache; end//if NewChildCount > Node.ChildCount @@ -14783,7 +14797,7 @@ procedure TBaseVirtualTree.SetFocusedColumn(Value: TColumnIndex); InvalidateColumn(FFocusedColumn); InvalidateColumn(Value); FFocusedColumn := Value; - if Assigned(FFocusedNode) and not (toDisableAutoscrollOnFocus in FOptions.FAutoOptions) then + if Assigned(FFocusedNode) and not (toDisableAutoscrollOnFocus in FOptions.AutoOptions) then begin if ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, True) then InvalidateNode(FFocusedNode); @@ -14843,7 +14857,7 @@ procedure TBaseVirtualTree.SetFullyVisible(Node: PVirtualNode; Value: Boolean); procedure TBaseVirtualTree.SetHasChildren(Node: PVirtualNode; Value: Boolean); begin - if Assigned(Node) and not (toReadOnly in FOptions.FMiscOptions) then + if Assigned(Node) and not (toReadOnly in FOptions.MiscOptions) then begin if Value then Include(Node.States, vsHasChildren) @@ -14895,7 +14909,7 @@ procedure TBaseVirtualTree.SetFiltered(Node: PVirtualNode; Value: Boolean); if Value then begin Include(Node.States, vsFiltered); - if not (toShowFilteredNodes in FOptions.FPaintOptions) then + if not (toShowFilteredNodes in FOptions.PaintOptions) then begin if (vsInitializing in Node.States) and not (vsHasChildren in Node.States) then AdjustTotalHeight(Node, 0, False) @@ -14918,7 +14932,7 @@ procedure TBaseVirtualTree.SetFiltered(Node: PVirtualNode; Value: Boolean); else begin Exclude(Node.States, vsFiltered); - if not (toShowFilteredNodes in FOptions.FPaintOptions) then + if not (toShowFilteredNodes in FOptions.PaintOptions) then begin AdjustTotalHeight(Node, Integer(NodeHeight[Node]), True); if FullyVisible[Node] then @@ -15262,7 +15276,7 @@ procedure TBaseVirtualTree.SetSelected(Node: PVirtualNode; Value: Boolean); if FSelectionCount = 0 then FRangeAnchor := Node else begin - if not (toMultiSelect in FOptions.FSelectionOptions) then + if not (toMultiSelect in FOptions.SelectionOptions) then ClearSelection; if FRangeAnchor = nil then FRangeAnchor := Node; @@ -15270,7 +15284,7 @@ procedure TBaseVirtualTree.SetSelected(Node: PVirtualNode; Value: Boolean); AddToSelection(Node, True); - if not (toMultiSelect in FOptions.FSelectionOptions) then + if not (toMultiSelect in FOptions.SelectionOptions) then FocusedNode := GetFirstSelected; // if only one node can be selected, make sure the focused node changes with the selected node // Make sure there is a valid column selected (if there are columns at all). if ((FFocusedColumn < 0) or not (coVisible in FHeader.Columns[FFocusedColumn].Options)) and @@ -15568,6 +15582,13 @@ procedure TBaseVirtualTree.SetWindowTheme(const Theme: string); //---------------------------------------------------------------------------------------------------------------------- +//used by TCustomVirtualTreeOptions +procedure TBaseVirtualTree.SetVisibleCount(value : Cardinal); +begin + FVisibleCount := value; +end; + + procedure TBaseVirtualTree.TileBackground(Source: TPicture; Target: TCanvas; Offset: TPoint; R: TRect; aBkgColor: TColor); // Draws the given source graphic so that it tiles into the given rectangle which is relative to the target bitmap. @@ -15637,7 +15658,7 @@ function TBaseVirtualTree.ToggleCallback(Step, StepSize: Integer; Data: Pointer) LocalBrush: HBRUSH; begin - with TToggleAnimationData(Data^), FHeader.FColumns do + with TToggleAnimationData(Data^), FHeader.Columns do begin // Iterate through all columns and erase background in their local color. // LocalBrush is a brush in the color of the particular column. @@ -15645,7 +15666,7 @@ function TBaseVirtualTree.ToggleCallback(Step, StepSize: Integer; Data: Pointer) while (Column > InvalidColumn) and (Run.Left < ClientWidth) do begin GetColumnBounds(Column, Run.Left, Run.Right); - if coParentColor in Items[Column].FOptions then + if coParentColor in Items[Column].Options then FillRect(DC, Run, Brush) else begin @@ -15768,8 +15789,8 @@ procedure TBaseVirtualTree.CMBiDiModeChanged(var Message: TMessage); if FEffectiveOffsetX < 0 then FEffectiveOffsetX := 0; - if toAutoBidiColumnOrdering in FOptions.FAutoOptions then - FHeader.FColumns.ReorderColumns(UseRightToLeftAlignment); + if toAutoBidiColumnOrdering in FOptions.AutoOptions then + FHeader.Columns.ReorderColumns(UseRightToLeftAlignment); FHeader.Invalidate(nil); end; @@ -15994,15 +16015,15 @@ procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow); //workaround for issue #291 //it duplicates parts of the following code and code in TVirtualTreeHintWindow HintStr := ''; - if FHeader.UseColumns and (hoShowHint in FHeader.FOptions) and FHeader.InHeader(CursorPos) then + if FHeader.UseColumns and (hoShowHint in FHeader.Options) and FHeader.InHeader(CursorPos) then begin CursorRect := FHeaderRect; // Convert the cursor rectangle into real client coordinates. - OffsetRect(CursorRect, 0, -Integer(FHeader.FHeight)); - HitInfo.HitColumn := FHeader.FColumns.GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right); + OffsetRect(CursorRect, 0, -Integer(FHeader.Height)); + HitInfo.HitColumn := FHeader.Columns.GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right); if (HitInfo.HitColumn > NoColumn) and not (csLButtonDown in ControlState) and - (FHeader.FColumns[HitInfo.HitColumn].FHint <> '') then - HintStr := FHeader.FColumns[HitInfo.HitColumn].FHint; + (FHeader.Columns[HitInfo.HitColumn].Hint <> '') then + HintStr := FHeader.Columns[HitInfo.HitColumn].Hint; end else if HintMode = hmDefault then @@ -16017,12 +16038,12 @@ procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow); end; // First check whether there is a header hint to show. - if FHeader.UseColumns and (hoShowHint in FHeader.FOptions) and FHeader.InHeader(CursorPos) then + if FHeader.UseColumns and (hoShowHint in FHeader.Options) and FHeader.InHeader(CursorPos) then begin CursorRect := FHeaderRect; // Convert the cursor rectangle into real client coordinates. - OffsetRect(CursorRect, 0, -Integer(FHeader.FHeight)); - HitInfo.HitColumn := FHeader.FColumns.GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right); + OffsetRect(CursorRect, 0, -Integer(FHeader.Height)); + HitInfo.HitColumn := FHeader.Columns.GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right); // align the vertical hint position on the bottom bound of the header, but // avoid overlapping of mouse cursor and hint HintPos.Y := Max(HintPos.Y, ClientToScreen(Point(0, CursorRect.Bottom)).Y); @@ -16031,9 +16052,9 @@ procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow); // cancel this with ESC. if (HitInfo.HitColumn > NoColumn) and not (csLButtonDown in ControlState) then begin - HintStr := FHeader.FColumns[HitInfo.HitColumn].FHint; + HintStr := FHeader.Columns[HitInfo.HitColumn].Hint; if HintStr = '' then - with FHeader.FColumns[HitInfo.HitColumn] do + with FHeader.Columns[HitInfo.HitColumn] do begin if (2 * FMargin + CaptionWidth + 1) >= Width then HintStr := FCaptionText; @@ -16073,19 +16094,19 @@ procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow); begin if HitInfo.HitColumn > NoColumn then begin - FHeader.FColumns.GetColumnBounds(HitInfo.HitColumn, ColLeft, ColRight); + FHeader.Columns.GetColumnBounds(HitInfo.HitColumn, ColLeft, ColRight); // The right column border might be extended if column spanning is enabled. - if toAutoSpanColumns in FOptions.FAutoOptions then + if toAutoSpanColumns in FOptions.AutoOptions then begin SpanColumn := HitInfo.HitColumn; repeat - Dummy := FHeader.FColumns.GetNextVisibleColumn(SpanColumn); + Dummy := FHeader.Columns.GetNextVisibleColumn(SpanColumn); if (Dummy = InvalidColumn) or not ColumnIsEmpty(HitInfo.HitNode, Dummy) then Break; SpanColumn := Dummy; until False; if SpanColumn <> HitInfo.HitColumn then - FHeader.FColumns.GetColumnBounds(SpanColumn, Dummy, ColRight); + FHeader.Columns.GetColumnBounds(SpanColumn, Dummy, ColRight); end; end else @@ -16241,16 +16262,16 @@ procedure TBaseVirtualTree.CMMouseLeave(var Message: TMessage); if Assigned(FCurrentHotNode) then begin DoHotChange(FCurrentHotNode, nil); - if (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.FMiscOptions) then + if (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.MiscOptions) then InvalidateNode(FCurrentHotNode); FCurrentHotNode := nil; end; if Assigned(Header) then begin - Header.FColumns.FDownIndex := NoColumn; - Header.FColumns.FHoverIndex := NoColumn; - Header.FColumns.FCheckBoxHit := False; + Header.FColumns.DownIndex := NoColumn; + Header.FColumns.HoverIndex := NoColumn; + Header.FColumns.CheckBoxHit := False; end; DoMouseLeave(); inherited; @@ -16623,9 +16644,9 @@ procedure TBaseVirtualTree.WMHScroll(var Message: TWMHScroll); UpdateHorizontalScrollBar(False); end; SB_LINELEFT: - SetOffsetX(FOffsetX + RTLFactor * FScrollBarOptions.FIncrementX); + SetOffsetX(FOffsetX + RTLFactor * FScrollBarOptions.HorizontalIncrement); SB_LINERIGHT: - SetOffsetX(FOffsetX - RTLFactor * FScrollBarOptions.FIncrementX); + SetOffsetX(FOffsetX - RTLFactor * FScrollBarOptions.VerticalIncrement); SB_PAGELEFT: SetOffsetX(FOffsetX + RTLFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth)); SB_PAGERIGHT: @@ -16689,7 +16710,7 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); begin if (not assigned(anode)) or (not FHeader.UseColumns) - or (not (toAutoSpanColumns in FOptions.FAutoOptions)) + or (not (toAutoSpanColumns in FOptions.AutoOptions)) or (acolumn = FHeader.MainColumn) then begin //previously existing logic @@ -16697,7 +16718,7 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); exit; end; //consider auto spanning - with FHeader.FColumns do //standard loop for auto span + with FHeader.Columns do //standard loop for auto span begin PrevColumn := acolumn; repeat @@ -16720,7 +16741,7 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); begin if (not assigned(anode)) or (not FHeader.UseColumns) - or (not (toAutoSpanColumns in FOptions.FAutoOptions)) + or (not (toAutoSpanColumns in FOptions.AutoOptions)) or (acolumn = FHeader.MainColumn) then begin //previously existing logic @@ -16728,7 +16749,7 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); exit; end; //consider auto spanning - with FHeader.FColumns do //standard loop for auto span + with FHeader.Columns do //standard loop for auto span begin NextColumn := acolumn; repeat @@ -16752,10 +16773,10 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); result := false; if (not assigned(anode)) or (not FHeader.UseColumns) - or (not (toAutoSpanColumns in FOptions.FAutoOptions)) + or (not (toAutoSpanColumns in FOptions.AutoOptions)) or (acolumn = FHeader.MainColumn) then exit; - with FHeader.FColumns do + with FHeader.Columns do begin previousColumn := FHeader.Columns.GetPreviousVisibleColumn(acolumn); if (previousColumn = InvalidColumn) //there is no previous column @@ -16782,15 +16803,15 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); begin if (CharCode in [VK_HOME, VK_END, VK_PRIOR, VK_NEXT, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_BACK, VK_TAB]) and (RootNode.FirstChild <> nil) then begin - PerformMultiSelect := (ssShift in Shift) and (toMultiSelect in FOptions.FSelectionOptions) and not IsEditing; + PerformMultiSelect := (ssShift in Shift) and (toMultiSelect in FOptions.SelectionOptions) and not IsEditing; // Flag to avoid range selection in case of single node advance. DoRangeSelect := (CharCode in [VK_HOME, VK_END, VK_PRIOR, VK_NEXT]) and PerformMultiSelect and not IsEditing; NeedInvalidate := DoRangeSelect or (FSelectionCount > 1); - ActAsGrid := toGridExtensions in FOptions.FMiscOptions; + ActAsGrid := toGridExtensions in FOptions.MiscOptions; ClearPending := (Shift = []) or (ActAsGrid and not (ssShift in Shift)) or - not (toMultiSelect in FOptions.FSelectionOptions) or (CharCode in [VK_TAB, VK_BACK]); + not (toMultiSelect in FOptions.SelectionOptions) or (CharCode in [VK_TAB, VK_BACK]); // Keep old focused node for range selection. Use a default node if none was focused until now. LastFocused := FFocusedNode; @@ -16814,15 +16835,15 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); begin if (CharCode = VK_END) xor UseRightToLeftAlignment then begin - GetStartColumn := FHeader.FColumns.GetLastVisibleColumn; - GetNextColumn := FHeader.FColumns.GetPreviousVisibleColumn; + GetStartColumn := FHeader.Columns.GetLastVisibleColumn; + GetNextColumn := FHeader.Columns.GetPreviousVisibleColumn; GetNextNode := GetPreviousVisible; Node := GetLastVisible(nil, True); end else begin - GetStartColumn := FHeader.FColumns.GetFirstVisibleColumn; - GetNextColumn := FHeader.FColumns.GetNextVisibleColumn; + GetStartColumn := FHeader.Columns.GetFirstVisibleColumn; + GetNextColumn := FHeader.Columns.GetNextVisibleColumn; GetNextNode := GetNextVisible; Node := GetFirstVisible(nil, True); end; @@ -16843,7 +16864,7 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); if (Shift = [ssCtrl]) and not ActAsGrid then begin ScrollIntoView(Node, toCenterScrollIntoView in FOptions.SelectionOptions, - not (toDisableAutoscrollOnFocus in FOptions.FAutoOptions)); + not (toDisableAutoscrollOnFocus in FOptions.AutoOptions)); if (CharCode = VK_HOME) and not UseRightToLeftAlignment then SetOffsetX(0) else @@ -16855,9 +16876,9 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); FocusedNode := Node; //fix: In grid mode, if full row select option is ON, //then also go to the node determined from the earlier logic - if ActAsGrid and (toFullRowSelect in FOptions.FSelectionOptions) then + if ActAsGrid and (toFullRowSelect in FOptions.SelectionOptions) then FocusedNode := Node; - if ActAsGrid and not (toFullRowSelect in FOptions.FSelectionOptions) then + if ActAsGrid and not (toFullRowSelect in FOptions.SelectionOptions) then begin FocusedColumn := NewColumn; // fix: If auto span is ON the last column may be a merged column. So take @@ -16875,18 +16896,18 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); if [ssShift, ssAlt] = Shift then begin if FFocusedColumn <= NoColumn then - NewColumn := FHeader.FColumns.GetFirstVisibleColumn + NewColumn := FHeader.Columns.GetFirstVisibleColumn else begin - Offset := FHeader.FColumns.GetVisibleFixedWidth; + Offset := FHeader.Columns.GetVisibleFixedWidth; NewColumn := FFocusedColumn; while True do begin - TempColumn := FHeader.FColumns.GetPreviousVisibleColumn(NewColumn); - NewWidth := FHeader.FColumns[NewColumn].Width; + TempColumn := FHeader.Columns.GetPreviousVisibleColumn(NewColumn); + NewWidth := FHeader.Columns[NewColumn].Width; if (TempColumn <= NoColumn) or (Offset + NewWidth >= ClientWidth) or - (coFixed in FHeader.FColumns[TempColumn].FOptions) then + (coFixed in FHeader.Columns[TempColumn].Options) then Break; NewColumn := TempColumn; Inc(Offset, NewWidth); @@ -16926,18 +16947,18 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); if [ssShift, ssAlt] = Shift then begin if FFocusedColumn <= NoColumn then - NewColumn := FHeader.FColumns.GetFirstVisibleColumn + NewColumn := FHeader.Columns.GetFirstVisibleColumn else begin - Offset := FHeader.FColumns.GetVisibleFixedWidth; + Offset := FHeader.Columns.GetVisibleFixedWidth; NewColumn := FFocusedColumn; while True do begin - TempColumn := FHeader.FColumns.GetNextVisibleColumn(NewColumn); - NewWidth := FHeader.FColumns[NewColumn].Width; + TempColumn := FHeader.Columns.GetNextVisibleColumn(NewColumn); + NewWidth := FHeader.Columns[NewColumn].Width; if (TempColumn <= NoColumn) or (Offset + NewWidth >= ClientWidth) or - (coFixed in FHeader.FColumns[TempColumn].FOptions) then + (coFixed in FHeader.Columns[TempColumn].Options) then Break; NewColumn := TempColumn; Inc(Offset, NewWidth); @@ -17033,7 +17054,7 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); begin // other special cases Context := NoColumn; - if (toExtendedFocus in FOptions.FSelectionOptions) and (toGridExtensions in FOptions.FMiscOptions) then + if (toExtendedFocus in FOptions.SelectionOptions) and (toGridExtensions in FOptions.MiscOptions) then begin Context := getPreviousVisibleAutoSpanColumn(FFocusedColumn, FFocusedNode); if Context > NoColumn then @@ -17083,7 +17104,7 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); begin // other special cases Context := NoColumn; - if (toExtendedFocus in FOptions.FSelectionOptions) and (toGridExtensions in FOptions.FMiscOptions) then + if (toExtendedFocus in FOptions.SelectionOptions) and (toGridExtensions in FOptions.MiscOptions) then begin Context := getNextVisibleAutoSpanColumn(FFocusedColumn, FFocusedNode); if Context > NoColumn then @@ -17121,20 +17142,20 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); if Assigned(FFocusedNode) and (FFocusedNode.Parent <> FRoot) then FocusedNode := FocusedNode.Parent; VK_TAB: - if (toExtendedFocus in FOptions.FSelectionOptions) and FHeader.UseColumns then + if (toExtendedFocus in FOptions.SelectionOptions) and FHeader.UseColumns then begin // In order to avoid duplicating source code just to change the direction // we use function variables. if ssShift in Shift then begin - GetStartColumn := FHeader.FColumns.GetLastVisibleColumn; - GetNextColumn := FHeader.FColumns.GetPreviousVisibleColumn; + GetStartColumn := FHeader.Columns.GetLastVisibleColumn; + GetNextColumn := FHeader.Columns.GetPreviousVisibleColumn; GetNextNode := GetPreviousVisible; end else begin - GetStartColumn := FHeader.FColumns.GetFirstVisibleColumn; - GetNextColumn := FHeader.FColumns.GetNextVisibleColumn; + GetStartColumn := FHeader.Columns.GetFirstVisibleColumn; + GetNextColumn := FHeader.Columns.GetNextVisibleColumn; GetNextNode := GetNextVisible; end; @@ -17295,7 +17316,7 @@ procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); CancelEditNode; end; VK_SPACE: - if (toCheckSupport in FOptions.FMiscOptions) and Assigned(FFocusedNode) and + if (toCheckSupport in FOptions.MiscOptions) and Assigned(FFocusedNode) and (FFocusedNode.CheckType <> ctNone) then begin NewCheckState := DetermineNextCheckState(FFocusedNode.CheckType, GetCheckState(FFocusedNode)); @@ -17419,7 +17440,7 @@ procedure TBaseVirtualTree.WMKillFocus(var Msg: TWMKillFocus); tsMiddleButtonDown, tsOLEDragPending, tsVCLDragPending, tsIncrementalSearching, tsNodeHeightTrackPending, tsNodeHeightTracking]); - if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.FPaintOptions) then + if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.PaintOptions) then Invalidate else if Assigned(FFocusedNode) then @@ -17511,7 +17532,7 @@ procedure TBaseVirtualTree.WMMButtonDblClk(var Message: TWMMButtonDblClk); inherited; // get information about the hit - if toMiddleClickSelect in FOptions.FSelectionOptions then + if toMiddleClickSelect in FOptions.SelectionOptions then begin GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); HandleMouseDblClick(Message, HitInfo); @@ -17529,12 +17550,12 @@ procedure TBaseVirtualTree.WMMButtonDown(var Message: TWMMButtonDown); begin DoStateChange([tsMiddleButtonDown]); - if FHeader.FStates = [] then + if FHeader.States = [] then begin inherited; // Start wheel panning or scrolling if not already active, allowed and scrolling is useful at all. - if (toWheelPanning in FOptions.FMiscOptions) and ([tsWheelScrolling, tsWheelPanning] * FStates = []) and + if (toWheelPanning in FOptions.MiscOptions) and ([tsWheelScrolling, tsWheelPanning] * FStates = []) and ((Integer(FRangeX) > ClientWidth) or (Integer(FRangeY) > ClientHeight)) then begin FLastClickPos := SmallPointToPoint(Message.Pos); @@ -17545,7 +17566,7 @@ procedure TBaseVirtualTree.WMMButtonDown(var Message: TWMMButtonDown); StopWheelPanning; // Get information about the hit. - if toMiddleClickSelect in FOptions.FSelectionOptions then + if toMiddleClickSelect in FOptions.SelectionOptions then begin GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); HandleMouseDown(Message, HitInfo); @@ -17574,12 +17595,12 @@ procedure TBaseVirtualTree.WMMButtonUp(var Message: TWMMButtonUp); StopWheelPanning; end else - if FHeader.FStates = [] then + if FHeader.States = [] then begin inherited; // get information about the hit - if toMiddleClickSelect in FOptions.FSelectionOptions then + if toMiddleClickSelect in FOptions.SelectionOptions then begin GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); HandleMouseUp(Message, HitInfo); @@ -17595,7 +17616,7 @@ procedure TBaseVirtualTree.WMNCCalcSize(var Message: TWMNCCalcSize); inherited; with FHeader do - if hoVisible in FHeader.FOptions then + if hoVisible in FHeader.Options then with Message.CalcSize_Params^ do Inc(rgrc[0].Top, FHeight); end; @@ -17613,7 +17634,7 @@ procedure TBaseVirtualTree.WMNCDestroy(var Message: TWMNCDestroy); StopTimer(ChangeTimer); StopTimer(StructureChangeTimer); - if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.FMiscOptions) then + if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.MiscOptions) then RevokeDragDrop(Handle); // Clean up other stuff. @@ -17628,7 +17649,7 @@ procedure TBaseVirtualTree.WMNCHitTest(var Message: TWMNCHitTest); begin inherited; - if (hoVisible in FHeader.FOptions) and + if (hoVisible in FHeader.Options) and FHeader.InHeader(ScreenToClient(SmallPointToPoint(Message.Pos))) then Message.Result := HTBORDER; end; @@ -17712,12 +17733,12 @@ procedure TBaseVirtualTree.WMPaint(var Message: TWMPaint); if tsVCLDragging in FStates then ImageList_DragShowNolock(True); - if hoVisible in FHeader.FOptions then + if hoVisible in FHeader.Options then begin DC := GetDCEx(Handle, 0, DCX_CACHE or DCX_CLIPSIBLINGS or DCX_WINDOW or DCX_VALIDATE); if DC <> 0 then try - FHeader.FColumns.PaintHeader(DC, FHeaderRect, -FEffectiveOffsetX); + FHeader.Columns.PaintHeader(DC, FHeaderRect, -FEffectiveOffsetX); finally ReleaseDC(Handle, DC); end; @@ -17791,7 +17812,7 @@ procedure TBaseVirtualTree.WMRButtonDblClk(var Message: TWMRButtonDblClk); inherited; // get information about the hit - if toMiddleClickSelect in FOptions.FSelectionOptions then + if toMiddleClickSelect in FOptions.SelectionOptions then begin GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); HandleMouseDblClick(Message, HitInfo); @@ -17810,12 +17831,12 @@ procedure TBaseVirtualTree.WMRButtonDown(var Message: TWMRButtonDown); begin DoStateChange([tsRightButtonDown]); - if FHeader.FStates = [] then + if FHeader.States = [] then begin inherited; // get information about the hit - if toRightClickSelect in FOptions.FSelectionOptions then + if toRightClickSelect in FOptions.SelectionOptions then begin GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); // Go temporarily into sync mode to avoid a delayed change event for the node when selecting. #679 @@ -17840,7 +17861,7 @@ procedure TBaseVirtualTree.WMRButtonUp(var Message: TWMRButtonUp); begin DoStateChange([], [tsPopupMenuShown, tsRightButtonDown]); - if FHeader.FStates = [] then + if FHeader.States = [] then begin Application.CancelHint; @@ -17856,7 +17877,7 @@ procedure TBaseVirtualTree.WMRButtonUp(var Message: TWMRButtonUp); // get information about the hit GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - if toRightClickSelect in FOptions.FSelectionOptions then + if toRightClickSelect in FOptions.SelectionOptions then HandleMouseUp(Message, HitInfo); if not Assigned(PopupMenu) then @@ -17893,7 +17914,7 @@ procedure TBaseVirtualTree.WMSetCursor(var Message: TWMSetCursor); if not (csDesigning in ComponentState) then begin NewCursor := crDefault; - if (toNodeHeightResize in FOptions.FMiscOptions) then + if (toNodeHeightResize in FOptions.MiscOptions) then begin GetCursorPos(P); P := ScreenToClient(P); @@ -17939,7 +17960,7 @@ procedure TBaseVirtualTree.WMSetFocus(var Msg: TWMSetFocus); begin inherited; - if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.FPaintOptions) then + if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.PaintOptions) then Invalidate; end; @@ -18074,9 +18095,9 @@ procedure TBaseVirtualTree.WMVScroll(var Message: TWMVScroll); RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN); end; SB_LINEUP: - SetOffsetY(FOffsetY + FScrollBarOptions.FIncrementY); + SetOffsetY(FOffsetY + FScrollBarOptions.VerticalIncrement); SB_LINEDOWN: - SetOffsetY(FOffsetY - FScrollBarOptions.FIncrementY); + SetOffsetY(FOffsetY - FScrollBarOptions.VerticalIncrement); SB_PAGEUP: SetOffsetY(FOffsetY + ClientHeight); SB_PAGEDOWN: @@ -18165,7 +18186,7 @@ procedure TBaseVirtualTree.AdjustPaintCellRect(var PaintInfo: TVTPaintInfo; var begin // Since cells are always drawn from left to right the next column index is independent of the // bidi mode, but not the column borders, which might change depending on the cell's content. - NextNonEmpty := FHeader.FColumns.GetNextVisibleColumn(PaintInfo.Column); + NextNonEmpty := FHeader.Columns.GetNextVisibleColumn(PaintInfo.Column); end; //---------------------------------------------------------------------------------------------------------------------- @@ -18458,8 +18479,8 @@ function TBaseVirtualTree.CanAutoScroll: Boolean; IsDropTarget := Assigned(FDragManager) and DragManager.IsDropTarget; IsDrawSelecting := [tsDrawSelPending, tsDrawSelecting] * FStates <> []; IsWheelPanning := [tsWheelPanning, tsWheelScrolling] * FStates <> []; - Result := ((toAutoScroll in FOptions.FAutoOptions) or IsWheelPanning) and - (FHeader.FStates = []) and (IsDrawSelecting or IsDropTarget or (tsVCLDragging in FStates) or IsWheelPanning); + Result := ((toAutoScroll in FOptions.AutoOptions) or IsWheelPanning) and + (FHeader.States = []) and (IsDrawSelecting or IsDropTarget or (tsVCLDragging in FStates) or IsWheelPanning); end; //---------------------------------------------------------------------------------------------------------------------- @@ -18477,8 +18498,8 @@ function TBaseVirtualTree.CanShowDragImage: Boolean; function TBaseVirtualTree.CanSplitterResizeNode(P: TPoint; Node: PVirtualNode; Column: TColumnIndex): Boolean; begin - Result := (toNodeHeightResize in FOptions.FMiscOptions) and Assigned(Node) and (Node <> FRoot) and - (Column > NoColumn) and (coFixed in FHeader.FColumns[Column].FOptions); + Result := (toNodeHeightResize in FOptions.MiscOptions) and Assigned(Node) and (Node <> FRoot) and + (Column > NoColumn) and (coFixed in FHeader.Columns[Column].Options); DoCanSplitterResizeNode(P, Node, Column, Result); end; @@ -18510,7 +18531,7 @@ procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; Run: PVirtualNode; lNewNodeTotalHeight: Cardinal; begin - if (toAutoChangeScale in FOptions.FAutoOptions) then + if (toAutoChangeScale in FOptions.AutoOptions) then begin if (M <> D) then begin @@ -18768,7 +18789,7 @@ procedure TBaseVirtualTree.CreateParams(var Params: TCreateParams); with Params do begin Style := Style or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or ScrollBar[ScrollBarOptions.FScrollBars]; - if toFullRepaintOnResize in FOptions.FMiscOptions then + if toFullRepaintOnResize in FOptions.MiscOptions then WindowClass.style := WindowClass.style or CS_HREDRAW or CS_VREDRAW else WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW); @@ -18807,7 +18828,7 @@ procedure TBaseVirtualTree.CreateWnd; if ((StyleServices.Enabled ) and (toThemeAware in TreeOptions.PaintOptions) ) then begin DoStateChange([tsUseThemes]); - if (toUseExplorerTheme in FOptions.FPaintOptions) and IsWinVistaOrAbove then + if (toUseExplorerTheme in FOptions.PaintOptions) and IsWinVistaOrAbove then begin DoStateChange([tsUseExplorerTheme]); SetWindowTheme('explorer'); @@ -18820,15 +18841,15 @@ procedure TBaseVirtualTree.CreateWnd; // Because of the special recursion and update stopper when creating the window (or resizing it) // we have to manually trigger the auto size calculation here. - if hsNeedScaling in FHeader.FStates then + if hsNeedScaling in FHeader.States then FHeader.RescaleHeader; - if hoAutoResize in FHeader.FOptions then - FHeader.FColumns.AdjustAutoSize(InvalidColumn); + if hoAutoResize in FHeader.Options then + FHeader.Columns.AdjustAutoSize(InvalidColumn); PrepareBitmaps(True, True); // Register tree as OLE drop target. - if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.FMiscOptions) then + if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.MiscOptions) then if not (csLoading in ComponentState) then // will be done in Loaded after all inherited settings are loaded from the DFMs RegisterDragDrop(Handle, DragManager as IDropTarget); @@ -18894,17 +18915,17 @@ function TBaseVirtualTree.DetermineDropMode(const P: TPoint; var HitInfo: THitIn begin ImageHit := HitInfo.HitPositions * [hiOnNormalIcon, hiOnStateIcon] <> []; LabelHit := hiOnItemLabel in HitInfo.HitPositions; - ItemHit := ((hiOnItem in HitInfo.HitPositions) and - ((toFullRowDrag in FOptions.FMiscOptions) or (toFullRowSelect in FOptions.FSelectionOptions))); + ItemHit := (hiOnItem in HitInfo.HitPositions) and ((toFullRowDrag in FOptions.MiscOptions) or + (toFullRowSelect in FOptions.SelectionOptions))); // In report mode only direct hits of the node captions/images in the main column are accepted as hits. - if (toReportMode in FOptions.FMiscOptions) and not (ItemHit or ((LabelHit or ImageHit) and + if (toReportMode in FOptions.MiscOptions) and not (ItemHit or ((LabelHit or ImageHit) and (HitInfo.HitColumn = FHeader.MainColumn))) then HitInfo.HitNode := nil; if Assigned(HitInfo.HitNode) then begin - if LabelHit or ImageHit or not (toShowDropmark in FOptions.FPaintOptions) then + if LabelHit or ImageHit or not (toShowDropmark in FOptions.PaintOptions) then Result := dmOnNode else if ((NodeRect.Top + NodeRect.Bottom) div 2) > P.Y then @@ -18983,7 +19004,7 @@ procedure TBaseVirtualTree.DetermineHitPositionLTR(var HitInfo: THitInfo; Offset begin // Position is to the left of calculated indentation which can only happen for the main column. // Check whether it corresponds to a button/checkbox. - if (toShowButtons in FOptions.FPaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then + if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then begin // Position of button is interpreted very generously to avoid forcing the user // to click exactly into the 9x9 pixels area. The entire node height and one full @@ -19009,7 +19030,7 @@ procedure TBaseVirtualTree.DetermineHitPositionLTR(var HitInfo: THitInfo; Offset // (in this order). // In report mode no hit other than in the main column is possible. - if MainColumnHit or not (toReportMode in FOptions.FMiscOptions) then + if MainColumnHit or not (toReportMode in FOptions.MiscOptions) then begin if MainColumnHit and (Offset < lOffsets[ofsStateImage]) then begin @@ -19092,7 +19113,7 @@ procedure TBaseVirtualTree.DetermineHitPositionRTL(var HitInfo: THitInfo; Offset // If columns are not used or the main column is hit then the tree indentation must be considered too. if MainColumnHit then begin - if toFixedIndent in FOptions.FPaintOptions then + if toFixedIndent in FOptions.PaintOptions then Dec(Right, FIndent) else begin @@ -19102,7 +19123,7 @@ procedure TBaseVirtualTree.DetermineHitPositionRTL(var HitInfo: THitInfo; Offset Dec(Right, FIndent); Run := Run.Parent; end; - if toShowRoot in FOptions.FPaintOptions then + if toShowRoot in FOptions.PaintOptions then Dec(Right, FIndent); end; end; @@ -19111,7 +19132,7 @@ procedure TBaseVirtualTree.DetermineHitPositionRTL(var HitInfo: THitInfo; Offset begin // Position is to the right of calculated indentation which can only happen for the main column. // Check whether it corresponds to a button/checkbox. - if (toShowButtons in FOptions.FPaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then + if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then begin // Position of button is interpreted very generously to avoid forcing the user // to click exactly into the 9x9 pixels area. The entire node height and one full @@ -19137,12 +19158,12 @@ procedure TBaseVirtualTree.DetermineHitPositionRTL(var HitInfo: THitInfo; Offset // (in this order). // In report mode no hit other than in the main column is possible. - if MainColumnHit or not (toReportMode in FOptions.FMiscOptions) then + if MainColumnHit or not (toReportMode in FOptions.MiscOptions) then begin ImageOffset := Right - FMargin; // Check support is only available for the main column. - if MainColumnHit and (toCheckSupport in FOptions.FMiscOptions) and Assigned(FCheckImages) and + if MainColumnHit and (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages) and (HitInfo.HitNode.CheckType <> ctNone) then Dec(ImageOffset, FCheckImages.Width + FImagesMargin); @@ -19227,7 +19248,7 @@ function TBaseVirtualTree.DetermineLineImageAndSelectLevel(Node: PVirtualNode; v begin Result := 0; - if toShowRoot in FOptions.FPaintOptions then + if toShowRoot in FOptions.PaintOptions then X := 1 else X := 0; @@ -19247,10 +19268,10 @@ function TBaseVirtualTree.DetermineLineImageAndSelectLevel(Node: PVirtualNode; v Indent := X - 1; // Only use lines if requested. - if (toShowTreeLines in FOptions.FPaintOptions) and - (not (toHideTreeLinesIfThemed in FOptions.FPaintOptions) or not (tsUseThemes in FStates)) then + if (toShowTreeLines in FOptions.PaintOptions) and + (not (toHideTreeLinesIfThemed in FOptions.PaintOptions) or not (tsUseThemes in FStates)) then begin - if toChildrenAbove in FOptions.FPaintOptions then + if toChildrenAbove in FOptions.PaintOptions then begin Dec(X); if not HasVisiblePreviousSibling(Node) then @@ -19307,8 +19328,8 @@ function TBaseVirtualTree.DetermineLineImageAndSelectLevel(Node: PVirtualNode; v end; // Prepare root level. Run points at this stage to a top level node. - if (toShowRoot in FOptions.FPaintOptions) and ((toShowTreeLines in FOptions.FPaintOptions) and - (not (toHideTreeLinesIfThemed in FOptions.FPaintOptions) or not (tsUseThemes in FStates))) then + if (toShowRoot in FOptions.PaintOptions) and ((toShowTreeLines in FOptions.PaintOptions) and + (not (toHideTreeLinesIfThemed in FOptions.PaintOptions) or not (tsUseThemes in FStates))) then begin // Is the top node a root node? if Run = Node then @@ -19650,7 +19671,7 @@ function TBaseVirtualTree.DoChecking(Node: PVirtualNode; var NewCheckState: TChe // Determines if a node is allowed to change its check state to NewCheckState. begin - if (toReadOnly in FOptions.FMiscOptions) or (vsDisabled in Node.States) then + if (toReadOnly in FOptions.MiscOptions) or (vsDisabled in Node.States) then Result := False else begin @@ -19744,7 +19765,7 @@ procedure TBaseVirtualTree.DoColumnResize(Column: TColumnIndex); begin // Invalidate client area from the current column all to the right (or left in RTL mode). R := ClientRect; - if not (toAutoSpanColumns in FOptions.FAutoOptions) then + if not (toAutoSpanColumns in FOptions.AutoOptions) then if UseRightToLeftAlignment then R.Right := FHeader.Columns[Column].Left + FHeader.Columns[Column].Width + ComputeRTLOffset else @@ -19977,7 +19998,7 @@ procedure TBaseVirtualTree.DoEdit; StopTimer(EditTimer); DoStateChange([], [tsEditPending]); if Assigned(FFocusedNode) and not (vsDisabled in FFocusedNode.States) and - not (toReadOnly in FOptions.FMiscOptions) and (FEditLink = nil) then + not (toReadOnly in FOptions.MiscOptions) and (FEditLink = nil) then begin ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, not (toDisableAutoscrollOnEdit in FOptions.AutoOptions)); FEditLink := DoCreateEditor(FFocusedNode, FEditColumn); @@ -20118,7 +20139,7 @@ procedure TBaseVirtualTree.DoFocusNode(Node: PVirtualNode; Ask: Boolean); begin // Do automatic collapsing of last focused node if enabled. This is however only done if // old and new focused node have a common parent node. - if (toAutoExpand in FOptions.FAutoOptions) and Assigned(Node) and (Node.Parent = FFocusedNode.Parent) and + if (toAutoExpand in FOptions.AutoOptions) and Assigned(Node) and (Node.Parent = FFocusedNode.Parent) and (vsExpanded in FFocusedNode.States) then ToggleNode(FFocusedNode) else @@ -20131,13 +20152,13 @@ procedure TBaseVirtualTree.DoFocusNode(Node: PVirtualNode; Ask: Boolean); if Assigned(FFocusedNode) then begin // Make sure a valid column is set if columns are used and no column has currently the focus. - if FHeader.UseColumns and (not FHeader.FColumns.IsValidColumn(FFocusedColumn)) then + if FHeader.UseColumns and (not FHeader.Columns.IsValidColumn(FFocusedColumn)) then FFocusedColumn := FHeader.MainColumn; // Do automatic expansion of the newly focused node if enabled. - if (toAutoExpand in FOptions.FAutoOptions) and not (vsExpanded in FFocusedNode.States) then + if (toAutoExpand in FOptions.AutoOptions) and not (vsExpanded in FFocusedNode.States) then ToggleNode(FFocusedNode); InvalidateNode(FFocusedNode); - if (FUpdateCount = 0) and not (toDisableAutoscrollOnFocus in FOptions.FAutoOptions) then + if (FUpdateCount = 0) and not (toDisableAutoscrollOnFocus in FOptions.AutoOptions) then ScrollIntoView(FFocusedNode, (toCenterScrollIntoView in FOptions.SelectionOptions) and (MouseButtonDown * FStates = []), not (toFullRowSelect in FOptions.SelectionOptions) ); end; @@ -20824,7 +20845,7 @@ function TBaseVirtualTree.DoSetOffsetXY(Value: TPoint; Options: TScrollUpdateOpt if (suoScrollClientArea in Options) and not (tsToggling in FStates) then begin // Have to invalidate the entire window if there's a background. - if (toShowBackground in FOptions.FPaintOptions) and Assigned(FBackground.Graphic) then + if (toShowBackground in FOptions.PaintOptions) and Assigned(FBackground.Graphic) then begin // Since we don't use ScrollWindow here we have to move all client windows ourselves. DWPStructure := BeginDeferWindowPos(ControlCount); @@ -20865,7 +20886,7 @@ function TBaseVirtualTree.DoSetOffsetXY(Value: TPoint; Options: TScrollUpdateOpt if DeltaX <> 0 then begin UpdateHorizontalScrollBar(suoRepaintScrollBars in Options); - if (suoRepaintHeader in Options) and (hoVisible in FHeader.FOptions) then + if (suoRepaintHeader in Options) and (hoVisible in FHeader.Options) then FHeader.Invalidate(nil); if not (tsSizing in FStates) and (FScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssHorizontal, System.UITypes.TScrollStyle.ssBoth]) then UpdateVerticalScrollBar(suoRepaintScrollBars in Options); @@ -20995,9 +21016,9 @@ procedure TBaseVirtualTree.DoTimerScroll; DeltaY := FLastClickPos.Y - ClientP.Y - 8 else if InRect then - DeltaY := Min(FScrollBarOptions.FIncrementY, ClientHeight) + DeltaY := Min(FScrollBarOptions.VerticalIncrement, ClientHeight) else - DeltaY := Min(FScrollBarOptions.FIncrementY, ClientHeight) * Abs(R.Top - P.Y); + DeltaY := Min(FScrollBarOptions.VerticalIncrement, ClientHeight) * Abs(R.Top - P.Y); if FOffsetY = 0 then Exclude(FScrollDirections, sdUp); end; @@ -21008,9 +21029,9 @@ procedure TBaseVirtualTree.DoTimerScroll; DeltaY := FLastClickPos.Y - ClientP.Y + 8 else if InRect then - DeltaY := -Min(FScrollBarOptions.FIncrementY, ClientHeight) + DeltaY := -Min(FScrollBarOptions.VerticalIncrement, ClientHeight) else - DeltaY := -Min(FScrollBarOptions.FIncrementY, ClientHeight) * Abs(P.Y - R.Bottom); + DeltaY := -Min(FScrollBarOptions.VerticalIncrement, ClientHeight) * Abs(P.Y - R.Bottom); if (ClientHeight - FOffsetY) = Integer(FRangeY) then Exclude(FScrollDirections, sdDown); end; @@ -21021,9 +21042,9 @@ procedure TBaseVirtualTree.DoTimerScroll; DeltaX := FLastClickPos.X - ClientP.X - 8 else if InRect then - DeltaX := FScrollBarOptions.FIncrementX + DeltaX := FScrollBarOptions.HorizontalIncrement else - DeltaX := FScrollBarOptions.FIncrementX * Abs(R.Left - P.X); + DeltaX := FScrollBarOptions.HorizontalIncrement * Abs(R.Left - P.X); if FEffectiveOffsetX = 0 then Exclude(FScrollDirections, sdleft); end; @@ -21034,9 +21055,9 @@ procedure TBaseVirtualTree.DoTimerScroll; DeltaX := FLastClickPos.X - ClientP.X + 8 else if InRect then - DeltaX := -FScrollBarOptions.FIncrementX + DeltaX := -FScrollBarOptions.HorizontalIncrement else - DeltaX := -FScrollBarOptions.FIncrementX * Abs(P.X - R.Right); + DeltaX := -FScrollBarOptions.HorizontalIncrement * Abs(P.X - R.Right); if (ClientWidth + FEffectiveOffsetX) = Integer(FRangeX) then Exclude(FScrollDirections, sdRight); @@ -21196,7 +21217,7 @@ function TBaseVirtualTree.DoValidateCache(): Boolean; // In variable node height mode it might have happend that some or all of the nodes have been adjusted in their // height. During validation updates of the scrollbars is disabled so let's do this here. - if Result and (toVariableNodeHeight in FOptions.FMiscOptions) then + if Result and (toVariableNodeHeight in FOptions.MiscOptions) then begin TThread.Queue(nil, procedure begin UpdateScrollBars(True) end); end; @@ -21333,7 +21354,7 @@ function TBaseVirtualTree.DragEnter(KeyState: Integer; Pt: TPoint; var Effect: I FDropTargetNode := HitInfo.HitNode; R := GetDisplayRect(HitInfo.HitNode, FHeader.MainColumn, False); if (hiOnItemLabel in HitInfo.HitPositions) or ((hiOnItem in HitInfo.HitPositions) and - ((toFullRowDrag in FOptions.FMiscOptions) or (toFullRowSelect in FOptions.FSelectionOptions)))then + ((toFullRowDrag in FOptions.MiscOptions) or (toFullRowSelect in FOptions.SelectionOptions)))then FLastDropMode := dmOnNode else if ((R.Top + R.Bottom) div 2) > Pt.Y then @@ -21454,25 +21475,25 @@ function TBaseVirtualTree.DragOver(Source: TObject; KeyState: Integer; DragState // Determine amount to scroll. if sdUp in FScrollDirections then begin - DeltaY := Min(FScrollBarOptions.FIncrementY, ClientHeight); + DeltaY := Min(FScrollBarOptions.VerticalIncrement, ClientHeight); if FOffsetY = 0 then Exclude(FScrollDirections, sdUp); end; if sdDown in FScrollDirections then begin - DeltaY := -Min(FScrollBarOptions.FIncrementY, ClientHeight); + DeltaY := -Min(FScrollBarOptions.VerticalIncrement, ClientHeight); if (ClientHeight - FOffsetY) = Integer(FRangeY) then Exclude(FScrollDirections, sdDown); end; if sdLeft in FScrollDirections then begin - DeltaX := FScrollBarOptions.FIncrementX; + DeltaX := FScrollBarOptions.HorizontalIncrement; if FEffectiveOffsetX = 0 then Exclude(FScrollDirections, sdleft); end; if sdRight in FScrollDirections then begin - DeltaX := -FScrollBarOptions.FIncrementX; + DeltaX := -FScrollBarOptions.HorizontalIncrement; if (ClientWidth + FEffectiveOffsetX) = Integer(FRangeX) then Exclude(FScrollDirections, sdRight); end; @@ -21563,7 +21584,7 @@ function TBaseVirtualTree.DragOver(Source: TObject; KeyState: Integer; DragState end; // Start auto expand timer if necessary. - if (toAutoDropExpand in FOptions.FAutoOptions) and Assigned(FDropTargetNode) and + if (toAutoDropExpand in FOptions.AutoOptions) and Assigned(FDropTargetNode) and (vsHasChildren in FDropTargetNode.States) then SetTimer(Handle, ExpandTimer, FAutoExpandDelay, nil); end @@ -21638,7 +21659,7 @@ procedure TBaseVirtualTree.DrawDottedVLine(const PaintInfo: TVTPaintInfo; Top, B begin if UseSelectedBkColor then begin - if Focused or (toPopupMode in FOptions.FPaintOptions) then + if Focused or (toPopupMode in FOptions.PaintOptions) then Brush.Color := FColors.FocusedSelectionColor else Brush.Color := FColors.UnfocusedSelectionColor; @@ -22122,13 +22143,13 @@ procedure TBaseVirtualTree.HandleHotTrack(X, Y: Integer); Include(CheckPositions, hiOnItemButtonExact); if (CheckPositions * HitInfo.HitPositions = []) and - (not (toFullRowSelect in FOptions.FSelectionOptions) or (hiNowhere in HitInfo.HitPositions)) then + (not (toFullRowSelect in FOptions.SelectionOptions) or (hiNowhere in HitInfo.HitPositions)) then FCurrentHotNode := nil else FCurrentHotNode := HitInfo.HitNode; if (FCurrentHotNode <> oldHotNode) or (HitInfo.HitColumn <> FCurrentHotColumn) then begin - DoInvalidate := (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.FMiscOptions) or (oldHotNode <> FCurrentHotNode); + DoInvalidate := (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.MiscOptions) or (oldHotNode <> FCurrentHotNode); DoHotChange(oldHotNode, HitInfo.HitNode); if Assigned(oldHotNode) and DoInvalidate then InvalidateNode(oldHotNode); @@ -22421,7 +22442,7 @@ procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitI MayEdit: Boolean; begin - MayEdit := not (tsEditing in FStates) and (toEditOnDblClick in FOptions.FMiscOptions); + MayEdit := not (tsEditing in FStates) and (toEditOnDblClick in FOptions.MiscOptions); if tsEditPending in FStates then begin StopTimer(EditTimer); @@ -22430,7 +22451,7 @@ procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitI if not (tsEditing in FStates) or DoEndEdit then begin - if HitInfo.HitColumn = FHeader.FColumns.FClickIndex then + if HitInfo.HitColumn = FHeader.Columns.FClickIndex then DoColumnDblClick(HitInfo.HitColumn, KeysToShiftState(Message.Keys)); if HitInfo.HitNode <> nil then @@ -22438,7 +22459,7 @@ procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitI Node := nil; if (hiOnItem in HitInfo.HitPositions) and (HitInfo.HitColumn > NoColumn) and - (coFixed in FHeader.FColumns[HitInfo.HitColumn].FOptions) then + (coFixed in FHeader.Columns[HitInfo.HitColumn].Options) then begin if hiUpperSplitter in HitInfo.HitPositions then Node := GetPreviousVisible(HitInfo.HitNode, True) @@ -22447,7 +22468,7 @@ procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitI Node := HitInfo.HitNode; end; - if Assigned(Node) and (Node <> FRoot) and (toNodeHeightDblClickResize in FOptions.FMiscOptions) then + if Assigned(Node) and (Node <> FRoot) and (toNodeHeightDblClickResize in FOptions.MiscOptions) then begin if DoNodeHeightDblClickResize(Node, HitInfo.HitColumn, KeysToShiftState(Message.Keys), Point(Message.XPos, Message.YPos)) then begin @@ -22471,10 +22492,10 @@ procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitI end else begin - if toToggleOnDblClick in FOptions.FMiscOptions then + if toToggleOnDblClick in FOptions.MiscOptions then begin if ((([hiOnItemButton, hiOnItemLabel, hiOnNormalIcon, hiOnStateIcon] * HitInfo.HitPositions) <> []) or - ((toFullRowSelect in FOptions.FSelectionOptions) and Assigned(HitInfo.HitNode))) then + ((toFullRowSelect in FOptions.SelectionOptions) and Assigned(HitInfo.HitNode))) then begin ToggleNode(HitInfo.HitNode); MayEdit := False; @@ -22546,7 +22567,7 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T NextColumn: Integer; Dummy: TColumnIndex; begin - if (not FHeader.UseColumns) or (not (toAutoSpanColumns in FOptions.FAutoOptions)) + if (not FHeader.UseColumns) or (not (toAutoSpanColumns in FOptions.AutoOptions)) or (acolumn = FHeader.MainColumn) then begin //no need to find auto spanned next columns @@ -22554,7 +22575,7 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T exit; end; //invalidate auto spanned columns too - with FHeader.FColumns do //standard loop for auto span + with FHeader.Columns do //standard loop for auto span begin NextColumn := acolumn; repeat @@ -22604,15 +22625,15 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T end; // Keep clicked column in case the application needs it. - FHeader.FColumns.FClickIndex := HitInfo.HitColumn; + FHeader.Columns.FClickIndex := HitInfo.HitColumn; // Change column only if we have hit the node label. if (hiOnItemLabel in HitInfo.HitPositions) or - (toFullRowSelect in FOptions.FSelectionOptions) or - (toGridExtensions in FOptions.FMiscOptions) then + (toFullRowSelect in FOptions.SelectionOptions) or + (toGridExtensions in FOptions.MiscOptions) then begin NewColumn := FFocusedColumn <> HitInfo.HitColumn; - if toExtendedFocus in FOptions.FSelectionOptions then + if toExtendedFocus in FOptions.SelectionOptions then Column := HitInfo.HitColumn else Column := FHeader.MainColumn; @@ -22645,31 +22666,31 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T // Various combinations determine what states the tree enters now. // We initialize shorthand variables to avoid the following expressions getting too large // and to avoid repeative expensive checks. - IsLabelHit := not AltPressed and not (toSimpleDrawSelection in FOptions.FSelectionOptions) and + IsLabelHit := not AltPressed and not (toSimpleDrawSelection in FOptions.SelectionOptions) and ((hiOnItemLabel in HitInfo.HitPositions) or (hiOnNormalIcon in HitInfo.HitPositions)); IsCellHit := not AltPressed and not IsLabelHit and Assigned(HitInfo.HitNode) and ([hiOnItemButton, hiOnItemCheckBox, hiNoWhere] * HitInfo.HitPositions = []) and - ((toFullRowSelect in FOptions.FSelectionOptions) or - ((toGridExtensions in FOptions.FMiscOptions) and (HitInfo.HitColumn > NoColumn))); + ((toFullRowSelect in FOptions.SelectionOptions) or + ((toGridExtensions in FOptions.MiscOptions) and (HitInfo.HitColumn > NoColumn))); IsAnyHit := IsLabelHit or IsCellHit; - MultiSelect := toMultiSelect in FOptions.FSelectionOptions; + MultiSelect := toMultiSelect in FOptions.SelectionOptions; ShiftEmpty := ShiftState = []; NodeSelected := IsAnyHit and (vsSelected in HitInfo.HitNode.States); // Determine the Drag behavior. - if MultiSelect and not (toDisableDrawSelection in FOptions.FSelectionOptions) then + if MultiSelect and not (toDisableDrawSelection in FOptions.SelectionOptions) then begin // We have MultiSelect and want to draw a selection rectangle. // We will start a full row drag only in case a label was hit, // otherwise a multi selection will start. - FullRowDrag := (toFullRowDrag in FOptions.FMiscOptions) and IsCellHit and + FullRowDrag := (toFullRowDrag in FOptions.MiscOptions) and IsCellHit and not (hiNowhere in HitInfo.HitPositions) and (NodeSelected or (hiOnItemLabel in HitInfo.HitPositions) or (hiOnNormalIcon in HitInfo.HitPositions)); end else // No MultiSelect, hence we can start a drag anywhere in the row. - FullRowDrag := toFullRowDrag in FOptions.FMiscOptions; + FullRowDrag := toFullRowDrag in FOptions.MiscOptions; IsHeightTracking := (Message.Msg = WM_LBUTTONDOWN) and (hiOnItem in HitInfo.HitPositions) and @@ -22736,7 +22757,7 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T DoStateChange([tsClearPending]); // User starts a selection with a selection rectangle. - if not (toDisableDrawSelection in FOptions.FSelectionOptions) and not (IsLabelHit or FullRowDrag) and MultiSelect then + if not (toDisableDrawSelection in FOptions.SelectionOptions) and not (IsLabelHit or FullRowDrag) and MultiSelect then begin SetCapture(Handle); DoStateChange([tsDrawSelPending]); @@ -22780,13 +22801,13 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T // pending node edit if Focused and - ((hiOnItemLabel in HitInfo.HitPositions) or ((toGridExtensions in FOptions.FMiscOptions) and + ((hiOnItemLabel in HitInfo.HitPositions) or ((toGridExtensions in FOptions.MiscOptions) and (hiOnItem in HitInfo.HitPositions))) and NodeSelected and not NewColumn and ShiftEmpty and (SelectedCount = 1) then begin DoStateChange([tsEditPending]); end; - if not (toDisableDrawSelection in FOptions.FSelectionOptions) + if not (toDisableDrawSelection in FOptions.SelectionOptions) and not (IsLabelHit or FullRowDrag) and (MultiSelect or (hiNowhere in HitInfo.HitPositions)) then begin // The original code here was moved up to fix issue #187. @@ -22843,7 +22864,7 @@ procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: T if NewNode or NewColumn then begin ScrollIntoView(FFocusedNode, False, - not (toDisableAutoscrollOnFocus in FOptions.FAutoOptions) + not (toDisableAutoscrollOnFocus in FOptions.AutoOptions) and not (toFullRowSelect in FOptions.SelectionOptions)); DoFocusChange(FFocusedNode, FFocusedColumn); @@ -22903,7 +22924,7 @@ procedure TBaseVirtualTree.HandleMouseUp(var Message: TWMMouse; const HitInfo: T tsScrollPending, tsScrolling]); StopTimer(ScrollTimer); - if (FHeader.FColumns.FClickIndex > NoColumn) and (FHeader.FColumns.FClickIndex = HitInfo.HitColumn) then + if (FHeader.Columns.FClickIndex > NoColumn) and (FHeader.Columns.FClickIndex = HitInfo.HitColumn) then DoColumnClick(HitInfo.HitColumn, KeysToShiftState(Message.Keys)); if FLastHitInfo.HitNode <> nil then begin // Use THitInfo of mouse down here, see issue #692 @@ -22919,7 +22940,7 @@ procedure TBaseVirtualTree.HandleMouseUp(var Message: TWMMouse; const HitInfo: T begin // Is the mouse still over the same node? if (HitInfo.HitNode = FFocusedNode) and (hiOnItem in HitInfo.HitPositions) and - (toEditOnClick in FOptions.FMiscOptions) and (FFocusedColumn = HitInfo.HitColumn) and + (toEditOnClick in FOptions.MiscOptions) and (FFocusedColumn = HitInfo.HitColumn) and CanEdit(FFocusedNode, HitInfo.HitColumn) then begin FEditColumn := FFocusedColumn; @@ -23013,7 +23034,7 @@ procedure TBaseVirtualTree.InitNode(Node: PVirtualNode); // Fix: Any parent check state must be propagated here. // Because the CheckType is normally set in DoInitNode // by the App. - if (Node.CheckType = ctTriStateCheckBox) and (toAutoTristateTracking in FOptions.FAutoOptions) then + if (Node.CheckType = ctTriStateCheckBox) and (toAutoTristateTracking in FOptions.AutoOptions) then begin ParentCheckState := Self.GetCheckState(Node.Parent); SelfCheckState := Self.GetCheckState(Node); @@ -23042,7 +23063,7 @@ procedure TBaseVirtualTree.InitNode(Node: PVirtualNode); Include(States, vsFiltered); - if not (toShowFilteredNodes in FOptions.FPaintOptions) and MustAdjustInternalVariables then + if not (toShowFilteredNodes in FOptions.PaintOptions) and MustAdjustInternalVariables then begin AdjustTotalHeight(Node, -NodeHeight, True); if FullyVisible[Node] then @@ -23119,7 +23140,7 @@ procedure TBaseVirtualTree.InternalAddFromStream(Stream: TStream; Version: Integ else Stop := Node.NextSibling; - if toMultiSelect in FOptions.FSelectionOptions then + if toMultiSelect in FOptions.SelectionOptions then begin // Add all nodes which were selected before to the current selection (unless they are already there). while Node <> Stop do @@ -23181,17 +23202,17 @@ function TBaseVirtualTree.InternalAddToSelection(const NewItems: TNodeArray; New begin //Fix: For already selected node when selected, this path //is used that didn't contain the Constraint logic. Added. - Constrained := toLevelSelectConstraint in FOptions.FSelectionOptions; + Constrained := toLevelSelectConstraint in FOptions.SelectionOptions; if Constrained and (FLastSelectionLevel = -1) then FLastSelectionLevel := GetNodeLevelForSelectConstraint(NewItems[0]); AddedNodesSize := NewLength; end else begin - Constrained := toLevelSelectConstraint in FOptions.FSelectionOptions; + Constrained := toLevelSelectConstraint in FOptions.SelectionOptions; if Constrained and (FLastSelectionLevel = -1) then FLastSelectionLevel := GetNodeLevelForSelectConstraint(NewItems[0]); - SiblingConstrained := toSiblingSelectConstraint in FOptions.FSelectionOptions; + SiblingConstrained := toSiblingSelectConstraint in FOptions.SelectionOptions; if SiblingConstrained and (FRangeAnchor = nil) then FRangeAnchor := NewItems[0]; @@ -23640,7 +23661,7 @@ procedure TBaseVirtualTree.Loaded; inherited; // Call RegisterDragDrop after all visual inheritance changes to MiscOptions have been applied. - if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.FMiscOptions) then + if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.MiscOptions) then if HandleAllocated then RegisterDragDrop(Handle, DragManager as IDropTarget); @@ -23649,15 +23670,15 @@ procedure TBaseVirtualTree.Loaded; if (tsNeedRootCountUpdate in FStates) and (FRoot.ChildCount > 0) then begin DoStateChange([], [tsNeedRootCountUpdate]); - IsReadOnly := toReadOnly in FOptions.FMiscOptions; - Exclude(FOptions.FMiscOptions, toReadOnly); + IsReadOnly := toReadOnly in FOptions.MiscOptions; + FOptions.MiscOptions := FOptions.MiscOptions - [toReadOnly]; LastRootCount := FRoot.ChildCount; FRoot.ChildCount := 0; BeginUpdate; SetChildCount(FRoot, LastRootCount); EndUpdate; if IsReadOnly then - Include(FOptions.FMiscOptions, toReadOnly); + FOptions.MiscOptions := FOptions.MiscOptions + [toReadOnly]; end; // Prevent the object inspector at design time from marking the header as being modified @@ -23665,17 +23686,17 @@ procedure TBaseVirtualTree.Loaded; Updating; try FHeader.UpdateMainColumn; - FHeader.FColumns.FixPositions; - if toAutoBidiColumnOrdering in FOptions.FAutoOptions then - FHeader.FColumns.ReorderColumns(UseRightToLeftAlignment); + FHeader.Columns.FixPositions; + if toAutoBidiColumnOrdering in FOptions.AutoOptions then + FHeader.Columns.ReorderColumns(UseRightToLeftAlignment); // Because of the special recursion and update stopper when creating the window (or resizing it) // we have to manually trigger the auto size calculation here. - if hsNeedScaling in FHeader.FStates then + if hsNeedScaling in FHeader.States then FHeader.RescaleHeader else FHeader.RecalculateHeader; - if hoAutoResize in FHeader.FOptions then - FHeader.FColumns.AdjustAutoSize(InvalidColumn, True); + if hoAutoResize in FHeader.Options then + FHeader.Columns.AdjustAutoSize(InvalidColumn, True); finally Updated; end; @@ -23851,7 +23872,7 @@ procedure TBaseVirtualTree.Notification(AComponent: TComponent; Operation: TOper // Check for components linked to the header. if Assigned(FHeader) then begin - if AComponent = FHeader.FImages then + if AComponent = FHeader.Images then FHeader.Images := nil else if AComponent = FHeader.PopupMenu then @@ -24075,7 +24096,7 @@ procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas; const ImageInfo: TVT begin if Selected and not Ghosted then begin - if Focused or (toPopupMode in FOptions.FPaintOptions) then + if Focused or (toPopupMode in FOptions.PaintOptions) then ForegroundColor := ColorToRGB(FColors.FocusedSelectionColor) else ForegroundColor := ColorToRGB(FColors.UnfocusedSelectionColor); @@ -24105,7 +24126,7 @@ procedure TBaseVirtualTree.PaintImage(var PaintInfo: TVTPaintInfo; ImageInfoInde with PaintInfo do begin CutNode := (vsCutOrCopy in Node.States) and (tsCutPending in FStates); - PaintFocused := Focused or (toGhostedIfUnfocused in FOptions.FPaintOptions); + PaintFocused := Focused or (toGhostedIfUnfocused in FOptions.PaintOptions); // Since the overlay image must be specified together with the image to draw // it is meaningfull to retrieve it in advance. @@ -24119,7 +24140,7 @@ procedure TBaseVirtualTree.PaintImage(var PaintInfo: TVTPaintInfo; ImageInfoInde begin if (vsSelected in Node.States) and not(Ghosted or CutNode) then begin - if PaintFocused or (toPopupMode in FOptions.FPaintOptions) then + if PaintFocused or (toPopupMode in FOptions.PaintOptions) then Images.BlendColor := FColors.FocusedSelectionColor else Images.BlendColor := FColors.UnfocusedSelectionColor; @@ -24140,13 +24161,13 @@ procedure TBaseVirtualTree.PaintImage(var PaintInfo: TVTPaintInfo; ImageInfoInde CustomOverlayDrawing := False; // Blend image if enabled and the tree has the focus (or ghosted images must be drawn also if unfocused) ... - if (toUseBlendedImages in FOptions.FPaintOptions) and PaintFocused + if (toUseBlendedImages in FOptions.PaintOptions) and PaintFocused // ... and the image is ghosted... and (Ghosted or // ... or it is not the check image and the node is selected (but selection is not for the entire row)... ((vsSelected in Node.States) and - not (toFullRowSelect in FOptions.FSelectionOptions) and - not (toGridExtensions in FOptions.FMiscOptions)) or + not (toFullRowSelect in FOptions.SelectionOptions) and + not (toGridExtensions in FOptions.MiscOptions)) or // ... or the node must be shown in cut mode. CutNode) then ExtraStyle := ExtraStyle or ILD_BLEND50; @@ -24429,7 +24450,7 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, begin // if the full row selection is disabled or toGridExtensions is in the MiscOptions, draw the selection // into the InnerRect, otherwise into the RowRect - if not (toFullRowSelect in FOptions.FSelectionOptions) or (toGridExtensions in FOptions.FMiscOptions) then + if not (toFullRowSelect in FOptions.SelectionOptions) or (toGridExtensions in FOptions.MiscOptions) then DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, TVP_TREEITEM, State, InnerRect, nil) else DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, TVP_TREEITEM, State, RowRect, nil); @@ -24440,7 +24461,7 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, Theme: HTHEME; begin Theme := OpenThemeData(Application.ActiveFormHandle, 'Explorer::ItemsView'); - if not (toFullRowSelect in FOptions.FSelectionOptions) or (toGridExtensions in FOptions.FMiscOptions) then + if not (toFullRowSelect in FOptions.SelectionOptions) or (toGridExtensions in FOptions.MiscOptions) then DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, LVP_LISTDETAIL, State, InnerRect, nil) else DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, LVP_LISTDETAIL, State, RowRect, nil); @@ -24463,7 +24484,7 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, with PaintInfo, Canvas do begin // Fill cell background if its color differs from tree background. - with FHeader.FColumns do + with FHeader.Columns do if poColumnColor in PaintOptions then begin Brush.Color := Items[Column].GetEffectiveColor; @@ -24476,7 +24497,7 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, InnerRect := ContentRect; // The selection rectangle depends on alignment. - if not (toGridExtensions in FOptions.FMiscOptions) then + if not (toGridExtensions in FOptions.MiscOptions) then begin case Alignment of taLeftJustify: @@ -24497,7 +24518,7 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, end; end; - if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.FSelectionOptions) then + if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.SelectionOptions) then begin // Fill the selection rectangle. if poDrawSelection in PaintOptions then @@ -24509,8 +24530,8 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, Brush.Color := FColors.DropTargetColor; Pen.Color := FColors.DropTargetBorderColor; - if (toGridExtensions in FOptions.FMiscOptions) or - (toFullRowSelect in FOptions.FSelectionOptions) then + if (toGridExtensions in FOptions.MiscOptions) or + (toFullRowSelect in FOptions.SelectionOptions) then InnerRect := CellRect; if not IsRectEmpty(InnerRect) then if tsUseExplorerTheme in FStates then @@ -24530,7 +24551,7 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, else if vsSelected in Node.States then begin - if Focused or (toPopupMode in FOptions.FPaintOptions) then + if Focused or (toPopupMode in FOptions.PaintOptions) then begin Brush.Color := FColors.FocusedSelectionColor; Pen.Color := FColors.FocusedSelectionBorderColor; @@ -24540,14 +24561,14 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, Brush.Color := FColors.UnfocusedSelectionColor; Pen.Color := FColors.UnfocusedSelectionBorderColor; end; - if (toGridExtensions in FOptions.FMiscOptions) or (toFullRowSelect in FOptions.FSelectionOptions) then + if (toGridExtensions in FOptions.MiscOptions) or (toFullRowSelect in FOptions.SelectionOptions) then InnerRect := CellRect; if not IsRectEmpty(InnerRect) then if tsUseExplorerTheme in FStates then begin // If the node is also hot, its background will be drawn later. - if not (toHotTrack in FOptions.FPaintOptions) or (Node <> FCurrentHotNode) or - ((Column <> FCurrentHotColumn) and not (toFullRowSelect in FOptions.FSelectionOptions)) then + if not (toHotTrack in FOptions.PaintOptions) or (Node <> FCurrentHotNode) or + ((Column <> FCurrentHotColumn) and not (toFullRowSelect in FOptions.SelectionOptions)) then DrawBackground(IfThen(Self.Focused, TREIS_SELECTED, TREIS_SELECTEDNOTFOCUS)); end else @@ -24560,19 +24581,19 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, end; end; - if (tsUseExplorerTheme in FStates) and (toHotTrack in FOptions.FPaintOptions) and (Node = FCurrentHotNode) and - ((Column = FCurrentHotColumn) or (toFullRowSelect in FOptions.FSelectionOptions)) then - DrawBackground(IfThen((vsSelected in Node.States) and not (toAlwaysHideSelection in FOptions.FPaintOptions), + if (tsUseExplorerTheme in FStates) and (toHotTrack in FOptions.PaintOptions) and (Node = FCurrentHotNode) and + ((Column = FCurrentHotColumn) or (toFullRowSelect in FOptions.SelectionOptions)) then + DrawBackground(IfThen((vsSelected in Node.States) and not (toAlwaysHideSelection in FOptions.PaintOptions), TREIS_HOTSELECTED, TREIS_HOT)); - if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.FSelectionOptions) then + if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.SelectionOptions) then begin // draw focus rect if (poDrawFocusRect in PaintOptions) and - (Focused or (toPopupMode in FOptions.FPaintOptions)) and (FFocusedNode = Node) and + (Focused or (toPopupMode in FOptions.PaintOptions)) and (FFocusedNode = Node) and ( (Column = FFocusedColumn) or - ((not (toExtendedFocus in FOptions.FSelectionOptions) or IsWinVistaOrAbove) and - (toFullRowSelect in FOptions.FSelectionOptions) and + ((not (toExtendedFocus in FOptions.SelectionOptions) or IsWinVistaOrAbove) and + (toFullRowSelect in FOptions.SelectionOptions) and (tsUseExplorerTheme in FStates) ) ) then begin TextColorBackup := GetTextColor(Handle); @@ -24580,11 +24601,11 @@ procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, BackColorBackup := GetBkColor(Handle); SetBkColor(Handle, 0); - if not (toExtendedFocus in FOptions.FSelectionOptions) and (toFullRowSelect in FOptions.FSelectionOptions) and + if not (toExtendedFocus in FOptions.SelectionOptions) and (toFullRowSelect in FOptions.SelectionOptions) and (tsUseExplorerTheme in FStates) then FocusRect := RowRect else - if toGridExtensions in FOptions.FMiscOptions then + if toGridExtensions in FOptions.MiscOptions then FocusRect := CellRect else FocusRect := InnerRect; @@ -25549,10 +25570,10 @@ procedure TBaseVirtualTree.UpdateHeaderRect(); InflateRect(FHeaderRect, -OffsetX, -OffsetY); - if hoVisible in FHeader.FOptions then + if hoVisible in FHeader.Options then begin if FHeaderRect.Left <= FHeaderRect.Right then - FHeaderRect.Bottom := FHeaderRect.Top + Integer(FHeader.FHeight) + FHeaderRect.Bottom := FHeaderRect.Top + Integer(FHeader.Height) else FHeaderRect := Rect(0, 0, 0, 0); end @@ -25584,10 +25605,10 @@ procedure TBaseVirtualTree.UpdateEditBounds; end; if vsMultiline in FFocusedNode.States then R := GetDisplayRect(FFocusedNode, FEditColumn, True, False) - else if not (toGridExtensions in FOptions.FMiscOptions) then + else if not (toGridExtensions in FOptions.MiscOptions) then R := GetDisplayRect(FFocusedNode, FEditColumn, True, True); - if (toGridExtensions in FOptions.FMiscOptions) then + if (toGridExtensions in FOptions.MiscOptions) then begin // Use the whole cell when grid extensions are on. R := GetDisplayRect(FFocusedNode, FEditColumn, False, False); @@ -25610,8 +25631,8 @@ procedure TBaseVirtualTree.UpdateEditBounds; end else begin - CurrentAlignment := FHeader.Columns[FEditColumn].FAlignment; - CurrentBidiMode := FHeader.Columns[FEditColumn].FBiDiMode; + CurrentAlignment := FHeader.Columns[FEditColumn].Alignment; + CurrentBidiMode := FHeader.Columns[FEditColumn].BiDiMode; end; // Consider bidi mode here. In RTL context does left alignment actually mean right alignment and vice versa. if CurrentBidiMode <> bdLeftToRight then @@ -25787,7 +25808,7 @@ procedure TBaseVirtualTree.WndProc(var Message: TMessage); Handled := False; // Try the header whether it needs to take this message. - if Assigned(FHeader) and (FHeader.FStates <> []) then + if Assigned(FHeader) and (FHeader.States <> []) then Handled := FHeader.HandleMessage(Message); if not Handled then begin @@ -25901,7 +25922,7 @@ procedure TBaseVirtualTree.WriteNode(Stream: TStream; Node: PVirtualNode); begin // Initialize the node first if necessary and wanted. - if toInitOnSave in FOptions.FMiscOptions then + if toInitOnSave in FOptions.MiscOptions then begin if not (vsInitialized in Node.States) then InitNode(Node); @@ -25961,7 +25982,7 @@ function TBaseVirtualTree.AddChild(Parent: PVirtualNode; UserData: Pointer = nil // against the virtual paradigm and hence I dissuade from its usage. begin - if not (toReadOnly in FOptions.FMiscOptions) then + if not (toReadOnly in FOptions.MiscOptions) then Result := InsertNode(Parent, TVTNodeAttachMode.amAddChildLast, UserData) else Result := nil; @@ -25993,7 +26014,7 @@ procedure TBaseVirtualTree.AddFromStream(Stream: TStream; TargetNode: PVirtualNo Node: PVirtualNode; begin - if not (toReadOnly in FOptions.FMiscOptions) then + if not (toReadOnly in FOptions.MiscOptions) then begin // check first whether this is a stream we can read Stream.ReadBuffer(ThisID, SizeOf(TMagicID)); @@ -26051,7 +26072,7 @@ procedure TBaseVirtualTree.AfterConstruction; procedure TBaseVirtualTree.Assign(Source: TPersistent); begin - if (Source is TBaseVirtualTree) and not (toReadOnly in FOptions.FMiscOptions) then + if (Source is TBaseVirtualTree) and not (toReadOnly in FOptions.MiscOptions) then with Source as TBaseVirtualTree do begin Self.Align := Align; @@ -26275,7 +26296,7 @@ function TBaseVirtualTree.CanEdit(Node: PVirtualNode; Column: TColumnIndex): Boo // Returns True if the given node can be edited. begin - Result := (toEditable in FOptions.FMiscOptions) and Enabled and not (toReadOnly in FOptions.FMiscOptions) + Result := (toEditable in FOptions.MiscOptions) and Enabled and not (toReadOnly in FOptions.MiscOptions) and ((Column < 0) or (coEditable in FHeader.Columns[Column].Options)); DoCanEdit(Node, Column, Result); end; @@ -26302,7 +26323,7 @@ function TBaseVirtualTree.CanFocus: Boolean; procedure TBaseVirtualTree.Clear; begin - if (not IsEmpty and not (toReadOnly in FOptions.FMiscOptions)) or (csDestroying in ComponentState) then + if (not IsEmpty and not (toReadOnly in FOptions.MiscOptions)) or (csDestroying in ComponentState) then begin BeginUpdate; try @@ -26360,6 +26381,13 @@ procedure TBaseVirtualTree.ClearSelection(); //---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.ClearDragManager; +begin + Pointer(FDragManager) := nil; +end; + +//---------------------------------------------------------------------------------------------------------------------- + procedure TBaseVirtualTree.ClearSelection(pFireChangeEvent: Boolean); var @@ -26446,7 +26474,7 @@ function TBaseVirtualTree.CopyTo(Source, Target: PVirtualNode; Mode: TVTNodeAtta else TargetTree := TreeFromNode(Target); - if not (toReadOnly in TargetTree.FOptions.FMiscOptions) then + if not (toReadOnly in TargetTree.TreeOptions.MiscOptions) then begin if Target = TargetTree.FRoot then begin @@ -26537,7 +26565,7 @@ procedure TBaseVirtualTree.CopyToClipboard; procedure TBaseVirtualTree.CutToClipboard; begin - if (FSelectionCount > 0) and not (toReadOnly in FOptions.FMiscOptions) then + if (FSelectionCount > 0) and not (toReadOnly in FOptions.MiscOptions) then begin if OleSetClipboard(TVTDataObject.Create(Self, True)) = S_OK then begin @@ -26563,7 +26591,7 @@ procedure TBaseVirtualTree.DeleteChildren(Node: PVirtualNode; ResetHasChildren: ParentVisible: Boolean; begin - if Assigned(Node) and (Node.ChildCount > 0) and not (toReadOnly in FOptions.FMiscOptions) then + if Assigned(Node) and (Node.ChildCount > 0) and not (toReadOnly in FOptions.MiscOptions) then begin Assert(not (tsIterating in FStates), 'Deleting nodes during tree iteration leads to invalid pointers.'); @@ -26657,7 +26685,7 @@ procedure TBaseVirtualTree.DeleteNode(Node: PVirtualNode; Reindex: Boolean; Pare WasInSynchMode: Boolean; begin - if Assigned(Node) and (Node <> FRoot) and not (toReadOnly in FOptions.FMiscOptions) then + if Assigned(Node) and (Node <> FRoot) and not (toReadOnly in FOptions.MiscOptions) then begin Assert(not (tsIterating in FStates), 'Deleting nodes during tree iteration leads to invalid pointers.'); @@ -26767,7 +26795,7 @@ procedure TBaseVirtualTree.DeleteSelectedNodes; lNodes: TNodeArray; begin lNodes := nil; - if (FSelectionCount > 0) and not (toReadOnly in FOptions.FMiscOptions) then + if (FSelectionCount > 0) and not (toReadOnly in FOptions.MiscOptions) then begin lNodes := GetSortedSelection(True); DeleteNodes(lNodes); @@ -26797,7 +26825,7 @@ function TBaseVirtualTree.EditNode(Node: PVirtualNode; Column: TColumnIndex): Bo Result := tsEditing in FStates; // If the tree is already editing then we don't disrupt this. - if not Result and not (toReadOnly in FOptions.FMiscOptions) then + if not Result and not (toReadOnly in FOptions.MiscOptions) then begin FocusedNode := Node; if Assigned(FFocusedNode) and (Node = FFocusedNode) and CanEdit(FFocusedNode, Column) then @@ -26889,8 +26917,8 @@ procedure TBaseVirtualTree.EndUpdate; if tsChangePending in FStates then DoChange(FLastChangedNode); finally - if toAutoSort in FOptions.FAutoOptions then - SortTree(FHeader.FSortColumn, FHeader.FSortDirection, True); + if toAutoSort in FOptions.AutoOptions then + SortTree(FHeader.SortColumn, FHeader.SortDirection, True); SetUpdateState(False); if HandleAllocated then @@ -26928,7 +26956,7 @@ function TBaseVirtualTree.ExecuteAction(Action: TBasicAction): Boolean; if Result then CopyToClipboard else - if not (toReadOnly in FOptions.FMiscOptions) then + if not (toReadOnly in FOptions.MiscOptions) then begin Result := Action is TEditCut; if Result then @@ -27171,7 +27199,7 @@ function TBaseVirtualTree.GetDisplayRect(Node: PVirtualNode; Column: TColumnInde // Limit left and right bounds to the given column (if any) and move bounds according to current scroll state. if Column > NoColumn then begin - FHeader.FColumns.GetColumnBounds(Column, Result.Left, Result.Right); + FHeader.Columns.GetColumnBounds(Column, Result.Left, Result.Right); // The right column border is not part of this cell. Dec(Result.Right); OffsetRect(Result, 0, FOffsetY); @@ -27190,8 +27218,8 @@ function TBaseVirtualTree.GetDisplayRect(Node: PVirtualNode; Column: TColumnInde end else begin - CurrentBidiMode := FHeader.FColumns[Column].BidiMode; - CurrentAlignment := FHeader.FColumns[Column].Alignment; + CurrentBidiMode := FHeader.Columns[Column].BidiMode; + CurrentAlignment := FHeader.Columns[Column].Alignment; end; GetOffsets(Node, lOffsets, TVTElement.ofsLabel, Column); @@ -27275,7 +27303,7 @@ function TBaseVirtualTree.GetEffectivelyFiltered(Node: PVirtualNode): Boolean; begin if Assigned(Node) then - Result := (vsFiltered in Node.States) and not (toShowFilteredNodes in FOptions.FPaintOptions) + Result := (vsFiltered in Node.States) and not (toShowFilteredNodes in FOptions.PaintOptions) else Result := False; end; @@ -27295,7 +27323,7 @@ function TBaseVirtualTree.GetFirst(ConsiderChildrenAbove: Boolean = False): PVir // Returns the first node in the tree while optionally considering toChildrenAbove. begin - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin if vsHasChildren in FRoot.States then begin @@ -27443,7 +27471,7 @@ function TBaseVirtualTree.GetFirstNoInit(ConsiderChildrenAbove: Boolean = False) // No initialization is performed. begin - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin if vsHasChildren in FRoot.States then begin @@ -27498,7 +27526,7 @@ function TBaseVirtualTree.GetFirstVisible(Node: PVirtualNode = nil; ConsiderChil begin Result := GetFirstChild(Result); - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin repeat // Search the first visible sibling. @@ -27624,7 +27652,7 @@ function TBaseVirtualTree.GetFirstVisibleNoInit(Node: PVirtualNode = nil; begin Result := Result.FirstChild; - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin repeat // Search the first visible sibling. @@ -27747,24 +27775,24 @@ procedure TBaseVirtualTree.GetHitTestInfoAt(X, Y: Integer; Relative: Boolean; va begin HitInfo.HitColumn := FHeader.Columns.GetColumnAndBounds(Point(X, Y), ColLeft, ColRight, False); // If auto column spanning is enabled then look for the last non empty column. - if toAutoSpanColumns in FOptions.FAutoOptions then + if toAutoSpanColumns in FOptions.AutoOptions then begin InitialColumn := HitInfo.HitColumn; // Search to the left of the hit column for empty columns. while (HitInfo.HitColumn > NoColumn) and ColumnIsEmpty(HitInfo.HitNode, HitInfo.HitColumn) do begin - NextColumn := FHeader.FColumns.GetPreviousVisibleColumn(HitInfo.HitColumn); + NextColumn := FHeader.Columns.GetPreviousVisibleColumn(HitInfo.HitColumn); if NextColumn = InvalidColumn then Break; HitInfo.HitColumn := NextColumn; - Dec(ColLeft, FHeader.FColumns[NextColumn].Width); + Dec(ColLeft, FHeader.Columns[NextColumn].Width); end; // Search to the right of the hit column for empty columns. repeat - InitialColumn := FHeader.FColumns.GetNextVisibleColumn(InitialColumn); + InitialColumn := FHeader.Columns.GetNextVisibleColumn(InitialColumn); if (InitialColumn = InvalidColumn) or not ColumnIsEmpty(HitInfo.HitNode, InitialColumn) then Break; - Inc(ColRight, FHeader.FColumns[InitialColumn].Width); + Inc(ColRight, FHeader.Columns[InitialColumn].Width); until False; end; // Make the X position and the right border relative to the start of the column. @@ -27786,7 +27814,7 @@ procedure TBaseVirtualTree.GetHitTestInfoAt(X, Y: Integer; Relative: Boolean; va HitInfo.HitPositions := [hiOnItem]; // Avoid getting the display rect if this is not necessary. - if toNodeHeightResize in FOptions.FMiscOptions then + if toNodeHeightResize in FOptions.MiscOptions then begin NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, False); if Y <= (NodeRect.Top - FOffsetY + 1) then @@ -27803,8 +27831,8 @@ procedure TBaseVirtualTree.GetHitTestInfoAt(X, Y: Integer; Relative: Boolean; va end else begin - CurrentBidiMode := FHeader.FColumns[HitInfo.HitColumn].BidiMode; - CurrentAlignment := FHeader.FColumns[HitInfo.HitColumn].Alignment; + CurrentBidiMode := FHeader.Columns[HitInfo.HitColumn].BidiMode; + CurrentAlignment := FHeader.Columns[HitInfo.HitColumn].Alignment; end; if CurrentBidiMode = bdLeftToRight then @@ -27828,7 +27856,7 @@ function TBaseVirtualTree.GetLast(Node: PVirtualNode = nil; ConsiderChildrenAbov begin Result := GetLastChild(Node); - if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.FPaintOptions) then + if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then while Assigned(Result) do begin // Test if there is a next last child. If not keep the node from the last run. @@ -27864,7 +27892,7 @@ function TBaseVirtualTree.GetLastNoInit(Node: PVirtualNode = nil; ConsiderChildr begin Result := GetLastChildNoInit(Node); - if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.FPaintOptions) then + if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then while Assigned(Result) do begin // Test if there is a next last child. If not keep the node from the last run. @@ -27999,7 +28027,7 @@ function TBaseVirtualTree.GetLastVisibleNoInit(Node: PVirtualNode = nil; begin Result := GetLastVisibleChildNoInit(Node, IncludeFiltered); - if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.FPaintOptions) then + if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then while Assigned(Result) and (vsExpanded in Result.States) do begin // Test if there is a next last child. If not keep the node from the last run. @@ -28034,7 +28062,7 @@ function TBaseVirtualTree.GetMaxColumnWidth(Column: TColumnIndex; UseSmartColumn if OperationCanceled then begin // Behave non-destructive. - Result := FHeader.FColumns[Column].Width; + Result := FHeader.Columns[Column].Width; Exit; end else @@ -28057,7 +28085,7 @@ function TBaseVirtualTree.GetMaxColumnWidth(Column: TColumnIndex; UseSmartColumn LastNode := nil; if hoAutoResizeInclCaption in FHeader.Options then - Result := Result + (2 * Header.Columns[Column].FMargin + Header.Columns[Column].CaptionWidth + 2); + Result := Result + (2 * Header.Columns[Column].Margin + Header.Columns[Column].CaptionWidth + 2); while Assigned(Run) and not OperationCanceled do begin @@ -28088,7 +28116,7 @@ function TBaseVirtualTree.GetMaxColumnWidth(Column: TColumnIndex; UseSmartColumn Break; Run := NextNode; end; - if toShowVertGridLines in FOptions.FPaintOptions then + if toShowVertGridLines in FOptions.PaintOptions then Inc(Result); if Assigned(FOnAfterGetMaxColumnWidth) then @@ -28111,7 +28139,7 @@ function TBaseVirtualTree.GetNext(Node: PVirtualNode; ConsiderChildrenAbove: Boo begin Assert(Result <> FRoot, 'Node must not be the hidden root node.'); - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin // If this node has no siblings use the parent. if not Assigned(Result.NextSibling) then @@ -28324,7 +28352,7 @@ function TBaseVirtualTree.GetNextNoInit(Node: PVirtualNode; ConsiderChildrenAbov begin Assert(Result <> FRoot, 'Node must not be the hidden root node.'); - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin // If this node has no siblings use the parent. if not Assigned(Result.NextSibling) then @@ -28458,7 +28486,7 @@ function TBaseVirtualTree.GetNextVisible(Node: PVirtualNode; ConsiderChildrenAbo if not FullyVisible[Result] then Result := GetVisibleParent(Result, True); - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin repeat // If there a no siblings anymore, go up one level. @@ -28570,7 +28598,7 @@ function TBaseVirtualTree.GetNextVisibleNoInit(Node: PVirtualNode; ConsiderChild Assert(Result <> FRoot, 'Node must not be the hidden root node.'); repeat - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin repeat // If there a no siblings anymore, go up one level. @@ -28877,7 +28905,7 @@ function TBaseVirtualTree.GetPrevious(Node: PVirtualNode; ConsiderChildrenAbove: begin Assert(Result <> FRoot, 'Node must not be the hidden root node.'); - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin // Has this node got children? Initialize them if necessary. if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then @@ -29083,7 +29111,7 @@ function TBaseVirtualTree.GetPreviousNoInit(Node: PVirtualNode; ConsiderChildren begin Assert(Result <> FRoot, 'Node must not be the hidden root node.'); - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin // If there is a last child, take it; if not try the previous sibling. if Assigned(Result.LastChild) then @@ -29216,7 +29244,7 @@ function TBaseVirtualTree.GetPreviousVisible(Node: PVirtualNode; ConsiderChildre end else begin - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin repeat if Assigned(Result.LastChild) and (vsExpanded in Result.States) then @@ -29325,7 +29353,7 @@ function TBaseVirtualTree.GetPreviousVisibleNoInit(Node: PVirtualNode; end else begin - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.FPaintOptions) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin repeat // Is the current node expanded and has children? @@ -29855,8 +29883,8 @@ function TBaseVirtualTree.InsertNode(Node: PVirtualNode; Mode: TVTNodeAttachMode // Note: Node can never be FRoot at this point. StructureChange(Result, crNodeAdded); // If auto sort is enabled then sort the node or its parent (depending on the insert mode). - if (toAutoSort in FOptions.FAutoOptions) and (FHeader.FSortColumn > InvalidColumn) then - Sort(Node.Parent, FHeader.FSortColumn, FHeader.FSortDirection, True); + if (toAutoSort in FOptions.AutoOptions) and (FHeader.SortColumn > InvalidColumn) then + Sort(Node.Parent, FHeader.SortColumn, FHeader.SortDirection, True); InvalidateToBottom(Result) end; amAddChildFirst, @@ -29864,8 +29892,8 @@ function TBaseVirtualTree.InsertNode(Node: PVirtualNode; Mode: TVTNodeAttachMode begin StructureChange(Node, crChildAdded); // If auto sort is enabled then sort the node or its parent (depending on the insert mode). - if (toAutoSort in FOptions.FAutoOptions) and (FHeader.FSortColumn > InvalidColumn) then - Sort(Node, FHeader.FSortColumn, FHeader.FSortDirection, True); + if (toAutoSort in FOptions.AutoOptions) and (FHeader.SortColumn > InvalidColumn) then + Sort(Node, FHeader.SortColumn, FHeader.SortDirection, True); InvalidateToBottom(Node); end; end; @@ -29921,7 +29949,7 @@ procedure TBaseVirtualTree.InvalidateColumn(Column: TColumnIndex); R: TRect; begin - if (FUpdateCount = 0) and HandleAllocated and FHeader.FColumns.IsValidColumn(Column) then + if (FUpdateCount = 0) and HandleAllocated and FHeader.Columns.IsValidColumn(Column) then begin R := ClientRect; FHeader.Columns.GetColumnBounds(Column, R.Left, R.Right); @@ -29970,7 +29998,7 @@ procedure TBaseVirtualTree.InvalidateToBottom(Node: PVirtualNode); R := GetDisplayRect(Node, NoColumn, False); if R.Top < ClientHeight then begin - if (toChildrenAbove in FOptions.FPaintOptions) and (vsExpanded in Node.States) then + if (toChildrenAbove in FOptions.PaintOptions) and (vsExpanded in Node.States) then Dec(R.Top, Node.TotalHeight + NodeHeight[Node]); R.Bottom := ClientHeight; InvalidateRect(Handle, @R, False); @@ -29993,7 +30021,7 @@ procedure TBaseVirtualTree.InvertSelection(VisibleOnly: Boolean); TriggerChange: Boolean; begin - if not FSelectionLocked and (toMultiSelect in FOptions.FSelectionOptions) then + if not FSelectionLocked and (toMultiSelect in FOptions.SelectionOptions) then begin Run := FRoot.FirstChild; ClearTempCache; @@ -30192,7 +30220,7 @@ procedure TBaseVirtualTree.LoadFromStream(Stream: TStream); Node: PVirtualNode; begin - if not (toReadOnly in FOptions.FMiscOptions) then + if not (toReadOnly in FOptions.MiscOptions) then begin Clear; // Check first whether this is a stream we can read. @@ -30249,7 +30277,7 @@ procedure TBaseVirtualTree.MeasureItemHeight(const Canvas: TCanvas; Node: PVirtu if not (vsHeightMeasured in Node.States) then begin Include(Node.States, vsHeightMeasured); - if (toVariableNodeHeight in FOptions.FMiscOptions) then + if (toVariableNodeHeight in FOptions.MiscOptions) then begin NewNodeHeight := Node.NodeHeight; // Anonymous methods help to make this thread safe easily. @@ -30303,7 +30331,7 @@ procedure TBaseVirtualTree.MoveTo(Source, Target: PVirtualNode; Mode: TVTNodeAtt Allowed := (Source <> Target) or ((Mode in [amInsertBefore, amInsertAfter]) and ChildrenOnly); if Allowed and (Mode <> amNoWhere) and Assigned(Source) and (Source <> FRoot) and - not (toReadOnly in FOptions.FMiscOptions) then + not (toReadOnly in FOptions.MiscOptions) then begin // Assume that an empty destination means the root in this (the source) tree. if Target = nil then @@ -30571,16 +30599,16 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe R := Rect(0, 0, Max(FRangeX, ClientWidth), 0); // For quick checks some intermediate variables are used. - UseBackground := (toShowBackground in FOptions.FPaintOptions) and Assigned(FBackground.Graphic) and + UseBackground := (toShowBackground in FOptions.PaintOptions) and Assigned(FBackground.Graphic) and (poBackground in PaintOptions); - ShowCheckImages := Assigned(FCheckImages) and (toCheckSupport in FOptions.FMiscOptions); + ShowCheckImages := Assigned(FCheckImages) and (toCheckSupport in FOptions.MiscOptions); UseColumns := FHeader.UseColumns; // Adjust paint options to tree settings. Hide selection if told so or the tree is unfocused. - if (toAlwaysHideSelection in FOptions.FPaintOptions) or - (not Focused and (toHideSelection in FOptions.FPaintOptions)) then + if (toAlwaysHideSelection in FOptions.PaintOptions) or + (not Focused and (toHideSelection in FOptions.PaintOptions)) then Exclude(PaintOptions, poDrawSelection); - if toHideFocusRect in FOptions.FPaintOptions then + if toHideFocusRect in FOptions.PaintOptions then Exclude(PaintOptions, poDrawFocusRect); // Determine node to start drawing with. @@ -30619,7 +30647,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe // Initialize node if not already done. if not (vsInitialized in PaintInfo.Node.States) then InitNode(PaintInfo.Node); - if (vsSelected in PaintInfo.Node.States) and not (toChildrenAbove in FOptions.FPaintOptions) then + if (vsSelected in PaintInfo.Node.States) and not (toChildrenAbove in FOptions.PaintOptions) then Inc(SelectLevel); // Ensure the node's height is determined. @@ -30680,7 +30708,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe InitializeFirstColumnValues(PaintInfo); // Now go through all visible columns (there's still one run if columns aren't used). - with FHeader.FColumns do + with FHeader.Columns do begin while ((PaintInfo.Column > InvalidColumn) or not UseColumns) and (PaintInfo.CellRect.Left < Window.Right) do @@ -30690,8 +30718,8 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe PaintInfo.Column := FPositionToIndex[PaintInfo.Position]; if FirstColumn = InvalidColumn then FirstColumn := PaintInfo.Column; - PaintInfo.BidiMode := Items[PaintInfo.Column].FBiDiMode; - PaintInfo.Alignment := Items[PaintInfo.Column].FAlignment; + PaintInfo.BidiMode := Items[PaintInfo.Column].BiDiMode; + PaintInfo.Alignment := Items[PaintInfo.Column].Alignment; end else begin @@ -30708,7 +30736,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe ((Column = FEditColumn) or not UseColumns) then Exclude(PaintOptions, poDrawSelection); if not UseColumns or - ((vsSelected in Node.States) and (toFullRowSelect in FOptions.FSelectionOptions) and + ((vsSelected in Node.States) and (toFullRowSelect in FOptions.SelectionOptions) and (poDrawSelection in PaintOptions)) or (coParentColor in Items[PaintInfo.Column].Options) or ((coStyleColor in Items[PaintInfo.Column].Options) and VclStyleEnabled) @@ -30723,7 +30751,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe // Paint the current cell if it is marked as being visible or columns aren't used and // if this cell belongs to the main column if only the main column should be drawn. - if (not UseColumns or (coVisible in Items[PaintInfo.Column].FOptions)) and + if (not UseColumns or (coVisible in Items[PaintInfo.Column].Options)) and (not (poMainOnly in PaintOptions) or IsMainColumn) then begin AdjustPaintCellRect(PaintInfo, NextColumn); @@ -30764,19 +30792,19 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe end; // Paint the horizontal grid line. - if (poGridLines in PaintOptions) and (toShowHorzGridLines in FOptions.FPaintOptions) then + if (poGridLines in PaintOptions) and (toShowHorzGridLines in FOptions.PaintOptions) then begin Canvas.Font.Color := FColors.GridLineColor; if IsMainColumn and (FLineMode = lmBands) then begin if BidiMode = bdLeftToRight then begin - DrawDottedHLine(PaintInfo, CellRect.Left + IfThen(toFixedIndent in FOptions.FPaintOptions, 1, IndentSize) * Integer(FIndent), CellRect.Right - 1, + DrawDottedHLine(PaintInfo, CellRect.Left + IfThen(toFixedIndent in FOptions.PaintOptions, 1, IndentSize) * Integer(FIndent), CellRect.Right - 1, CellRect.Bottom - 1); end else begin - DrawDottedHLine(PaintInfo, CellRect.Left, CellRect.Right - IfThen(toFixedIndent in FOptions.FPaintOptions, 1, IndentSize) * Integer(FIndent) - 1, + DrawDottedHLine(PaintInfo, CellRect.Left, CellRect.Right - IfThen(toFixedIndent in FOptions.PaintOptions, 1, IndentSize) * Integer(FIndent) - 1, CellRect.Bottom - 1); end; end @@ -30790,16 +30818,16 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe if UseColumns then begin // Paint vertical grid line. - if (poGridLines in PaintOptions) and (toShowVertGridLines in FOptions.FPaintOptions) then + if (poGridLines in PaintOptions) and (toShowVertGridLines in FOptions.PaintOptions) then begin // These variables and the nested if conditions shall make the logic // easier to understand. CellIsTouchingClientRight := PaintInfo.CellRect.Right = ClientRect.Right; CellIsInLastColumn := Position = TColumnPosition(Count - 1); - ColumnIsFixed := coFixed in FHeader.FColumns[Column].Options; + ColumnIsFixed := coFixed in FHeader.Columns[Column].Options; // Don't draw if this is the last column and the header is in autosize mode. - if not ((hoAutoResize in FHeader.FOptions) and CellIsInLastColumn) then + if not ((hoAutoResize in FHeader.Options) and CellIsInLastColumn) then begin // We have to take spanned cells into account which we determine // by checking if CellRect.Right equals the Window.Right. @@ -30812,7 +30840,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe if (BidiMode = bdLeftToRight) or not ColumnIsEmpty(Node, Column) then begin Canvas.Font.Color := FColors.GridLineColor; - lUseSelectedBkColor := (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.FSelectionOptions) and + lUseSelectedBkColor := (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.SelectionOptions) and (vsSelected in Node.States) and not (toUseBlendedSelection in FOptions.PaintOptions) and not (tsUseExplorerTheme in FStates); DrawDottedVLine(PaintInfo, CellRect.Top, CellRect.Bottom, CellRect.Right - 1, lUseSelectedBkColor); @@ -30831,14 +30859,14 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe // Some parts are only drawn for the main column. if IsMainColumn then begin - if (toShowTreeLines in FOptions.FPaintOptions) and - (not (toHideTreeLinesIfThemed in FOptions.FPaintOptions) or + if (toShowTreeLines in FOptions.PaintOptions) and + (not (toHideTreeLinesIfThemed in FOptions.PaintOptions) or not (tsUseThemes in FStates)) then - PaintTreeLines(PaintInfo, IfThen(toFixedIndent in FOptions.FPaintOptions, 1, + PaintTreeLines(PaintInfo, IfThen(toFixedIndent in FOptions.PaintOptions, 1, IndentSize), LineImage); // Show node button if allowed, if there child nodes and at least one of the child // nodes is visible or auto button hiding is disabled. - if (toShowButtons in FOptions.FPaintOptions) and (vsHasChildren in Node.States) and + if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in Node.States) and not ((vsAllChildrenHidden in Node.States) and (toAutoHideButtons in TreeOptions.FAutoOptions)) and ((toShowRoot in TreeOptions.PaintOptions) or (GetNodeLevel(Node) > 0)) @@ -30881,7 +30909,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe PaintInfo.Position := Items[NextColumn].Position; // Move clip rectangle and continue. - if coVisible in Items[NextColumn].FOptions then + if coVisible in Items[NextColumn].Options then with PaintInfo do begin Items[NextColumn].GetAbsoluteBounds(CellRect.Left, CellRect.Right); @@ -30897,7 +30925,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe DoAfterItemPaint(Canvas, Node, R); // Final touch for this node: mark it if it is the current drop target node. - if (Node = FDropTargetNode) and (toShowDropmark in FOptions.FPaintOptions) and + if (Node = FDropTargetNode) and (toShowDropmark in FOptions.PaintOptions) and (poDrawDropMark in PaintOptions) then DoPaintDropMark(Canvas, Node, R); end; @@ -30966,7 +30994,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe SetCanvasOrigin(PaintInfo.Canvas, Target.X, 0); // This line caused issue #313 when it was placed above the if-statement if UseColumns then begin - with FHeader.FColumns do + with FHeader.Columns do begin // If there is no content in the tree then the first column has not yet been determined. if FirstColumn = InvalidColumn then @@ -30976,7 +31004,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe if FirstColumn <> InvalidColumn then begin R.Left := Items[FirstColumn].Left; - R.Right := R.Left + Items[FirstColumn].FWidth; + R.Right := R.Left + Items[FirstColumn].Width; if R.Right > TargetRect.Left then Break; FirstColumn := GetNextVisibleColumn(FirstColumn); @@ -30986,7 +31014,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe else begin R.Left := Items[FirstColumn].Left; - R.Right := R.Left + Items[FirstColumn].FWidth; + R.Right := R.Left + Items[FirstColumn].Width; end; // Initialize MaxRight. @@ -30997,7 +31025,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe begin // Determine left and right coordinate of the current column ColLeft := Items[FirstColumn].Left; - ColRight := (ColLeft + Items[FirstColumn].FWidth); + ColRight := (ColLeft + Items[FirstColumn].Width); // Check wether this column needs to be painted at all. if (ColRight >= MaxRight) then @@ -31007,16 +31035,16 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe MaxRight := ColRight; // And record were to start the next column. if (poGridLines in PaintOptions) and - (toFullVertGridLines in FOptions.FPaintOptions) and - (toShowVertGridLines in FOptions.FPaintOptions) and - (not (hoAutoResize in FHeader.FOptions) or (Cardinal(FirstColumn) < TColumnPosition(Count - 1))) then + (toFullVertGridLines in FOptions.PaintOptions) and + (toShowVertGridLines in FOptions.PaintOptions) and + (not (hoAutoResize in FHeader.Options) or (Cardinal(FirstColumn) < TColumnPosition(Count - 1))) then begin DrawDottedVLine(PaintInfo, R.Top, R.Bottom, R.Right - 1); Dec(R.Right); end; - if not (coParentColor in Items[FirstColumn].FOptions) then - PaintInfo.Canvas.Brush.Color := Items[FirstColumn].FColor + if not (coParentColor in Items[FirstColumn].Options) then + PaintInfo.Canvas.Brush.Color := Items[FirstColumn].Color else PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor; PaintInfo.Canvas.FillRect(R); @@ -31031,8 +31059,8 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe R.Right := TargetRect.Right + Target.X; // Prevent erasing the last vertical grid line. if (poGridLines in PaintOptions) and - (toFullVertGridLines in FOptions.FPaintOptions) and (toShowVertGridLines in FOptions.FPaintOptions) and - (not (hoAutoResize in FHeader.FOptions)) then + (toFullVertGridLines in FOptions.PaintOptions) and (toShowVertGridLines in FOptions.PaintOptions) and + (not (hoAutoResize in FHeader.Options)) then Inc(R.Left); PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor; PaintInfo.Canvas.FillRect(R); @@ -31107,7 +31135,7 @@ function TBaseVirtualTree.PasteFromClipboard: Boolean; begin Result := False; - if not (toReadOnly in FOptions.FMiscOptions) then + if not (toReadOnly in FOptions.MiscOptions) then begin if OleGetClipboard(Data) <> S_OK then ShowError(SClipboardFailed, hcTFClipboardFailed) @@ -31278,7 +31306,7 @@ procedure TBaseVirtualTree.Print(Printer: TPrinter; PrintHeader: Boolean); LogFont.lfQuality := ANTIALIASED_QUALITY; FHeader.Font.Handle := CreateFontIndirect(LogFont); ImgRect.Bottom := FHeader.Height; - FHeader.FColumns.PaintHeader(Image.Canvas.Handle, ImgRect, 0); + FHeader.Columns.PaintHeader(Image.Canvas.Handle, ImgRect, 0); FHeader.Font := SaveHeaderFont; finally SaveHeaderFont.Free; @@ -31493,7 +31521,7 @@ function TBaseVirtualTree.ProcessOLEData(Source: TBaseVirtualTree; const DataObj cfFormat := CF_VIRTUALTREE; end; Result := DataObject.QueryGetData(StandardOLEFormat) = S_OK; - if Result and not (toReadOnly in FOptions.FMiscOptions) then + if Result and not (toReadOnly in FOptions.MiscOptions) then begin BeginUpdate; Result := False; @@ -31783,10 +31811,10 @@ function TBaseVirtualTree.ScrollIntoView(Node: PVirtualNode; Center: Boolean; Ho Run := Run.Parent; end; UseColumns := FHeader.UseColumns; - if UseColumns and FHeader.FColumns.IsValidColumn(FFocusedColumn) then - R := GetDisplayRect(Node, FFocusedColumn, not (toGridExtensions in FOptions.FMiscOptions)) + if UseColumns and FHeader.Columns.IsValidColumn(FFocusedColumn) then + R := GetDisplayRect(Node, FFocusedColumn, not (toGridExtensions in FOptions.MiscOptions)) else - R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.FMiscOptions)); + R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.MiscOptions)); // The returned rectangle can never be empty after the expand code above. // 1) scroll vertically @@ -31852,7 +31880,7 @@ function TBaseVirtualTree.ScrollIntoView(Column: TColumnIndex; Center: Boolean; ColumnRight := ColumnLeft + Header.Columns.Items[Column].Width; end else if Assigned(Node) and (toCenterScrollIntoView in FOptions.SelectionOptions) then begin Center := False; - R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.FMiscOptions)); + R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.MiscOptions)); ColumnLeft := R.Left; ColumnRight := R.Right; end else @@ -31900,7 +31928,7 @@ procedure TBaseVirtualTree.SelectAll(VisibleOnly: Boolean); Run: PVirtualNode; NextFunction: TGetNextNodeProc; begin - if not FSelectionLocked and (toMultiSelect in FOptions.FSelectionOptions) then + if not FSelectionLocked and (toMultiSelect in FOptions.SelectionOptions) then begin ClearTempCache; if VisibleOnly then @@ -32232,7 +32260,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); Window := Handle; DC := GetDC(Handle); - if (toShowBackground in FOptions.FPaintOptions) and Assigned(FBackground.Graphic) then + if (toShowBackground in FOptions.PaintOptions) and Assigned(FBackground.Graphic) then Self.Brush.Style := bsClear else begin @@ -32301,7 +32329,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); // Calculate the height delta right now as we need it for toChildrenAbove anyway. HeightDelta := -Integer(Node.TotalHeight) + Integer(NodeHeight[Node]); - if (FUpdateCount = 0) and (toAnimatedToggle in FOptions.FAnimationOptions) and not + if (FUpdateCount = 0) and (toAnimatedToggle in FOptions.AnimationOptions) and not (tsCollapsing in FStates) then begin if tsHint in Self.FStates then @@ -32315,7 +32343,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); // on the position of the node to be collapsed. R1 := GetDisplayRect(Node, NoColumn, False); Mode2 := tamNoScroll; - if toChildrenAbove in FOptions.FPaintOptions then + if toChildrenAbove in FOptions.PaintOptions then begin PosHoldable := (FOffsetY + (Integer(Node.TotalHeight) - Integer(NodeHeight[Node]))) <= 0; NodeInView := R1.Top < ClientHeight; @@ -32323,7 +32351,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); StepsR1 := 0; if NodeInView then begin - if PosHoldable or not (toAdvancedAnimatedToggle in FOptions.FAnimationOptions) then + if PosHoldable or not (toAdvancedAnimatedToggle in FOptions.AnimationOptions) then begin // Scroll the child nodes down. Mode1 := tamScrollDown; @@ -32345,7 +32373,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); begin if (Integer(FRangeY) + FOffsetY - R1.Bottom + HeightDelta >= ClientHeight - R1.Bottom) or (Integer(FRangeY) <= ClientHeight) or (FOffsetY = 0) or not - (toAdvancedAnimatedToggle in FOptions.FAnimationOptions) then + (toAdvancedAnimatedToggle in FOptions.AnimationOptions) then begin // Do a simple scroll up over the child nodes. Mode1 := tamScrollUp; @@ -32386,7 +32414,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); DoCollapsed(Node); // Remove child nodes now, if enabled. - if (toAutoFreeOnCollapse in FOptions.FAutoOptions) and (Node.ChildCount > 0) then + if (toAutoFreeOnCollapse in FOptions.AutoOptions) and (Node.ChildCount > 0) then begin DeleteChildren(Node); Include(Node.States, vsHasChildren); @@ -32420,7 +32448,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); until Child = nil; // Getting the display rectangle is already done here as it is needed for toChildrenAbove in any case. - if (toChildrenAbove in FOptions.FPaintOptions) or (FUpdateCount = 0) then + if (toChildrenAbove in FOptions.PaintOptions) or (FUpdateCount = 0) then begin with ToggleData do begin @@ -32428,7 +32456,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); Mode2 := tamNoScroll; TotalFit := HeightDelta + Integer(NodeHeight[Node]) <= ClientHeight; - if toChildrenAbove in FOptions.FPaintOptions then + if toChildrenAbove in FOptions.PaintOptions then begin // The main goal with toChildrenAbove being set is to keep the nodes visual position so the user does // not get confused. Therefore we need to scroll the view when the expanding is done. @@ -32450,7 +32478,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); begin // Do animated expanding if enabled. if (ToggleData.R1.Top < ClientHeight) and ([tsPainting, tsExpanding] * FStates = []) and - (toAnimatedToggle in FOptions.FAnimationOptions)then + (toAnimatedToggle in FOptions.AnimationOptions)then begin if tsHint in Self.FStates then Application.CancelHint; @@ -32458,12 +32486,12 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); // animated expanding with ToggleData do begin - if toChildrenAbove in FOptions.FPaintOptions then + if toChildrenAbove in FOptions.PaintOptions then begin // At first check if we hold the position, which is the most common case. - if not (toAdvancedAnimatedToggle in FOptions.FAnimationOptions) or + if not (toAdvancedAnimatedToggle in FOptions.AnimationOptions) or (PosHoldable and ( (NodeInView and ChildrenInView) or not - (toAutoScrollOnExpand in FOptions.FAutoOptions) )) then + (toAutoScrollOnExpand in FOptions.AutoOptions) )) then begin Mode1 := tamScrollUp; R1 := Rect(R1.Left, 0, R1.Right, R1.Top); @@ -32475,7 +32503,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); Mode1 := tamScrollDown; Mode2 := tamScrollUp; R2 := Rect(R1.Left, 0, R1.Right, R1.Top); - if not (toAutoScrollOnExpand in FOptions.FAutoOptions) then + if not (toAutoScrollOnExpand in FOptions.AutoOptions) then begin // If we shall not or cannot scroll to the desired extent we calculate the new position (with // max FOffsetY applied) and animate it that way. @@ -32526,8 +32554,8 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); else begin // toChildrenAbove is not set. - if (PosHoldable and ChildrenInView) or not (toAutoScrollOnExpand in FOptions.FAutoOptions) or not - (toAdvancedAnimatedToggle in FOptions.FAnimationOptions) or (R1.Top <= 0) then + if (PosHoldable and ChildrenInView) or not (toAutoScrollOnExpand in FOptions.AutoOptions) or not + (toAdvancedAnimatedToggle in FOptions.AnimationOptions) or (R1.Top <= 0) then begin // If the node will stay at its visual position, do a simple down-scroll. Mode1 := tamScrollDown; @@ -32560,8 +32588,8 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); end; end; end; - if toAutoSort in FOptions.FAutoOptions then - Sort(Node, FHeader.FSortColumn, FHeader.FSortDirection, False); + if toAutoSort in FOptions.AutoOptions then + Sort(Node, FHeader.SortColumn, FHeader.SortDirection, False); end;// if UpdateCount = 0 Include(Node.States, vsExpanded); @@ -32585,14 +32613,14 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); UpdateScrollBars(True); if [tsPainting, tsExpanding] * FStates = [] then begin - if (vsExpanded in Node.States) and ((toAutoScrollOnExpand in FOptions.FAutoOptions) or - (toChildrenAbove in FOptions.FPaintOptions)) then + if (vsExpanded in Node.States) and ((toAutoScrollOnExpand in FOptions.AutoOptions) or + (toChildrenAbove in FOptions.PaintOptions)) then begin - if toChildrenAbove in FOptions.FPaintOptions then + if toChildrenAbove in FOptions.PaintOptions then begin NeedFullInvalidate := True; if (PosHoldable and ChildrenInView and NodeInView) or not - (toAutoScrollOnExpand in FOptions.FAutoOptions) then + (toAutoScrollOnExpand in FOptions.AutoOptions) then SetOffsetY(FOffsetY - Integer(HeightDelta)) else if TotalFit and NodeInView then @@ -32620,7 +32648,7 @@ procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); begin // If we have collapsed the node or toAutoScrollOnExpand is not set, we try to keep the nodes // visual position. - if toChildrenAbove in FOptions.FPaintOptions then + if toChildrenAbove in FOptions.PaintOptions then SetOffsetY(FOffsetY - Integer(HeightDelta)); NeedFullInvalidate := True; end; @@ -32658,7 +32686,7 @@ procedure TBaseVirtualTree.UpdateHorizontalRange; begin if FHeader.UseColumns then - SetRangeX(FHeader.FColumns.TotalWidth) + SetRangeX(FHeader.Columns.TotalWidth) else SetRangeX(GetMaxRightExtend); end; @@ -32956,6 +32984,19 @@ constructor TVTEdit.Create(Link: TStringEditLink); //---------------------------------------------------------------------------------------------------------------------- +procedure TVTEdit.ClearLink; +begin + FLink := nil +end; + +//---------------------------------------------------------------------------------------------------------------------- +procedure TVTEdit.ClearRefLink; +begin + FRefLink := nil +end; + +//---------------------------------------------------------------------------------------------------------------------- + function TVTEdit.CalcMinHeight: Integer; var textHeight : Integer; @@ -32981,7 +33022,7 @@ procedure TVTEdit.CMAutoAdjust(var Message: TMessage); procedure TVTEdit.CMExit(var Message: TMessage); begin - if Assigned(FLink) and not FLink.FStopping then + if Assigned(FLink) and not FLink.Stopping then with FLink, FTree do begin if (toAutoAcceptEditChange in TreeOptions.StringOptions) then @@ -33004,8 +33045,8 @@ procedure TVTEdit.CMRelease(var Message: TMessage); procedure TVTEdit.CNCommand(var Message: TWMCommand); begin - if Assigned(FLink) and Assigned(FLink.FTree) and (Message.NotifyCode = EN_UPDATE) and - not (vsMultiline in FLink.FNode.States) then + if Assigned(FLink) and Assigned(FLink.Tree) and (Message.NotifyCode = EN_UPDATE) and + not (vsMultiline in FLink.Node.States) then // Instead directly calling AutoAdjustSize it is necessary on Win9x/Me to decouple this notification message // and eventual resizing. Hence we use a message to accomplish that. AutoAdjustSize() @@ -33029,7 +33070,7 @@ procedure TVTEdit.WMDestroy(var Message: TWMDestroy); begin // If editing stopped by other means than accept or cancel then we have to do default processing for // pending changes. - if Assigned(FLink) and not FLink.FStopping and not (csRecreating in Self.ControlState) then + if Assigned(FLink) and not FLink.Stopping and not (csRecreating in Self.ControlState) then begin with FLink, FTree do begin @@ -33068,7 +33109,7 @@ procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown); EditOptions: TVTEditOptions; Column: TVirtualTreeColumn; begin - Tree := FLink.FTree; + Tree := FLink.Tree; case Message.CharCode of VK_ESCAPE: begin @@ -33076,7 +33117,7 @@ procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown); end; VK_RETURN: begin - EndEdit := not (vsMultiline in FLink.FNode.States); + EndEdit := not (vsMultiline in FLink.Node.States); if not EndEdit then begin // If a multiline node is being edited the finish editing only if Ctrl+Enter was pressed, @@ -33086,10 +33127,10 @@ procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown); end; if EndEdit then begin - Tree := FLink.FTree; - FLink.FTree.InvalidateNode(FLink.FNode); - NextNode := Tree.GetNextVisible(FLink.FNode, True); - FLink.FTree.DoEndEdit; + Tree := FLink.Tree; + FLink.Tree.InvalidateNode(FLink.Node); + NextNode := Tree.GetNextVisible(FLink.Node, True); + FLink.Tree.DoEndEdit; // get edit options for column as priority. If column has toDefaultEdit // use global edit options for tree @@ -33152,13 +33193,13 @@ procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown); end; VK_UP: begin - if not (vsMultiline in FLink.FNode.States) then + if not (vsMultiline in FLink.Node.States) then Message.CharCode := VK_LEFT; inherited; end; VK_DOWN: begin - if not (vsMultiline in FLink.FNode.States) then + if not (vsMultiline in FLink.Node.States) then Message.CharCode := VK_RIGHT; inherited; end; @@ -33166,11 +33207,11 @@ procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown); begin if Tree.IsEditing then begin - Tree.InvalidateNode(FLink.FNode); + Tree.InvalidateNode(FLink.Node); if ssShift in KeyDataToShiftState(Message.KeyData) then - NextNode := Tree.GetPreviousVisible(FLink.FNode, True) // Shift+Tab goes to previous mode + NextNode := Tree.GetPreviousVisible(FLink.Node, True) // Shift+Tab goes to previous mode else - NextNode := Tree.GetNextVisible(FLink.FNode, True); + NextNode := Tree.GetNextVisible(FLink.Node, True); Tree.EndEditNode; // check NextNode, otherwise we got AV if NextNode <> nil then @@ -33206,18 +33247,18 @@ procedure TVTEdit.AutoAdjustSize; var Size: TSize; begin - if not (vsMultiline in FLink.FNode.States) and not (toGridExtensions in FLink.FTree.FOptions.FMiscOptions{see issue #252}) then + if not (vsMultiline in FLink.Node.States) and not (toGridExtensions in FLink.Tree.TreeOptions.MiscOptions{see issue #252}) then begin // avoid flicker SendMessage(Handle, WM_SETREDRAW, 0, 0); try Size := GetTextSize; - Inc(Size.cx, 2 * FLink.FTree.FTextMargin); + Inc(Size.cx, 2 * FLink.Tree.FTextMargin); // Repaint associated node if the edit becomes smaller. if Size.cx < Width then - FLink.FTree.Invalidate(); + FLink.Tree.Invalidate(); - if FLink.FAlignment = taRightJustify then + if FLink.Alignment = taRightJustify then FLink.SetBounds(Rect(Left + Width - Size.cx, Top, Left + Width, Top + Max(Size.cy, Height))) else FLink.SetBounds(Rect(Left, Top, Left + Size.cx, Top + Max(Size.cy, Height))); @@ -33233,7 +33274,7 @@ procedure TVTEdit.CreateParams(var Params: TCreateParams); begin inherited; - if not Assigned(FLink.FNode) then + if not Assigned(FLink.Node) then exit; // Prevent AV exceptions occasionally seen in code below // Only with multiline style we can use the text formatting rectangle. @@ -33241,9 +33282,9 @@ procedure TVTEdit.CreateParams(var Params: TCreateParams); with Params do begin Style := Style or ES_MULTILINE; - if vsMultiline in FLink.FNode.States then + if vsMultiline in FLink.Node.States then Style := Style and not (ES_AUTOHSCROLL or WS_HSCROLL) or WS_VSCROLL or ES_AUTOVSCROLL; - if tsUseThemes in FLink.FTree.FStates then + if tsUseThemes in FLink.Tree.FStates then begin Style := Style and not WS_BORDER; ExStyle := ExStyle or WS_EX_CLIENTEDGE; @@ -33276,7 +33317,7 @@ function TVTEdit.GetTextSize: TSize; procedure TVTEdit.KeyPress(var Key: Char); begin - if (Key = #13) and Assigned(FLink) and not (vsMultiline in FLink.FNode.States) then + if (Key = #13) and Assigned(FLink) and not (vsMultiline in FLink.Node.States) then Key := #0; // Filter out return keys as they will be added to the text, avoids #895 inherited; end; @@ -33354,8 +33395,8 @@ function TStringEditLink.CancelEdit: Boolean; FStopping := True; FEdit.Hide; FTree.CancelEditNode; - FEdit.FLink := nil; - FEdit.FRefLink := nil; + FEdit.ClearLink; + FEdit.ClearRefLink; end; end; @@ -33371,8 +33412,8 @@ function TStringEditLink.EndEdit: Boolean; if FEdit.Modified then FTree.Text[FNode, FColumn] := FEdit.Text; FEdit.Hide; - FEdit.FLink := nil; - FEdit.FRefLink := nil; + FEdit.ClearLink; + FEdit.ClearRefLink; except FStopping := False; raise; @@ -33509,9 +33550,9 @@ procedure TStringEditLink.SetBounds(R: TRect); end; lOffset := IfThen(vsMultiline in FNode.States, 0, 2); - if tsUseThemes in FTree.FStates then + if tsUseThemes in FTree.TreeStates then Inc(lOffset); - InflateRect(R, -FTree.FTextMargin + lOffset, lOffset); + InflateRect(R, -FTree.TextMargin + lOffset, lOffset); if not (vsMultiline in FNode.States) then begin tOffset := FTextBounds.Top - FEdit.Top; @@ -33689,7 +33730,7 @@ procedure TCustomVirtualStringTree.InitializeTextProperties(var PaintInfo: TVTPa else Canvas.Font.Color := FColors.DisabledColor; - if (toHotTrack in FOptions.FPaintOptions) and (Node = FCurrentHotNode) then + if (toHotTrack in FOptions.PaintOptions) and (Node = FCurrentHotNode) then begin if not (tsUseExplorerTheme in FStates) then begin @@ -33701,12 +33742,12 @@ procedure TCustomVirtualStringTree.InitializeTextProperties(var PaintInfo: TVTPa // Change the font color only if the node also is drawn in selected style. if poDrawSelection in PaintOptions then begin - if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.FSelectionOptions) then + if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.SelectionOptions) then begin if Node = FDropTargetNode then begin if ((FLastDropMode = dmOnNode) or (vsSelected in Node.States)) then - Canvas.Font.Color := FColors.GetSelectedNodeFontColor(Focused or (toPopupMode in FOptions.FPaintOptions)); + Canvas.Font.Color := FColors.GetSelectedNodeFontColor(Focused or (toPopupMode in FOptions.PaintOptions)); end else if vsSelected in Node.States then @@ -33818,7 +33859,7 @@ procedure TCustomVirtualStringTree.PaintStaticText(const PaintInfo: TVTPaintInfo with PaintInfo do begin Canvas.Font := Font; - if toFullRowSelect in FOptions.FSelectionOptions then + if toFullRowSelect in FOptions.SelectionOptions then begin if Node = FDropTargetNode then begin @@ -33830,7 +33871,7 @@ procedure TCustomVirtualStringTree.PaintStaticText(const PaintInfo: TVTPaintInfo else if vsSelected in Node.States then begin - if Focused or (toPopupMode in FOptions.FPaintOptions) then + if Focused or (toPopupMode in FOptions.PaintOptions) then Canvas.Font.Color := FColors.GetSelectedNodeFontColor(Focused) else Canvas.Font.Color := FColors.NodeFontColor; @@ -34002,8 +34043,8 @@ procedure TCustomVirtualStringTree.AdjustPaintCellRect(var PaintInfo: TVTPaintIn // Note: the autospan feature can only be used with left-to-right layout. begin - if (toAutoSpanColumns in FOptions.FAutoOptions) and FHeader.UseColumns and (PaintInfo.BidiMode = bdLeftToRight) then - with FHeader.FColumns, PaintInfo do + if (toAutoSpanColumns in FOptions.AutoOptions) and FHeader.UseColumns and (PaintInfo.BidiMode = bdLeftToRight) then + with FHeader.Columns, PaintInfo do begin // Start with the directly following column. NextNonEmpty := GetNextVisibleColumn(Column); From 9dcb53abf5e24b798610fabcd09a11619ce01b08 Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Fri, 30 Apr 2021 20:22:35 +1000 Subject: [PATCH 207/681] re-included previous PR --- Source/VirtualTrees.pas | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 0fef7fddd..88de02063 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1046,6 +1046,7 @@ TVirtualTreeColumn = class(TCollectionItem) procedure SetWidth(Value: TDimension); protected FLeft: TDimension; + procedure ChangeScale(M, D: TDimension; isDpiChange: Boolean); virtual; procedure ComputeHeaderLayout(var PaintInfo: THeaderPaintInfo; DrawFormat: Cardinal; CalculateTextRect: Boolean = False); procedure DefineProperties(Filer: TFiler); override; procedure GetAbsoluteBounds(var Left, Right: TDimension); @@ -7100,6 +7101,13 @@ procedure TVirtualTreeColumn.SetWidth(Value: Integer); //---------------------------------------------------------------------------------------------------------------------- +procedure TVirtualTreeColumn.ChangeScale(M, D: TDimension; isDpiChange: Boolean); +begin + FMinWidth := MulDiv(FMinWidth, M, D); + FMaxWidth := MulDiv(FMaxWidth, M, D); + FSpacing := MulDiv(FSpacing, M, D); + Self.Width := MulDiv(Self.Width, M, D); +end; procedure TVirtualTreeColumn.ComputeHeaderLayout(var PaintInfo: THeaderPaintInfo; DrawFormat: Cardinal; CalculateTextRect: Boolean = False); @@ -10019,9 +10027,7 @@ procedure TVTHeader.ChangeScale(M, D: Integer; isDpiChange: Boolean); Font.Height := MulDiv(Font.Height, M, D); // Scale the columns widths too for I := 0 to FColumns.Count - 1 do - begin - Self.FColumns[I].Width := MulDiv(Self.FColumns[I].Width, M, D); - end;//for I + Self.FColumns[I].ChangeScale(M, D, isDpiChange); if not isDpiChange then AutoScale(); end; From 9c75f132f206d9f3855eee8f0b59353b46986b99 Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Fri, 30 Apr 2021 22:59:08 +1000 Subject: [PATCH 208/681] SetMiscOptions has side effects that were bypassed before. --- Source/VirtualTrees.pas | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 88de02063..147b2fe6e 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -640,6 +640,8 @@ TCustomVirtualTreeOptions = class(TPersistent) protected // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) function StyleServices(AControl: TControl = nil): TCustomStyleServices; + //these bypass the side effects in the regular setters. + procedure InternalSetMiscOptions(const Value: TVTMiscOptions); public constructor Create(AOwner: TBaseVirtualTree); virtual; procedure AssignTo(Dest: TPersistent); override; @@ -4552,6 +4554,13 @@ procedure TCustomVirtualTreeOptions.SetAutoOptions(const Value: TVTAutoOptions); //---------------------------------------------------------------------------------------------------------------------- +procedure TCustomVirtualTreeOptions.InternalSetMiscOptions(const Value: TVTMiscOptions); +begin + FMiscOptions := value; +end; + +//---------------------------------------------------------------------------------------------------------------------- + procedure TCustomVirtualTreeOptions.SetMiscOptions(const Value: TVTMiscOptions); var @@ -12239,7 +12248,7 @@ destructor TBaseVirtualTree.Destroy; CheckSynchronize(); CheckSynchronize(); end;// if - FOptions.MiscOptions := FOptions.MiscOptions - [toReadOnly]; + FOptions.InternalSetMiscOptions(FOptions.FMiscOptions - [toReadOnly]); //SetMiscOptions has side effects // Make sure there is no reference remaining to the releasing tree. TWorkerThread.ReleaseThreadReference(); StopWheelPanning; @@ -23677,14 +23686,14 @@ procedure TBaseVirtualTree.Loaded; begin DoStateChange([], [tsNeedRootCountUpdate]); IsReadOnly := toReadOnly in FOptions.MiscOptions; - FOptions.MiscOptions := FOptions.MiscOptions - [toReadOnly]; + FOptions.InternalSetMiscOptions(FOptions.MiscOptions - [toReadOnly]); LastRootCount := FRoot.ChildCount; FRoot.ChildCount := 0; BeginUpdate; SetChildCount(FRoot, LastRootCount); EndUpdate; if IsReadOnly then - FOptions.MiscOptions := FOptions.MiscOptions + [toReadOnly]; + FOptions.InternalSetMiscOptions(FOptions.MiscOptions + [toReadOnly]); end; // Prevent the object inspector at design time from marking the header as being modified From 14268ee45ddc6e1a55127012b24f5e2275bb3351 Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Fri, 30 Apr 2021 23:07:21 +1000 Subject: [PATCH 209/681] Column.SetWidth has side effects that needed to be bypassed. --- Source/VirtualTrees.pas | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 147b2fe6e..5f0e1f089 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -1056,6 +1056,7 @@ TVirtualTreeColumn = class(TCollectionItem) function GetText: string; virtual; // [IPK] procedure SetText(const Value: string); virtual; // [IPK] private to protected & virtual function GetOwner: TVirtualTreeColumns; reintroduce; + procedure InternalSetWidth(const value : TDimension); //bypass side effects in SetWidth procedure ReadHint(Reader: TReader); procedure ReadText(Reader: TReader); procedure SetCollection(Value: TCollection); override; @@ -7511,6 +7512,13 @@ function TVirtualTreeColumn.GetOwner: TVirtualTreeColumns; //---------------------------------------------------------------------------------------------------------------------- +procedure TVirtualTreeColumn.InternalSetWidth(const value : TDimension); +begin + FWidth := value; +end + +//---------------------------------------------------------------------------------------------------------------------- + procedure TVirtualTreeColumn.ReadText(Reader: TReader); begin @@ -11095,7 +11103,7 @@ procedure TVTHeader.RescaleHeader; while I > NoColumn do begin if (coFixed in FColumns[I].Options) and (FColumns[I].Width < FColumns[I].MinWidth) then - FColumns[I].FWidth := FColumns[I].MinWidth; //TODO : SetWidth has side effects and this bypasses them, what to do here? + FColumns[I].InternalSetWidth(FColumns[I].MinWidth); //SetWidth has side effects and this bypasses them I := GetNextVisibleColumn(I); end; FixedWidth := GetVisibleFixedWidth; From 5f46fc80c0565b6ddd0f91b0b56ebbf0bba710d6 Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Fri, 30 Apr 2021 23:08:18 +1000 Subject: [PATCH 210/681] oops, missed a semicolon --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 5f0e1f089..3af29706a 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -7515,7 +7515,7 @@ function TVirtualTreeColumn.GetOwner: TVirtualTreeColumns; procedure TVirtualTreeColumn.InternalSetWidth(const value : TDimension); begin FWidth := value; -end +end; //---------------------------------------------------------------------------------------------------------------------- From f83a7a6a87ade58ab2d26084abc77c770a613dfc Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Sat, 1 May 2021 10:54:51 +1000 Subject: [PATCH 211/681] Missed a few. --- Source/VirtualTrees.pas | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 3af29706a..7717103d6 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -12256,7 +12256,7 @@ destructor TBaseVirtualTree.Destroy; CheckSynchronize(); CheckSynchronize(); end;// if - FOptions.InternalSetMiscOptions(FOptions.FMiscOptions - [toReadOnly]); //SetMiscOptions has side effects + FOptions.InternalSetMiscOptions(FOptions.MiscOptions - [toReadOnly]); //SetMiscOptions has side effects // Make sure there is no reference remaining to the releasing tree. TWorkerThread.ReleaseThreadReference(); StopWheelPanning; @@ -19384,7 +19384,7 @@ function TBaseVirtualTree.DetermineLineImageAndSelectLevel(Node: PVirtualNode; v end; if (tsUseExplorerTheme in FStates) and HasChildren[Node] and (Indent >= 0) - and not ((vsAllChildrenHidden in Node.States) and (toAutoHideButtons in TreeOptions.FAutoOptions)) then + and not ((vsAllChildrenHidden in Node.States) and (toAutoHideButtons in TreeOptions.AutoOptions)) then LineImage[Indent] := ltNone; end; @@ -30891,7 +30891,7 @@ procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Targe // nodes is visible or auto button hiding is disabled. if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in Node.States) and not ((vsAllChildrenHidden in Node.States) and - (toAutoHideButtons in TreeOptions.FAutoOptions)) and + (toAutoHideButtons in TreeOptions.AutoOptions)) and ((toShowRoot in TreeOptions.PaintOptions) or (GetNodeLevel(Node) > 0)) then PaintNodeButton(Canvas, Node, Column, CellRect, Offsets[ofsToggleButton], ButtonY, BidiMode); // Relative X position of toggle button is needed for proper BiDi calculation @@ -34198,7 +34198,7 @@ function TCustomVirtualStringTree.DoGetNodeExtraWidth(Node: PVirtualNode; Column Canvas: TCanvas = nil): Integer; begin - if not (toShowStaticText in TreeOptions.FStringOptions) then + if not (toShowStaticText in TreeOptions.StringOptions) then Exit(0); if Canvas = nil then Canvas := Self.Canvas; @@ -34324,7 +34324,7 @@ procedure TCustomVirtualStringTree.DoPaintNode(var PaintInfo: TVTPaintInfo); PaintNormalText(PaintInfo, TextOutFlags, lEventArgs.CellText); // ... and afterwards the static text if not centered and the node is not multiline enabled. - if (Alignment <> taCenter) and not (vsMultiline in PaintInfo.Node.States) and (toShowStaticText in TreeOptions.FStringOptions) and not lEventArgs.StaticText.IsEmpty then + if (Alignment <> taCenter) and not (vsMultiline in PaintInfo.Node.States) and (toShowStaticText in TreeOptions.StringOptions) and not lEventArgs.StaticText.IsEmpty then PaintStaticText(PaintInfo, lEventArgs.StaticTextAlignment, lEventArgs.StaticText); finally RestoreFontChangeEvent(PaintInfo.Canvas); @@ -34506,9 +34506,9 @@ procedure TCustomVirtualStringTree.ReadOldStringOptions(Reader: TReader); OldOption := TOldVTStringOption(GetEnumValue(TypeInfo(TOldVTStringOption), EnumName)); case OldOption of soSaveCaptions: - StringOptions := FStringOptions + [toSaveCaptions]; + StringOptions := StringOptions + [toSaveCaptions]; soShowStaticText: - StringOptions := FStringOptions + [toShowStaticText]; + StringOptions := StringOptions + [toShowStaticText]; end; end; end; @@ -34559,7 +34559,7 @@ procedure TCustomVirtualStringTree.WriteChunks(Stream: TStream; Node: PVirtualNo begin inherited; - if (toSaveCaptions in TreeOptions.FStringOptions) and (Node <> FRoot) and + if (toSaveCaptions in TreeOptions.StringOptions) and (Node <> FRoot) and (vsInitialized in Node.States) then with Stream do begin From 9680e496b13780bfd598371c5b785ad7c39a200f Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Sun, 2 May 2021 18:29:07 +0100 Subject: [PATCH 212/681] Fixed superfluous bracket in PR #10 --- Source/VirtualTrees.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 7717103d6..8fe360854 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -18939,7 +18939,7 @@ function TBaseVirtualTree.DetermineDropMode(const P: TPoint; var HitInfo: THitIn ImageHit := HitInfo.HitPositions * [hiOnNormalIcon, hiOnStateIcon] <> []; LabelHit := hiOnItemLabel in HitInfo.HitPositions; ItemHit := (hiOnItem in HitInfo.HitPositions) and ((toFullRowDrag in FOptions.MiscOptions) or - (toFullRowSelect in FOptions.SelectionOptions))); + (toFullRowSelect in FOptions.SelectionOptions)); // In report mode only direct hits of the node captions/images in the main column are accepted as hits. if (toReportMode in FOptions.MiscOptions) and not (ItemHit or ((LabelHit or ImageHit) and From e7e242d7b46c59a51639efffcdfa0d5188b1970f Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Wed, 5 May 2021 10:22:55 +1000 Subject: [PATCH 213/681] Split out Header, Columns, Options, DragnDrop and a few other things into separate units. --- Packages/RAD Studio 10.1/VirtualTreesR.dpk | 12 +- Packages/RAD Studio 10.1/VirtualTreesR.dproj | 10 + Packages/RAD Studio 10.2/VirtualTreesR.dpk | 12 +- Packages/RAD Studio 10.2/VirtualTreesR.dproj | 10 + Packages/RAD Studio 10.3/VirtualTreesR.dpk | 12 +- Packages/RAD Studio 10.3/VirtualTreesR.dproj | 14 +- Packages/RAD Studio 10.4/VirtualTreesR.dpk | 18 +- Packages/RAD Studio 10.4/VirtualTreesR.dproj | 765 +- Packages/RAD Studio 10/VirtualTreesR.dpk | 12 +- Packages/RAD Studio 10/VirtualTreesR.dproj | 10 + Packages/RAD Studio XE3/VirtualTreesR.dpk | 13 +- Packages/RAD Studio XE3/VirtualTreesR.dproj | 12 +- Packages/RAD Studio XE4/VirtualTreesR.dpk | 14 +- Packages/RAD Studio XE4/VirtualTreesR.dproj | 12 +- Packages/RAD Studio XE5/VirtualTreesR.dpk | 14 +- Packages/RAD Studio XE5/VirtualTreesR.dproj | 12 +- Packages/RAD Studio XE6/VirtualTreesR.dpk | 14 +- Packages/RAD Studio XE6/VirtualTreesR.dproj | 12 +- Packages/RAD Studio XE7/VirtualTreesR.dpk | 12 +- Packages/RAD Studio XE7/VirtualTreesR.dproj | 12 +- Packages/RAD Studio XE8/VirtualTreesR.dpk | 12 +- Packages/RAD Studio XE8/VirtualTreesR.dproj | 10 + Source/VirtualTrees.Accessibility.pas | 3 +- Source/VirtualTrees.Actions.pas | 3 +- Source/VirtualTrees.ClipBoard.pas | 13 +- Source/VirtualTrees.Colors.pas | 252 + Source/VirtualTrees.Columns.pas | 3596 ++ Source/VirtualTrees.Constants.pas | 110 + Source/VirtualTrees.DataObject.pas | 482 + Source/VirtualTrees.DragImage.pas | 572 + Source/VirtualTrees.DragnDrop.pas | 343 + Source/VirtualTrees.EditLink.pas | 670 + Source/VirtualTrees.Export.pas | 9 +- Source/VirtualTrees.Header.pas | 2529 + Source/VirtualTrees.HeaderPopup.pas | 10 +- Source/VirtualTrees.Options.pas | 620 + Source/VirtualTrees.StyleHooks.pas | 19 +- Source/VirtualTrees.Types.pas | 45 + Source/VirtualTrees.pas | 41462 +++++++---------- 39 files changed, 26470 insertions(+), 25292 deletions(-) create mode 100644 Source/VirtualTrees.Colors.pas create mode 100644 Source/VirtualTrees.Columns.pas create mode 100644 Source/VirtualTrees.Constants.pas create mode 100644 Source/VirtualTrees.DataObject.pas create mode 100644 Source/VirtualTrees.DragImage.pas create mode 100644 Source/VirtualTrees.DragnDrop.pas create mode 100644 Source/VirtualTrees.EditLink.pas create mode 100644 Source/VirtualTrees.Header.pas create mode 100644 Source/VirtualTrees.Options.pas create mode 100644 Source/VirtualTrees.Types.pas diff --git a/Packages/RAD Studio 10.1/VirtualTreesR.dpk b/Packages/RAD Studio 10.1/VirtualTreesR.dpk index 186395feb..d6f90edc6 100644 --- a/Packages/RAD Studio 10.1/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.1/VirtualTreesR.dpk @@ -44,7 +44,17 @@ contains VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio 10.1/VirtualTreesR.dproj b/Packages/RAD Studio 10.1/VirtualTreesR.dproj index 9eab59276..a20f93ca3 100644 --- a/Packages/RAD Studio 10.1/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.1/VirtualTreesR.dproj @@ -90,6 +90,16 @@ + + + + + + + + + + Cfg_2 Base diff --git a/Packages/RAD Studio 10.2/VirtualTreesR.dpk b/Packages/RAD Studio 10.2/VirtualTreesR.dpk index 527e8a9a0..205e582a5 100644 --- a/Packages/RAD Studio 10.2/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.2/VirtualTreesR.dpk @@ -44,7 +44,17 @@ contains VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio 10.2/VirtualTreesR.dproj b/Packages/RAD Studio 10.2/VirtualTreesR.dproj index b1c9bb26f..0747e143b 100644 --- a/Packages/RAD Studio 10.2/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.2/VirtualTreesR.dproj @@ -90,6 +90,16 @@ + + + + + + + + + + Cfg_2 Base diff --git a/Packages/RAD Studio 10.3/VirtualTreesR.dpk b/Packages/RAD Studio 10.3/VirtualTreesR.dpk index d27dfdafa..1a5d1abf3 100644 --- a/Packages/RAD Studio 10.3/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.3/VirtualTreesR.dpk @@ -44,7 +44,17 @@ contains VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio 10.3/VirtualTreesR.dproj b/Packages/RAD Studio 10.3/VirtualTreesR.dproj index 346c57e3b..849f0aa7c 100644 --- a/Packages/RAD Studio 10.3/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.3/VirtualTreesR.dproj @@ -7,7 +7,7 @@ Package VCL DCC32 - 18.5 + 18.8 Win32 3 @@ -90,6 +90,16 @@ + + + + + + + + + + Cfg_2 Base @@ -137,7 +147,7 @@ 1.0.0.0 - + True diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dpk b/Packages/RAD Studio 10.4/VirtualTreesR.dpk index a9e3dd7a1..70ae7c19f 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dpk @@ -9,7 +9,7 @@ package VirtualTreesR; {$EXTENDEDSYNTAX ON} {$IMPORTEDDATA ON} {$IOCHECKS ON} -{$LOCALSYMBOLS ON} +{$LOCALSYMBOLS OFF} {$LONGSTRINGS ON} {$OPENSTRINGS ON} {$OPTIMIZATION ON} @@ -25,7 +25,7 @@ package VirtualTreesR; {$IMAGEBASE $400000} {$DEFINE RELEASE} {$ENDIF IMPLICITBUILDING} -{$LIBSUFFIX AUTO} +{$LIBSUFFIX '270'} {$RUNONLY} {$IMPLICITBUILD OFF} @@ -44,6 +44,18 @@ contains VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. + + diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dproj b/Packages/RAD Studio 10.4/VirtualTreesR.dproj index e889d06a1..07d2f6e96 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dproj @@ -1,14 +1,14 @@  + {B62F3689-96E1-47D5-9FB2-2A2718281FDB} + VirtualTreesR.dpk True - Package Release - DCC32 + Package VCL - VirtualTreesR.dpk - Win32 - {B62F3689-96E1-47D5-9FB2-2A2718281FDB} + DCC32 19.2 + Win32 3 @@ -29,35 +29,49 @@ Base true + + true + Cfg_2 + true + true + - VirtualTreesR All + VirtualTreesR .\$(Platform)\$(Config) ..\..\Source - 00400000 - System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) true + 270 + true ..\..\source;.\$(Platform)\$(Config);$(DCC_UnitSearchPath) - $(Auto) - true + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + 1053 + false true - true + 00400000 CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= - 1053 + false + true + false + false + false Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) - 0 - RELEASE;$(DCC_Define) false + RELEASE;$(DCC_Define) 0 + 0 DEBUG;$(DCC_Define) - true false + true + + + true @@ -76,6 +90,20 @@ + + + + + + + + + + + + Cfg_2 + Base + Base @@ -83,10 +111,6 @@ Cfg_1 Base - - Cfg_2 - Base - Delphi.Personality.12 @@ -96,7 +120,6 @@ VirtualTreesR.dpk - True False @@ -124,14 +147,716 @@ 1.0.0.0 + True True + + + + VirtualTreesR.bpl + true + + + + + 1 + + + 0 + + + + + classes + 1 + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + 12 + diff --git a/Packages/RAD Studio 10/VirtualTreesR.dpk b/Packages/RAD Studio 10/VirtualTreesR.dpk index ed3ea3b3c..70010c7e3 100644 --- a/Packages/RAD Studio 10/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10/VirtualTreesR.dpk @@ -44,7 +44,17 @@ contains VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio 10/VirtualTreesR.dproj b/Packages/RAD Studio 10/VirtualTreesR.dproj index 5efe3e8e5..0938d21bd 100644 --- a/Packages/RAD Studio 10/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10/VirtualTreesR.dproj @@ -90,6 +90,16 @@ + + + + + + + + + + Cfg_2 Base diff --git a/Packages/RAD Studio XE3/VirtualTreesR.dpk b/Packages/RAD Studio XE3/VirtualTreesR.dpk index 0dbe6b5bf..7cf65eeb3 100644 --- a/Packages/RAD Studio XE3/VirtualTreesR.dpk +++ b/Packages/RAD Studio XE3/VirtualTreesR.dpk @@ -42,9 +42,18 @@ contains VirtualTrees.Classes in '..\..\Source\VirtualTrees.Classes.pas', VirtualTrees.WorkerThread in '..\..\Source\VirtualTrees.WorkerThread.pas', VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas'; end. diff --git a/Packages/RAD Studio XE3/VirtualTreesR.dproj b/Packages/RAD Studio XE3/VirtualTreesR.dproj index d84abad29..2c66ffb53 100644 --- a/Packages/RAD Studio XE3/VirtualTreesR.dproj +++ b/Packages/RAD Studio XE3/VirtualTreesR.dproj @@ -103,9 +103,19 @@ - + + + + + + + + + + + Cfg_2 Base diff --git a/Packages/RAD Studio XE4/VirtualTreesR.dpk b/Packages/RAD Studio XE4/VirtualTreesR.dpk index fe6b6abeb..d0aa25479 100644 --- a/Packages/RAD Studio XE4/VirtualTreesR.dpk +++ b/Packages/RAD Studio XE4/VirtualTreesR.dpk @@ -42,9 +42,19 @@ contains VirtualTrees.Classes in '..\..\Source\VirtualTrees.Classes.pas', VirtualTrees.WorkerThread in '..\..\Source\VirtualTrees.WorkerThread.pas', VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio XE4/VirtualTreesR.dproj b/Packages/RAD Studio XE4/VirtualTreesR.dproj index 53c4c2117..31814a6f2 100644 --- a/Packages/RAD Studio XE4/VirtualTreesR.dproj +++ b/Packages/RAD Studio XE4/VirtualTreesR.dproj @@ -105,9 +105,19 @@ - + + + + + + + + + + + Cfg_2 Base diff --git a/Packages/RAD Studio XE5/VirtualTreesR.dpk b/Packages/RAD Studio XE5/VirtualTreesR.dpk index 25d2f1d77..1a87d894a 100644 --- a/Packages/RAD Studio XE5/VirtualTreesR.dpk +++ b/Packages/RAD Studio XE5/VirtualTreesR.dpk @@ -42,9 +42,19 @@ contains VirtualTrees.Classes in '..\..\Source\VirtualTrees.Classes.pas', VirtualTrees.WorkerThread in '..\..\Source\VirtualTrees.WorkerThread.pas', VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio XE5/VirtualTreesR.dproj b/Packages/RAD Studio XE5/VirtualTreesR.dproj index 2ca518671..45d98b332 100644 --- a/Packages/RAD Studio XE5/VirtualTreesR.dproj +++ b/Packages/RAD Studio XE5/VirtualTreesR.dproj @@ -114,9 +114,19 @@ - + + + + + + + + + + + Cfg_2 Base diff --git a/Packages/RAD Studio XE6/VirtualTreesR.dpk b/Packages/RAD Studio XE6/VirtualTreesR.dpk index f8c6494e0..6c21ffe2a 100644 --- a/Packages/RAD Studio XE6/VirtualTreesR.dpk +++ b/Packages/RAD Studio XE6/VirtualTreesR.dpk @@ -42,9 +42,19 @@ contains VirtualTrees.Classes in '..\..\Source\VirtualTrees.Classes.pas', VirtualTrees.WorkerThread in '..\..\Source\VirtualTrees.WorkerThread.pas', VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio XE6/VirtualTreesR.dproj b/Packages/RAD Studio XE6/VirtualTreesR.dproj index 997de03b8..e1120f100 100644 --- a/Packages/RAD Studio XE6/VirtualTreesR.dproj +++ b/Packages/RAD Studio XE6/VirtualTreesR.dproj @@ -114,9 +114,19 @@ - + + + + + + + + + + + Cfg_2 Base diff --git a/Packages/RAD Studio XE7/VirtualTreesR.dpk b/Packages/RAD Studio XE7/VirtualTreesR.dpk index 67e35d692..af08a58a2 100644 --- a/Packages/RAD Studio XE7/VirtualTreesR.dpk +++ b/Packages/RAD Studio XE7/VirtualTreesR.dpk @@ -44,7 +44,17 @@ contains VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio XE7/VirtualTreesR.dproj b/Packages/RAD Studio XE7/VirtualTreesR.dproj index d47bbc215..4e0aa0c55 100644 --- a/Packages/RAD Studio XE7/VirtualTreesR.dproj +++ b/Packages/RAD Studio XE7/VirtualTreesR.dproj @@ -88,8 +88,18 @@ - + + + + + + + + + + + Cfg_2 Base diff --git a/Packages/RAD Studio XE8/VirtualTreesR.dpk b/Packages/RAD Studio XE8/VirtualTreesR.dpk index 4ea2e1b16..5d3c8aeaf 100644 --- a/Packages/RAD Studio XE8/VirtualTreesR.dpk +++ b/Packages/RAD Studio XE8/VirtualTreesR.dpk @@ -44,7 +44,17 @@ contains VirtualTrees.ClipBoard in '..\..\Source\VirtualTrees.ClipBoard.pas', VirtualTrees.Actions in '..\..\Source\VirtualTrees.Actions.pas', VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', - VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas'; + VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', + VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', + VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', + VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', + VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', + VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', + VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', + VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', + VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio XE8/VirtualTreesR.dproj b/Packages/RAD Studio XE8/VirtualTreesR.dproj index 622e1da0c..4a9fc6dfa 100644 --- a/Packages/RAD Studio XE8/VirtualTreesR.dproj +++ b/Packages/RAD Studio XE8/VirtualTreesR.dproj @@ -90,6 +90,16 @@ + + + + + + + + + + Cfg_2 Base diff --git a/Source/VirtualTrees.Accessibility.pas b/Source/VirtualTrees.Accessibility.pas index 04b516d2a..97911200d 100644 --- a/Source/VirtualTrees.Accessibility.pas +++ b/Source/VirtualTrees.Accessibility.pas @@ -99,7 +99,8 @@ TVTMultiColumnAccessibleItemProvider = class(TInterfacedObject, IVTAccessibleP implementation uses - System.SysUtils, Vcl.Forms, System.Variants, System.Math; + System.SysUtils, Vcl.Forms, System.Variants, System.Math, + VirtualTrees.Columns; type diff --git a/Source/VirtualTrees.Actions.pas b/Source/VirtualTrees.Actions.pas index 8e6078107..04df24f07 100644 --- a/Source/VirtualTrees.Actions.pas +++ b/Source/VirtualTrees.Actions.pas @@ -7,7 +7,8 @@ interface System.Actions, Vcl.Controls, Vcl.ActnList, - VirtualTrees; + VirtualTrees, + VirtualTrees.Columns; type TVirtualTreeAction = class(TCustomAction) diff --git a/Source/VirtualTrees.ClipBoard.pas b/Source/VirtualTrees.ClipBoard.pas index e995a8775..b35c94165 100644 --- a/Source/VirtualTrees.ClipBoard.pas +++ b/Source/VirtualTrees.ClipBoard.pas @@ -32,7 +32,8 @@ interface Winapi.Windows, Winapi.ActiveX, System.Classes, - VirtualTrees; + VirtualTrees, + VirtualTrees.Types; type TClipboardFormatEntry = record @@ -98,6 +99,16 @@ TClipboardFormatList = class class function FindFormat(Fmt: Word; var Description: string): TVirtualTreeClass; overload; end; +var + // Clipboard format IDs used in OLE drag'n drop and clipboard transfers. + CF_VIRTUALTREE, + CF_VTREFERENCE, + CF_VRTF, + CF_VRTFNOOBJS, // Unfortunately CF_RTF* is already defined as being + // registration strings so I have to use different identifiers. + CF_HTML, + CF_CSV: Word; + implementation diff --git a/Source/VirtualTrees.Colors.pas b/Source/VirtualTrees.Colors.pas new file mode 100644 index 000000000..dcb859fae --- /dev/null +++ b/Source/VirtualTrees.Colors.pas @@ -0,0 +1,252 @@ +unit VirtualTrees.Colors; + +interface + +uses + System.Classes, + Vcl.Graphics, + Vcl.Themes, + Vcl.Controls; + +type + //class to collect all switchable colors into one place + TVTColors = class(TPersistent) + private type + TVTColorEnum = (cDisabledColor, cDropMarkColor, cDropTargetColor, cFocusedSelectionColor, cGridLineColor, cTreeLineColor, cUnfocusedSelectionColor, cBorderColor, cHotColor, + cFocusedSelectionBorderColor, cUnfocusedSelectionBorderColor, cDropTargetBorderColor, cSelectionRectangleBlendColor, cSelectionRectangleBorderColor, cHeaderHotColor, + cSelectionTextColor, cUnfocusedColor); + + //Please make sure that the published Color properties at the corresponding index + //have the same color if you change anything here! + const + cDefaultColors : array [TVTColorEnum] of TColor = (clBtnShadow, //DisabledColor + clHighlight, //DropMarkColor + clHighlight, //DropTargetColor + clHighlight, //FocusedSelectionColor + clBtnFace, //GridLineColor + clBtnShadow, //TreeLineColor + clInactiveCaption, //UnfocusedSelectionColor + clBtnFace, //BorderColor + clWindowText, //HotColor + clHighlight, //FocusedSelectionBorderColor + clInactiveCaption, //UnfocusedSelectionBorderColor + clHighlight, //DropTargetBorderColor + clHighlight, //SelectionRectangleBlendColor + clHighlight, //SelectionRectangleBorderColor + clBtnShadow, //HeaderHotColor + clHighlightText, //SelectionTextColor + clInactiveCaptionText); //UnfocusedColor [IPK] + + private + FOwner : TCustomControl; + FColors : array [TVTColorEnum] of TColor; //[IPK] 15 -> 16 + function GetColor(const Index : TVTColorEnum) : TColor; + procedure SetColor(const Index : TVTColorEnum; const Value : TColor); + function GetBackgroundColor : TColor; + function GetHeaderFontColor : TColor; + function GetNodeFontColor : TColor; + public + constructor Create(AOwner : TCustomControl); + + procedure Assign(Source : TPersistent); override; + function GetSelectedNodeFontColor(Focused : boolean) : TColor; + property BackGroundColor : TColor read GetBackgroundColor; + property HeaderFontColor : TColor read GetHeaderFontColor; + property NodeFontColor : TColor read GetNodeFontColor; + //Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl : TControl = nil) : TCustomStyleServices; + published + property BorderColor : TColor index cBorderColor read GetColor write SetColor default clBtnFace; + property DisabledColor : TColor index cDisabledColor read GetColor write SetColor default clBtnShadow; + property DropMarkColor : TColor index cDropMarkColor read GetColor write SetColor default clHighlight; + property DropTargetColor : TColor index cDropTargetColor read GetColor write SetColor default clHighlight; + property DropTargetBorderColor : TColor index cDropTargetBorderColor read GetColor write SetColor default clHighlight; + ///The background color of selected nodes in case the tree has the focus, or the toPopupMode flag is set. + property FocusedSelectionColor : TColor index cFocusedSelectionColor read GetColor write SetColor default clHighlight; + ///The border color of selected nodes when the tree has the focus. + property FocusedSelectionBorderColor : TColor index cFocusedSelectionBorderColor read GetColor write SetColor default clHighlight; + property GridLineColor : TColor index cGridLineColor read GetColor write SetColor default clBtnFace; + property HeaderHotColor : TColor index cHeaderHotColor read GetColor write SetColor default clBtnShadow; + property HotColor : TColor index cHotColor read GetColor write SetColor default clWindowText; + property SelectionRectangleBlendColor : TColor index cSelectionRectangleBlendColor read GetColor write SetColor default clHighlight; + property SelectionRectangleBorderColor : TColor index cSelectionRectangleBorderColor read GetColor write SetColor default clHighlight; + ///The text color of selected nodes + property SelectionTextColor : TColor index cSelectionTextColor read GetColor write SetColor default clHighlightText; + property TreeLineColor : TColor index cTreeLineColor read GetColor write SetColor default clBtnShadow; + property UnfocusedColor : TColor index cUnfocusedColor read GetColor write SetColor default clInactiveCaptionText; //[IPK] Added + ///The background color of selected nodes in case the tree does not have the focus and the toPopupMode flag is not set. + property UnfocusedSelectionColor : TColor index cUnfocusedSelectionColor read GetColor write SetColor default clInactiveCaption; + ///The border color of selected nodes in case the tree does not have the focus and the toPopupMode flag is not set. + property UnfocusedSelectionBorderColor : TColor index cUnfocusedSelectionBorderColor read GetColor write SetColor default clInactiveCaption; + end; + +implementation + +uses + WinApi.Windows, + VirtualTrees, + VirtualTrees.Utils, + VirtualTrees.StyleHooks; + +type + TBaseVirtualTreeCracker = class(TBaseVirtualTree); + + TVTColorsHelper = class helper for TVTColors + function TreeView : TBaseVirtualTreeCracker; + end; + + //----------------- TVTColors ------------------------------------------------------------------------------------------ + +constructor TVTColors.Create(AOwner : TCustomControl); +var + CE : TVTColorEnum; +begin + FOwner := AOwner; + for CE := Low(TVTColorEnum) to High(TVTColorEnum) do + FColors[CE] := cDefaultColors[CE]; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTColors.GetBackgroundColor : TColor; +begin + //XE2 VCL Style + if TreeView.VclStyleEnabled and (seClient in FOwner.StyleElements) then + Result := StyleServices.GetStyleColor(scTreeView) + else + Result := TreeView.Color; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTColors.GetColor(const Index : TVTColorEnum) : TColor; +begin + //Only try to fetch the color via StyleServices if theses are enabled + //Return default/user defined color otherwise + if TreeView.VclStyleEnabled then + begin + //If the ElementDetails are not defined, fall back to the SystemColor + case Index of + cDisabledColor : + if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemDisabled), ecTextColor, Result) then + Result := StyleServices.GetSystemColor(FColors[Index]); + cTreeLineColor : + if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttBranch), ecBorderColor, Result) then + Result := StyleServices.GetSystemColor(FColors[Index]); + cBorderColor : + if (seBorder in FOwner.StyleElements) then + Result := StyleServices.GetSystemColor(FColors[Index]) + else + Result := FColors[Index]; + cHotColor : + if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemHot), ecTextColor, Result) then + Result := StyleServices.GetSystemColor(FColors[Index]); + cHeaderHotColor : + if not StyleServices.GetElementColor(StyleServices.GetElementDetails(thHeaderItemHot), ecTextColor, Result) then + Result := StyleServices.GetSystemColor(FColors[Index]); + cSelectionTextColor : + if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemSelected), ecTextColor, Result) then + Result := StyleServices.GetSystemColor(clHighlightText); + cUnfocusedColor : + if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemSelectedNotFocus), ecTextColor, Result) then + Result := StyleServices.GetSystemColor(FColors[Index]); + else + Result := StyleServices.GetSystemColor(FColors[Index]); + end; + end + else + Result := FColors[Index]; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTColors.GetHeaderFontColor : TColor; +begin + //XE2+ VCL Style + if TreeView.VclStyleEnabled and (seFont in FOwner.StyleElements) then + StyleServices.GetElementColor(StyleServices.GetElementDetails(thHeaderItemNormal), ecTextColor, Result) + else + Result := TreeView.Header.Font.Color; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTColors.GetNodeFontColor : TColor; +begin + if TreeView.VclStyleEnabled and (seFont in FOwner.StyleElements) then + StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemNormal), ecTextColor, Result) + else + Result := TreeView.Font.Color; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTColors.GetSelectedNodeFontColor(Focused : boolean) : TColor; +begin + if Focused then + begin + if (tsUseExplorerTheme in TreeView.TreeStates) and not IsHighContrastEnabled then + begin + Result := NodeFontColor + end + else + Result := SelectionTextColor + end//if Focused + else + Result := UnfocusedColor; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTColors.SetColor(const Index : TVTColorEnum; const Value : TColor); +begin + if FColors[Index] <> Value then + begin + FColors[Index] := Value; + if not (csLoading in FOwner.ComponentState) and FOwner.HandleAllocated then + begin + //Cause helper bitmap rebuild if the button color changed. + case Index of + cTreeLineColor : + begin + TreeView.PrepareBitmaps(True, False); + FOwner.Invalidate; + end; + cBorderColor : + RedrawWindow(FOwner.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN) + else + FOwner.Invalidate; + end; + end; + end; +end; + +function TVTColors.StyleServices(AControl : TControl) : TCustomStyleServices; +begin + if AControl = nil then + AControl := FOwner; + Result := VTStyleServices(AControl); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTColors.Assign(Source : TPersistent); +begin + if Source is TVTColors then + begin + FColors := TVTColors(Source).FColors; + if TreeView.UpdateCount = 0 then + TreeView.Invalidate; + end + else + inherited; +end; + +{ TVTColorsHelper } + +function TVTColorsHelper.TreeView : TBaseVirtualTreeCracker; +begin + Result := TBaseVirtualTreeCracker(FOwner); +end; + +end. diff --git a/Source/VirtualTrees.Columns.pas b/Source/VirtualTrees.Columns.pas new file mode 100644 index 000000000..7b47b2315 --- /dev/null +++ b/Source/VirtualTrees.Columns.pas @@ -0,0 +1,3596 @@ +unit VirtualTrees.Columns; + +interface + +uses + System.Classes, + System.Types, + System.UITypes, + WinApi.Windows, + Vcl.Controls, + Vcl.Graphics, + Vcl.Menus, + Vcl.Themes, + Vcl.GraphUtil, + VirtualTrees.Constants, + VirtualTrees.Types, + VirtualTrees.Options; + +{$MINENUMSIZE 1, make enumerations as small as possible} + + +type + // Options per column. + TVTColumnOption = ( + coAllowClick, // Column can be clicked (must be enabled too). + coDraggable, // Column can be dragged. + coEnabled, // Column is enabled. + coParentBidiMode, // Column uses the parent's bidi mode. + coParentColor, // Column uses the parent's background color. + coResizable, // Column can be resized. + coShowDropMark, // Column shows the drop mark if it is currently the drop target. + coVisible, // Column is shown. + coAutoSpring, // Column takes part in the auto spring feature of the header (must be resizable too). + coFixed, // Column is fixed and can not be selected or scrolled etc. + coSmartResize, // Column is resized to its largest entry which is in view (instead of its largest + // visible entry). + coAllowFocus, // Column can be focused. + coDisableAnimatedResize, // Column resizing is not animated. + coWrapCaption, // Caption could be wrapped across several header lines to fit columns width. + coUseCaptionAlignment, // Column's caption has its own aligment. + coEditable, // Column can be edited + coStyleColor // Prefer background color of VCL style over TVirtualTreeColumn.Color + ); + TVTColumnOptions = set of TVTColumnOption; + + TVirtualTreeColumnStyle = ( + vsText, + vsOwnerDraw + ); + + TVTHeaderColumnLayout = ( + blGlyphLeft, + blGlyphRight, + blGlyphTop, + blGlyphBottom + ); + + TSortDirection = ( + sdAscending, + sdDescending + ); + + TSortDirectionHelper = record helper for VirtualTrees.Columns.TSortDirection + strict private + const + cSortDirectionToInt : Array [TSortDirection] of Integer = (1, - 1); + public + /// Returns +1 for ascending and -1 for descending sort order. + function ToInt() : Integer; inline; + end; + + TCheckType = ( + ctNone, + ctTriStateCheckBox, + ctCheckBox, + ctRadioButton, + ctButton + ); + + // The check states include both, transient and fluent (temporary) states. The only temporary state defined so + // far is the pressed state. + TCheckState = ( + csUncheckedNormal, // unchecked and not pressed + csUncheckedPressed, // unchecked and pressed + csCheckedNormal, // checked and not pressed + csCheckedPressed, // checked and pressed + csMixedNormal, // 3-state check box and not pressed + csMixedPressed, // 3-state check box and pressed + csUncheckedDisabled, // disabled checkbox, not checkable + csCheckedDisabled, // disabled checkbox, not uncheckable + csMixedDisabled // disabled 3-state checkbox + ); + + /// Adds some convenience methods to type TCheckState + TCheckStateHelper = record helper for TCheckState + strict private + const + // Lookup to quickly convert a specific check state into its pressed counterpart and vice versa. + cPressedState : array [TCheckState] of TCheckState = ( + csUncheckedPressed, csUncheckedPressed, csCheckedPressed, csCheckedPressed, csMixedPressed, csMixedPressed, csUncheckedDisabled, csCheckedDisabled, csMixedDisabled); + cUnpressedState : array [TCheckState] of TCheckState = ( + csUncheckedNormal, csUncheckedNormal, csCheckedNormal, csCheckedNormal, csMixedNormal, csMixedNormal, csUncheckedDisabled, csCheckedDisabled, csMixedDisabled); + cEnabledState : array [TCheckState] of TCheckState = ( + csUncheckedNormal, csUncheckedPressed, csCheckedNormal, csCheckedPressed, csMixedNormal, csMixedPressed, csUncheckedNormal, csCheckedNormal, csMixedNormal); + cToggledState : array [TCheckState] of TCheckState = ( + csCheckedNormal, csCheckedPressed, csUncheckedNormal, csUncheckedPressed, csCheckedNormal, csCheckedPressed, csUncheckedDisabled, csCheckedDisabled, csMixedDisabled); + public + function GetPressed() : TCheckState; inline; + function GetUnpressed() : TCheckState; inline; + function GetEnabled() : TCheckState; inline; + function GetToggled() : TCheckState; inline; + function IsDisabled() : Boolean; inline; + function IsChecked() : Boolean; inline; + function IsUnChecked() : Boolean; inline; + function IsMixed() : Boolean; inline; + end; + +// Used during owner draw of the header to indicate which drop mark for the column must be drawn. + TVTDropMarkMode = ( + dmmNone, + dmmLeft, + dmmRight + ); + + // auto scroll directions + TScrollDirections = set of TScrollDirection; +// sdLeft, +// sdUp, +// sdRight, +// sdDown +// ); + +const + DefaultColumnOptions = [coAllowClick, coDraggable, coEnabled, coParentColor, coParentBidiMode, coResizable, + coShowDropMark, coVisible, coAllowFocus, coEditable, coStyleColor]; + +type + TVirtualTreeColumn = class; + + // This structure carries all important information about header painting and is used in the advanced header painting. + THeaderPaintInfo = record + TargetCanvas : TCanvas; + Column : TVirtualTreeColumn; + PaintRectangle : TRect; + TextRectangle : TRect; + IsHoverIndex, + IsDownIndex, + IsEnabled, + ShowHeaderGlyph, + ShowSortGlyph, + ShowRightBorder : Boolean; + DropMark : TVTDropMarkMode; + GlyphPos, + SortGlyphPos : TPoint; + SortGlyphSize : TSize; + procedure DrawSortArrow(pDirection : TSortDirection); + procedure DrawDropMark(); + end; + + TVirtualTreeColumns = class; + + TVirtualTreeColumn = class(TCollectionItem) + private + const + cDefaultColumnSpacing = 3; + private + FText, + FHint : string; + FWidth : TDimension; + FPosition : TColumnPosition; + FMinWidth : TDimension; + FMaxWidth : TDimension; + FStyle : TVirtualTreeColumnStyle; + FImageIndex : TImageIndex; + FBiDiMode : TBiDiMode; + FLayout : TVTHeaderColumnLayout; + FMargin, + FSpacing : TDimension; + FOptions : TVTColumnOptions; + FEditOptions : TVTEditOptions; + FEditNextColumn : TDimension; + FTag : NativeInt; + FAlignment : TAlignment; + FCaptionAlignment : TAlignment; // Alignment of the caption. + FLastWidth : TDimension; + FColor : TColor; + FBonusPixel : Boolean; + FSpringRest : Single; // Accumulator for width adjustment when auto spring option is enabled. + FCaptionText : string; + FCheckBox : Boolean; + FCheckType : TCheckType; + FCheckState : TCheckState; + FImageRect : TRect; + FHasImage : Boolean; + FDefaultSortDirection : TSortDirection; + function GetCaptionAlignment : TAlignment; + function GetCaptionWidth : TDimension; + function GetLeft : TDimension; + function IsBiDiModeStored : Boolean; + function IsCaptionAlignmentStored : Boolean; + function IsColorStored : Boolean; + procedure SetAlignment(const Value : TAlignment); + procedure SetBiDiMode(Value : TBiDiMode); + procedure SetCaptionAlignment(const Value : TAlignment); + procedure SetCheckBox(Value : Boolean); + procedure SetCheckState(Value : TCheckState); + procedure SetCheckType(Value : TCheckType); + procedure SetColor(const Value : TColor); + procedure SetImageIndex(Value : TImageIndex); + procedure SetLayout(Value : TVTHeaderColumnLayout); + procedure SetMargin(Value : TDimension); + procedure SetMaxWidth(Value : TDimension); + procedure SetMinWidth(Value : TDimension); + procedure SetOptions(Value : TVTColumnOptions); + procedure SetPosition(Value : TColumnPosition); + procedure SetSpacing(Value : TDimension); + procedure SetStyle(Value : TVirtualTreeColumnStyle); + + protected + FLeft : TDimension; + procedure ChangeScale(M, D : TDimension; isDpiChange : Boolean); virtual; + procedure ComputeHeaderLayout(var PaintInfo : THeaderPaintInfo; DrawFormat : Cardinal; CalculateTextRect : Boolean = False); + procedure DefineProperties(Filer : TFiler); override; + procedure GetAbsoluteBounds(var Left, Right : TDimension); + function GetDisplayName : string; override; + function GetText : string; virtual; // [IPK] + procedure SetText(const Value : string); virtual; // [IPK] private to protected & virtual + function GetOwner : TVirtualTreeColumns; reintroduce; + procedure InternalSetWidth(const Value : TDimension); //bypass side effects in SetWidth + procedure ReadHint(Reader : TReader); + procedure ReadText(Reader : TReader); + procedure SetCollection(Value : TCollection); override; + procedure SetWidth(Value : TDimension); + public + constructor Create(Collection : TCollection); override; + destructor Destroy; override; + + procedure Assign(Source : TPersistent); override; + function Equals(OtherColumnObj : TObject) : Boolean; override; + function GetRect : TRect; virtual; + property HasImage : Boolean read FHasImage; + property ImageRect : TRect read FImageRect; + procedure LoadFromStream(const Stream : TStream; Version : Integer); + procedure ParentBiDiModeChanged; + procedure ParentColorChanged; + procedure RestoreLastWidth; + function GetEffectiveColor() : TColor; + procedure SaveToStream(const Stream : TStream); + function UseRightToLeftReading : Boolean; + + property BonusPixel : Boolean read FBonusPixel write FBonusPixel; + property CaptionText : string read FCaptionText; + property Left : TDimension read GetLeft; + property Owner : TVirtualTreeColumns read GetOwner; + property SpringRest : Single read FSpringRest write FSpringRest; + published + property Alignment : TAlignment read FAlignment write SetAlignment default taLeftJustify; + property BiDiMode : TBiDiMode read FBiDiMode write SetBiDiMode stored IsBiDiModeStored; + property CaptionAlignment : TAlignment read GetCaptionAlignment write SetCaptionAlignment + stored IsCaptionAlignmentStored default taLeftJustify; + property CaptionWidth : TDimension read GetCaptionWidth; + property CheckType : TCheckType read FCheckType write SetCheckType default ctCheckBox; + property CheckState : TCheckState read FCheckState write SetCheckState default csUncheckedNormal; + property CheckBox : Boolean read FCheckBox write SetCheckBox default False; + property Color : TColor read FColor write SetColor stored IsColorStored; + property DefaultSortDirection : TSortDirection read FDefaultSortDirection write FDefaultSortDirection default sdAscending; + property Hint : string read FHint write FHint; + property ImageIndex : TImageIndex read FImageIndex write SetImageIndex default - 1; + property Layout : TVTHeaderColumnLayout read FLayout write SetLayout default blGlyphLeft; + property Margin : TDimension read FMargin write SetMargin default 4; + property MaxWidth : TDimension read FMaxWidth write SetMaxWidth default 10000; + property MinWidth : TDimension read FMinWidth write SetMinWidth default 10; + property Options : TVTColumnOptions read FOptions write SetOptions default DefaultColumnOptions; + property EditOptions : TVTEditOptions read FEditOptions write FEditOptions default toDefaultEdit; + property EditNextColumn : TDimension read FEditNextColumn write FEditNextColumn default - 1; + property Position : TColumnPosition read FPosition write SetPosition; + property Spacing : TDimension read FSpacing write SetSpacing default cDefaultColumnSpacing; + property Style : TVirtualTreeColumnStyle read FStyle write SetStyle default vsText; + property Tag : NativeInt read FTag write FTag default 0; + property Text : string read GetText write SetText; + property Width : TDimension read FWidth write SetWidth default 50; + end; + + TVirtualTreeColumnClass = class of TVirtualTreeColumn; + + TColumnsArray = array of TVirtualTreeColumn; + TCardinalArray = array of Cardinal; + TIndexArray = array of TColumnIndex; + + TVirtualTreeColumns = class(TCollection) + private + FHeader : TPersistent; + FHeaderBitmap : TBitmap; // backbuffer for drawing + FHoverIndex, // currently "hot" column + FDownIndex, // Column on which a mouse button is held down. + FTrackIndex : TColumnIndex; // Index of column which is currently being resized. + FClickIndex : TColumnIndex; // Index of the last clicked column. + FCheckBoxHit : Boolean; // True if the last click was on a header checkbox. + FPositionToIndex : TIndexArray; + FDefaultWidth : TDimension; // the width columns are created with + FNeedPositionsFix : Boolean; // True if FixPositions must still be called after DFM loading or Bidi mode change. + FClearing : Boolean; // True if columns are being deleted entirely. + FColumnPopupMenu : TPopupMenu; // Member for storing the TVTHeaderPopupMenu + + function GetCount : TDimension; + function GetItem(Index : TColumnIndex) : TVirtualTreeColumn; + function GetNewIndex(P : TPoint; var OldIndex : TColumnIndex) : Boolean; + procedure SetDefaultWidth(Value : TDimension); + procedure SetItem(Index : TColumnIndex; Value : TVirtualTreeColumn); + protected + // drag support + FDragIndex : TColumnIndex; // index of column currently being dragged + FDropTarget : TColumnIndex; // current target column (index) while dragging + FDropBefore : Boolean; // True if drop position is in the left half of a column, False for the right + // side to drop the dragged column to + + procedure AdjustAutoSize(CurrentIndex : TColumnIndex; Force : Boolean = False); + function AdjustDownColumn(P : TPoint) : TColumnIndex; + function AdjustHoverColumn(P : TPoint) : Boolean; + procedure AdjustPosition(Column : TVirtualTreeColumn; Position : Cardinal); + function CanSplitterResize(P : TPoint; Column : TColumnIndex) : Boolean; + procedure DoCanSplitterResize(P : TPoint; Column : TColumnIndex; var Allowed : Boolean); virtual; + procedure DrawButtonText(DC : HDC; Caption : string; Bounds : TRect; Enabled, Hot : Boolean; DrawFormat : Cardinal; + WrapCaption : Boolean); + procedure FixPositions; + function GetColumnAndBounds(P : TPoint; var ColumnLeft, ColumnRight : TDimension; Relative : Boolean = True) : Integer; + function GetOwner : TPersistent; override; + function HandleClick(P : TPoint; Button : TMouseButton; Force, DblClick : Boolean) : Boolean; virtual; + procedure HeaderPopupMenuAddHeaderPopupItem(const Sender : TObject; const Column : TColumnIndex; var Cmd : TAddPopupItemType); + procedure HeaderPopupMenuColumnChange(const Sender : TObject; const Column : TColumnIndex; Visible : Boolean); + procedure IndexChanged(OldIndex, NewIndex : Integer); + procedure InitializePositionArray; + procedure Notify(Item : TCollectionItem; Action : System.Classes.TCollectionNotification); override; + procedure ReorderColumns(RTL : Boolean); + procedure SetHoverIndex(Index : TColumnIndex); + procedure Update(Item : TCollectionItem); override; + procedure UpdatePositions(Force : Boolean = False); + + property HeaderBitmap : TBitmap read FHeaderBitmap; + property PositionToIndex : TIndexArray read FPositionToIndex; + property HoverIndex : TColumnIndex read FHoverIndex write FHoverIndex; + property DownIndex : TColumnIndex read FDownIndex write FDownIndex; + property CheckBoxHit : Boolean read FCheckBoxHit write FCheckBoxHit; + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl : TControl = nil) : TCustomStyleServices; + public + constructor Create(AOwner : TPersistent); virtual; + destructor Destroy; override; + + function Add : TVirtualTreeColumn; virtual; + procedure AnimatedResize(Column : TColumnIndex; NewWidth : TDimension); + procedure Assign(Source : TPersistent); override; + procedure Clear; virtual; + function ColumnFromPosition(P : TPoint; Relative : Boolean = True) : TColumnIndex; overload; virtual; + function ColumnFromPosition(PositionIndex : TColumnPosition) : TColumnIndex; overload; virtual; + function Equals(OtherColumnsObj : TObject) : Boolean; override; + procedure GetColumnBounds(Column : TColumnIndex; var Left, Right : TDimension); + function GetFirstVisibleColumn(ConsiderAllowFocus : Boolean = False) : TColumnIndex; + function GetLastVisibleColumn(ConsiderAllowFocus : Boolean = False) : TColumnIndex; + function GetFirstColumn : TColumnIndex; + function GetNextColumn(Column : TColumnIndex) : TColumnIndex; + function GetNextVisibleColumn(Column : TColumnIndex; ConsiderAllowFocus : Boolean = False) : TColumnIndex; + function GetPreviousColumn(Column : TColumnIndex) : TColumnIndex; + function GetPreviousVisibleColumn(Column : TColumnIndex; ConsiderAllowFocus : Boolean = False) : TColumnIndex; + function GetScrollWidth : TDimension; + function GetVisibleColumns : TColumnsArray; + function GetVisibleFixedWidth : TDimension; + function IsValidColumn(Column : TColumnIndex) : Boolean; + procedure LoadFromStream(const Stream : TStream; Version : Integer); + procedure PaintHeader(DC : HDC; R : TRect; HOffset : TDimension); overload; virtual; + procedure PaintHeader(TargetCanvas : TCanvas; R : TRect; const Target : TPoint; + RTLOffset : TDimension = 0); overload; virtual; + procedure SaveToStream(const Stream : TStream); + procedure EndUpdate(); override; + function TotalWidth : TDimension; + + property Count : Integer read GetCount; + property ClickIndex : TColumnIndex read FClickIndex write FClickIndex; + property DefaultWidth : TDimension read FDefaultWidth write SetDefaultWidth; + property DragIndex : TColumnIndex read FDragIndex write FDragIndex; + property DropBefore : Boolean read FDropBefore write FDropBefore; + property DropTarget : TColumnIndex read FDropTarget write FDropTarget; + property Items[Index : TColumnIndex] : TVirtualTreeColumn read GetItem write SetItem; default; + //property Header: TPersistent read FHeader; + property TrackIndex : TColumnIndex read FTrackIndex write FTrackIndex; + end; + + TVirtualTreeColumnsClass = class of TVirtualTreeColumns; + +implementation + +uses + WinApi.ShlObj, + WinApi.UxTheme, + System.Math, + System.SysUtils, + Vcl.ImgList, + VirtualTrees, + VirtualTrees.Header, + VirtualTrees.HeaderPopup, + VirtualTrees.StyleHooks, + VirtualTrees.Utils; + +type + TBaseVirtualTreeCracker = class(TBaseVirtualTree); + TVTHeaderCracker = class(TVTHeader); + + TVirtualTreeColumnHelper = class helper for TVirtualTreeColumn + function TreeView : TBaseVirtualTreeCracker; + function Header : TVTHeaderCracker; + end; + + TVirtualTreeColumnsHelper = class helper for TVirtualTreeColumns + function Header : TVTHeaderCracker; + function TreeView : TBaseVirtualTreeCracker; + end; + +{ TSortDirectionHelper } + +function TSortDirectionHelper.ToInt() : Integer; +begin + Result := cSortDirectionToInt[Self]; +end; + +{ TCheckStateHelper } + +function TCheckStateHelper.IsDisabled : Boolean; +begin + Result := Self >= TCheckState.csUncheckedDisabled; +end; + +function TCheckStateHelper.IsChecked : Boolean; +begin + Result := Self in [csCheckedNormal, csCheckedPressed, csCheckedDisabled]; +end; + +function TCheckStateHelper.IsUnChecked : Boolean; +begin + Result := Self in [csUncheckedNormal, csUncheckedPressed, csUncheckedDisabled]; +end; + +function TCheckStateHelper.IsMixed : Boolean; +begin + Result := Self in [csMixedNormal, csMixedPressed, csMixedDisabled]; +end; + +function TCheckStateHelper.GetEnabled : TCheckState; +begin + Result := cEnabledState[Self]; +end; + +function TCheckStateHelper.GetPressed() : TCheckState; +begin + Result := cPressedState[Self]; +end; + +function TCheckStateHelper.GetUnpressed() : TCheckState; +begin + Result := cUnpressedState[Self]; +end; + +function TCheckStateHelper.GetToggled() : TCheckState; +begin + Result := cToggledState[Self]; +end; + + + +//----------------- TVirtualTreeColumn --------------------------------------------------------------------------------- + +constructor TVirtualTreeColumn.Create(Collection : TCollection); + +begin + FMinWidth := 10; + FMaxWidth := 10000; + FImageIndex := - 1; + FMargin := 4; + FSpacing := cDefaultColumnSpacing; + FText := ''; + FOptions := DefaultColumnOptions; + FAlignment := taLeftJustify; + FBiDiMode := bdLeftToRight; + FColor := clWindow; + FLayout := blGlyphLeft; + FBonusPixel := False; + FCaptionAlignment := taLeftJustify; + FCheckType := ctCheckBox; + FCheckState := csUncheckedNormal; + FCheckBox := False; + FHasImage := False; + FDefaultSortDirection := sdAscending; + FEditNextColumn := - 1; + + inherited Create(Collection); + + if Assigned(Owner) then + begin + FWidth := Owner.DefaultWidth; + FLastWidth := Owner.DefaultWidth; + FPosition := Owner.Count - 1; + end; +end; + +procedure TVirtualTreeColumn.SetCollection(Value : TCollection); +begin + inherited; + // Read parent bidi mode and color values as default values. + ParentBiDiModeChanged; + ParentColorChanged; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +destructor TVirtualTreeColumn.Destroy; + +var + I : Integer; + ai : TColumnIndex; + sc : TColumnIndex; + + //--------------- local function --------------------------------------------- + + procedure AdjustColumnIndex(var ColumnIndex : TColumnIndex); + + begin + if Index = ColumnIndex then + ColumnIndex := NoColumn + else + if Index < ColumnIndex then + Dec(ColumnIndex); + end; + + //--------------- end local function ----------------------------------------- + +begin + // Check if this column is somehow referenced by its collection parent or the header. + with Owner do + begin + // If the columns collection object is currently deleting all columns + // then we don't need to check the various cached indices individually. + if not FClearing then + begin + TreeView.CancelEditNode; + IndexChanged(Index, - 1); + + AdjustColumnIndex(FHoverIndex); + AdjustColumnIndex(FDownIndex); + AdjustColumnIndex(FTrackIndex); + AdjustColumnIndex(FClickIndex); + + with TVTHeaderCracker(Header) do + begin + ai := AutoSizeIndex; + AdjustColumnIndex(ai); + InternalSetAutoSizeIndex(ai); + if Index = MainColumn then + begin + // If the current main column is about to be destroyed then we have to find a new main column. + InternalSetMainColumn(NoColumn); //SetColumn has side effects we want to avoid here. + for I := 0 to Count - 1 do + if I <> Index then + begin + InternalSetMainColumn(I); + Break; + end; + end; + sc := SortColumn; + AdjustColumnIndex(sc); + InternalSetSortColumn(sc); + end; + end; + end; + + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.GetCaptionAlignment : TAlignment; + +begin + if coUseCaptionAlignment in FOptions then + Result := FCaptionAlignment + else + Result := FAlignment; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.GetCaptionWidth : TDimension; +var + Theme : HTHEME; + AdvancedOwnerDraw : Boolean; + PaintInfo : THeaderPaintInfo; + RequestedElements : THeaderPaintElements; + + TextSize : TSize; + HeaderGlyphSize : TPoint; + UseText : Boolean; + R : TRect; +begin + AdvancedOwnerDraw := (hoOwnerDraw in Header.Options) and Assigned(TreeView.OnAdvancedHeaderDraw) and Assigned(TreeView.OnHeaderDrawQueryElements) and + not (csDesigning in TreeView.ComponentState); + + PaintInfo.Column := Self; + PaintInfo.TargetCanvas := Owner.HeaderBitmap.Canvas; + + with PaintInfo, Column do + begin + ShowHeaderGlyph := (hoShowImages in Header.Options) and ((Assigned(Header.Images) and (FImageIndex > - 1)) or FCheckBox); + ShowSortGlyph := ((Header.SortColumn > - 1) and (Self = Owner.Items[Header.SortColumn])) and (hoShowSortGlyphs in Header.Options); + + // This path for text columns or advanced owner draw. + // See if the application wants to draw part of the header itself. + RequestedElements := []; + if AdvancedOwnerDraw then + begin + PaintInfo.Column := Self; + TreeView.DoHeaderDrawQueryElements(PaintInfo, RequestedElements); + end; + end; + + UseText := Length(FText) > 0; + // If nothing is to show then don't waste time with useless preparation. + if not (UseText or PaintInfo.ShowHeaderGlyph or PaintInfo.ShowSortGlyph) then + Exit(0); + + // Calculate sizes of the involved items. + with Header do + begin + if PaintInfo.ShowHeaderGlyph then + if not FCheckBox then + begin + if Assigned(Images) then + HeaderGlyphSize := Point(Images.Width, Images.Height); + end + else + with Self.TreeView do + begin + if Assigned(CheckImages) then + HeaderGlyphSize := Point(CheckImages.Width, CheckImages.Height); + end + else + HeaderGlyphSize := Point(0, 0); + if PaintInfo.ShowSortGlyph then + begin + if tsUseExplorerTheme in Self.TreeView.TreeStates then + begin + R := Rect(0, 0, 100, 100); + Theme := OpenThemeData(Self.TreeView.Handle, 'HEADER'); + GetThemePartSize(Theme, PaintInfo.TargetCanvas.Handle, HP_HEADERSORTARROW, HSAS_SORTEDUP, @R, TS_TRUE, PaintInfo.SortGlyphSize); + CloseThemeData(Theme); + end + else + begin + PaintInfo.SortGlyphSize.cx := Self.TreeView.ScaledPixels(16); + PaintInfo.SortGlyphSize.cy := Self.TreeView.ScaledPixels(4); + end; + end + else + begin + PaintInfo.SortGlyphSize.cx := 0; + PaintInfo.SortGlyphSize.cy := 0; + end; + end; + + if UseText then + begin + GetTextExtentPoint32W(PaintInfo.TargetCanvas.Handle, PWideChar(FText), Length(FText), TextSize); + Inc(TextSize.cx, 2); + end + else + begin + TextSize.cx := 0; + TextSize.cy := 0; + end; + + // if CalculateTextRect then + Result := TextSize.cx; + if PaintInfo.ShowHeaderGlyph then + if Layout in [blGlyphLeft, blGlyphRight] then + Inc(Result, HeaderGlyphSize.X + FSpacing) + else // if Layout in [ blGlyphTop, blGlyphBottom] then + Result := Max(Result, HeaderGlyphSize.X); + if PaintInfo.ShowSortGlyph then + Inc(Result, PaintInfo.SortGlyphSize.cx + FSpacing + 2); // without this +2, there is a slight movement of the sort glyph when expanding the column + +end; +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.GetLeft : Integer; + +begin + Result := FLeft; + if [coVisible, coFixed] * FOptions <> [coVisible, coFixed] then + Dec(Result, TreeView.EffectiveOffsetX); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.IsBiDiModeStored : Boolean; + +begin + Result := not (coParentBidiMode in FOptions); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.IsCaptionAlignmentStored : Boolean; + +begin + Result := coUseCaptionAlignment in FOptions; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.IsColorStored : Boolean; + +begin + Result := not (coParentColor in FOptions); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetAlignment(const Value : TAlignment); + +begin + if FAlignment <> Value then + begin + FAlignment := Value; + Changed(False); + // Setting the alignment affects also the tree, hence invalidate it too. + TreeView.Invalidate; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetBiDiMode(Value : TBiDiMode); + +begin + if Value <> FBiDiMode then + begin + FBiDiMode := Value; + Exclude(FOptions, coParentBidiMode); + Changed(False); + // Setting the alignment affects also the tree, hence invalidate it too. + TreeView.Invalidate; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetCaptionAlignment(const Value : TAlignment); + +begin + if not (coUseCaptionAlignment in FOptions) or (FCaptionAlignment <> Value) then + begin + FCaptionAlignment := Value; + Include(FOptions, coUseCaptionAlignment); + // Setting the alignment affects also the tree, hence invalidate it too. + Header.Invalidate(Self); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetColor(const Value : TColor); + +begin + if FColor <> Value then + begin + FColor := Value; + Exclude(FOptions, coParentColor); + Exclude(FOptions, coStyleColor); // Issue #919 + Changed(False); + TreeView.Invalidate; + end; +end; + +function TVirtualTreeColumn.GetEffectiveColor() : TColor; +// Returns the color that should effectively be used as background color for this +// column considering all flags in the TVirtualTreeColumn.Options property +begin + if (coParentColor in Options) or ((coStyleColor in Options) and TreeView.VclStyleEnabled) then + Result := TreeView.Colors.BackGroundColor + else + Result := Self.Color; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetCheckBox(Value : Boolean); + +begin + if Value <> FCheckBox then + begin + FCheckBox := Value; + if Value and (csDesigning in TreeView.ComponentState) then + Header.Options := Header.Options + [hoShowImages]; + Changed(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetCheckState(Value : TCheckState); + +begin + if Value <> FCheckState then + begin + FCheckState := Value; + Changed(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetCheckType(Value : TCheckType); + +begin + if Value <> FCheckType then + begin + FCheckType := Value; + Changed(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetImageIndex(Value : TImageIndex); + +begin + if Value <> FImageIndex then + begin + FImageIndex := Value; + Changed(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetLayout(Value : TVTHeaderColumnLayout); + +begin + if FLayout <> Value then + begin + FLayout := Value; + Changed(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetMargin(Value : Integer); + +begin + // Compatibility setting for -1. + if Value < 0 then + Value := 4; + if FMargin <> Value then + begin + FMargin := Value; + Changed(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetMaxWidth(Value : Integer); + +begin + if Value < FMinWidth then + Value := FMinWidth; + FMaxWidth := Value; + SetWidth(FWidth); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetMinWidth(Value : Integer); + +begin + if Value < 0 then + Value := 0; + if Value > FMaxWidth then + Value := FMaxWidth; + FMinWidth := Value; + SetWidth(FWidth); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetOptions(Value : TVTColumnOptions); + +var + ToBeSet, + ToBeCleared : TVTColumnOptions; + VisibleChanged, + lParentColorSet : Boolean; +begin + if FOptions <> Value then + begin + ToBeCleared := FOptions - Value; + ToBeSet := Value - FOptions; + + FOptions := Value; + + VisibleChanged := coVisible in (ToBeSet + ToBeCleared); + lParentColorSet := coParentColor in ToBeSet; + + if coParentBidiMode in ToBeSet then + ParentBiDiModeChanged; + if lParentColorSet then + begin + Include(FOptions, coStyleColor); // Issue #919 + ParentColorChanged(); + end; + + if coAutoSpring in ToBeSet then + FSpringRest := 0; + + if coVisible in ToBeCleared then + Header.UpdateMainColumn(); // Fixes issue #946 + + if ((coFixed in ToBeSet) or (coFixed in ToBeCleared)) and (coVisible in FOptions) then + Header.RescaleHeader; + + Changed(False); + // Need to repaint and adjust the owner tree too. + if not (csLoading in TreeView.ComponentState) and (VisibleChanged or lParentColorSet) and (Owner.UpdateCount = 0) and TreeView.HandleAllocated then + begin + TreeView.Invalidate(); + if VisibleChanged then + begin + TreeView.DoColumnVisibilityChanged(Self.Index, coVisible in ToBeSet); + TreeView.UpdateHorizontalScrollBar(False); + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetPosition(Value : TColumnPosition); + +var + Temp : TColumnIndex; + +begin + if (csLoading in TreeView.ComponentState) or (Owner.UpdateCount > 0) then + // Only cache the position for final fixup when loading from DFM. + FPosition := Value + else + begin + if Value >= TColumnPosition(Collection.Count) then + Value := Collection.Count - 1; + if FPosition <> Value then + begin + with Owner do + begin + InitializePositionArray; + TreeView.CancelEditNode; + AdjustPosition(Self, Value); + Self.Changed(False); + + // Need to repaint. + with Self.Header do + begin + if (UpdateCount = 0) and TreeView.HandleAllocated then + begin + Invalidate(Self); + TreeView.Invalidate; + end; + end; + end; + + // If the moved column is now within the fixed columns then we make it fixed as well. If it's not + // we clear the fixed state (in case that fixed column is moved outside fixed area). + if (coFixed in FOptions) and (FPosition > 0) then + Temp := Owner.ColumnFromPosition(FPosition - 1) + else + Temp := Owner.ColumnFromPosition(FPosition + 1); + + if Temp <> NoColumn then + begin + if coFixed in Owner[Temp].Options then + Options := Options + [coFixed] + else + Options := Options - [coFixed]; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetSpacing(Value : Integer); + +begin + if FSpacing <> Value then + begin + FSpacing := Value; + Changed(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetStyle(Value : TVirtualTreeColumnStyle); + +begin + if FStyle <> Value then + begin + FStyle := Value; + Changed(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetText(const Value : string); + +begin + if FText <> Value then + begin + FText := Value; + FCaptionText := ''; + Changed(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SetWidth(Value : Integer); + +var + EffectiveMaxWidth, + EffectiveMinWidth, + TotalFixedMaxWidth, + TotalFixedMinWidth : Integer; + I : TColumnIndex; + +begin + if not (hsScaling in Header.States) then + if ([coVisible, coFixed] * FOptions = [coVisible, coFixed]) then + begin + with Header, FixedAreaConstraints, TreeView do + begin + TotalFixedMinWidth := 0; + TotalFixedMaxWidth := 0; + for I := 0 to Columns.Count - 1 do + if ([coVisible, coFixed] * Columns[I].Options = [coVisible, coFixed]) then + begin + Inc(TotalFixedMaxWidth, Columns[I].MaxWidth); + Inc(TotalFixedMinWidth, Columns[I].MinWidth); + end; + + // The percentage values have precedence over the pixel values. + If MaxWidthPercent > 0 then + TotalFixedMinWidth := Min((ClientWidth * MaxWidthPercent) div 100, TotalFixedMinWidth); + If MinWidthPercent > 0 then + TotalFixedMaxWidth := Max((ClientWidth * MinWidthPercent) div 100, TotalFixedMaxWidth); + + EffectiveMaxWidth := Min(TotalFixedMaxWidth - (Columns.GetVisibleFixedWidth - Self.FWidth), FMaxWidth); + EffectiveMinWidth := Max(TotalFixedMinWidth - (Columns.GetVisibleFixedWidth - Self.FWidth), FMinWidth); + Value := Min(Max(Value, EffectiveMinWidth), EffectiveMaxWidth); + + if MinWidthPercent > 0 then + Value := Max((ClientWidth * MinWidthPercent) div 100 - Columns.GetVisibleFixedWidth + Self.FWidth, Value); + if MaxWidthPercent > 0 then + Value := Min((ClientWidth * MaxWidthPercent) div 100 - Columns.GetVisibleFixedWidth + Self.FWidth, Value); + end; + end + else + Value := Min(Max(Value, FMinWidth), FMaxWidth); + + if FWidth <> Value then + begin + FLastWidth := FWidth; + if not (hsResizing in Header.States) then + FBonusPixel := False; + if not (hoAutoResize in Header.Options) or (Index <> Header.AutoSizeIndex) then + begin + FWidth := Value; + Owner.UpdatePositions; + end; + if not (csLoading in TreeView.ComponentState) and (Owner.UpdateCount = 0) then + begin + if hoAutoResize in Header.Options then + Owner.AdjustAutoSize(Index); + TreeView.DoColumnResize(Index); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.ChangeScale(M, D : TDimension; isDpiChange : Boolean); +begin + FMinWidth := MulDiv(FMinWidth, M, D); + FMaxWidth := MulDiv(FMaxWidth, M, D); + FSpacing := MulDiv(FSpacing, M, D); + Self.Width := MulDiv(Self.Width, M, D); +end; + +procedure TVirtualTreeColumn.ComputeHeaderLayout(var PaintInfo : THeaderPaintInfo; DrawFormat : Cardinal; CalculateTextRect : Boolean = False); + +// The layout of a column header is determined by a lot of factors. This method takes them all into account and +// determines all necessary positions and bounds: +// - for the header text +// - the header glyph +// - the sort glyph + +var + TextSize : TSize; + TextPos, + ClientSize, + HeaderGlyphSize : TPoint; + CurrentAlignment : TAlignment; + MinLeft, + MaxRight, + TextSpacing : Integer; + UseText : Boolean; + R : TRect; + Theme : HTHEME; + +begin + UseText := Length(FText) > 0; + // If nothing is to show then don't waste time with useless preparation. + if not (UseText or PaintInfo.ShowHeaderGlyph or PaintInfo.ShowSortGlyph) then + Exit; + + CurrentAlignment := CaptionAlignment; + if FBiDiMode <> bdLeftToRight then + ChangeBiDiModeAlignment(CurrentAlignment); + + // Calculate sizes of the involved items. + ClientSize := Point(PaintInfo.PaintRectangle.Right - PaintInfo.PaintRectangle.Left, PaintInfo.PaintRectangle.Bottom - PaintInfo.PaintRectangle.Top); + with Owner, Header do + begin + if PaintInfo.ShowHeaderGlyph then + if not FCheckBox then + HeaderGlyphSize := Point(Images.Width, Images.Height) + else + with Self.TreeView do + begin + if Assigned(CheckImages) then + HeaderGlyphSize := Point(CheckImages.Width, CheckImages.Height); + end + else + HeaderGlyphSize := Point(0, 0); + if PaintInfo.ShowSortGlyph then + begin + if tsUseExplorerTheme in Self.TreeView.TreeStates then + begin + R := Rect(0, 0, 100, 100); + Theme := OpenThemeData(TreeView.Handle, 'HEADER'); + GetThemePartSize(Theme, PaintInfo.TargetCanvas.Handle, HP_HEADERSORTARROW, HSAS_SORTEDUP, @R, TS_TRUE, PaintInfo.SortGlyphSize); + CloseThemeData(Theme); + end + else + begin + PaintInfo.SortGlyphSize.cx := Self.TreeView.ScaledPixels(16); + PaintInfo.SortGlyphSize.cy := Self.TreeView.ScaledPixels(4); + end; + + // In any case, the sort glyph is vertically centered. + PaintInfo.SortGlyphPos.Y := (ClientSize.Y - PaintInfo.SortGlyphSize.cy) div 2; + end + else + begin + PaintInfo.SortGlyphSize.cx := 0; + PaintInfo.SortGlyphSize.cy := 0; + end; + end; + + if UseText then + begin + if not (coWrapCaption in FOptions) then + begin + FCaptionText := FText; + GetTextExtentPoint32W(PaintInfo.TargetCanvas.Handle, PWideChar(FText), Length(FText), TextSize); + Inc(TextSize.cx, 2); + PaintInfo.TextRectangle := Rect(0, 0, TextSize.cx, TextSize.cy); + end + else + begin + R := PaintInfo.PaintRectangle; + if FCaptionText = '' then + FCaptionText := WrapString(PaintInfo.TargetCanvas.Handle, FText, R, DT_RTLREADING and DrawFormat <> 0, DrawFormat); + + GetStringDrawRect(PaintInfo.TargetCanvas.Handle, FCaptionText, R, DrawFormat); + TextSize.cx := PaintInfo.PaintRectangle.Right - PaintInfo.PaintRectangle.Left; + TextSize.cy := R.Bottom - R.Top; + PaintInfo.TextRectangle := Rect(0, 0, TextSize.cx, TextSize.cy); + end; + TextSpacing := FSpacing; + end + else + begin + TextSpacing := 0; + TextSize.cx := 0; + TextSize.cy := 0; + end; + + // Check first for the special case where nothing is shown except the sort glyph. + if PaintInfo.ShowSortGlyph and not (UseText or PaintInfo.ShowHeaderGlyph) then + begin + // Center the sort glyph in the available area if nothing else is there. + PaintInfo.SortGlyphPos := Point((ClientSize.X - PaintInfo.SortGlyphSize.cx) div 2, (ClientSize.Y - PaintInfo.SortGlyphSize.cy) div 2); + end + else + begin + // Determine extents of text and glyph and calculate positions which are clear from the layout. + if (Layout in [blGlyphLeft, blGlyphRight]) or not PaintInfo.ShowHeaderGlyph then + begin + PaintInfo.GlyphPos.Y := (ClientSize.Y - HeaderGlyphSize.Y) div 2; + // If the text is taller than the given height, perform no vertical centration as this + // would make the text even less readable. + //Using Max() fixes badly positioned text if Extra Large fonts have been activated in the Windows display options + TextPos.Y := Max( - 5, (ClientSize.Y - TextSize.cy) div 2); + end + else + begin + if Layout = blGlyphTop then + begin + PaintInfo.GlyphPos.Y := (ClientSize.Y - HeaderGlyphSize.Y - TextSize.cy - TextSpacing) div 2; + TextPos.Y := PaintInfo.GlyphPos.Y + HeaderGlyphSize.Y + TextSpacing; + end + else + begin + TextPos.Y := (ClientSize.Y - HeaderGlyphSize.Y - TextSize.cy - TextSpacing) div 2; + PaintInfo.GlyphPos.Y := TextPos.Y + TextSize.cy + TextSpacing; + end; + end; + + // Each alignment needs special consideration. + case CurrentAlignment of + taLeftJustify : + begin + MinLeft := FMargin; + if PaintInfo.ShowSortGlyph and (FBiDiMode <> bdLeftToRight) then + begin + // In RTL context is the sort glyph placed on the left hand side. + PaintInfo.SortGlyphPos.X := MinLeft; + Inc(MinLeft, PaintInfo.SortGlyphSize.cx + FSpacing); + end; + if Layout in [blGlyphTop, blGlyphBottom] then + begin + // Header glyph is above or below text, so both must be considered when calculating + // the left positition of the sort glyph (if it is on the right hand side). + TextPos.X := MinLeft; + if PaintInfo.ShowHeaderGlyph then + begin + PaintInfo.GlyphPos.X := (ClientSize.X - HeaderGlyphSize.X) div 2; + if PaintInfo.GlyphPos.X < MinLeft then + PaintInfo.GlyphPos.X := MinLeft; + MinLeft := Max(TextPos.X + TextSize.cx + TextSpacing, PaintInfo.GlyphPos.X + HeaderGlyphSize.X + FSpacing); + end + else + MinLeft := TextPos.X + TextSize.cx + TextSpacing; + end + else + begin + // Everything is lined up. TextSpacing might be 0 if there is no text. + // This simplifies the calculation because no extra tests are necessary. + if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphLeft) then + begin + PaintInfo.GlyphPos.X := MinLeft; + Inc(MinLeft, HeaderGlyphSize.X + FSpacing); + end; + TextPos.X := MinLeft; + Inc(MinLeft, TextSize.cx + TextSpacing); + if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphRight) then + begin + PaintInfo.GlyphPos.X := MinLeft; + Inc(MinLeft, HeaderGlyphSize.X + FSpacing); + end; + end; + if PaintInfo.ShowSortGlyph and (FBiDiMode = bdLeftToRight) then + PaintInfo.SortGlyphPos.X := MinLeft; + end; + taCenter : + begin + if Layout in [blGlyphTop, blGlyphBottom] then + begin + PaintInfo.GlyphPos.X := (ClientSize.X - HeaderGlyphSize.X) div 2; + TextPos.X := (ClientSize.X - TextSize.cx) div 2; + if PaintInfo.ShowSortGlyph then + Dec(TextPos.X, PaintInfo.SortGlyphSize.cx div 2); + end + else + begin + MinLeft := (ClientSize.X - HeaderGlyphSize.X - TextSpacing - TextSize.cx) div 2; + if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphLeft) then + begin + PaintInfo.GlyphPos.X := MinLeft; + Inc(MinLeft, HeaderGlyphSize.X + TextSpacing); + end; + TextPos.X := MinLeft; + Inc(MinLeft, TextSize.cx + TextSpacing); + if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphRight) then + PaintInfo.GlyphPos.X := MinLeft; + end; + if PaintInfo.ShowHeaderGlyph then + begin + MinLeft := Min(PaintInfo.GlyphPos.X, TextPos.X); + MaxRight := Max(PaintInfo.GlyphPos.X + HeaderGlyphSize.X, TextPos.X + TextSize.cx); + end + else + begin + MinLeft := TextPos.X; + MaxRight := TextPos.X + TextSize.cx; + end; + // Place the sort glyph directly to the left or right of the larger item. + if PaintInfo.ShowSortGlyph then + if FBiDiMode = bdLeftToRight then + begin + // Sort glyph on the right hand side. + PaintInfo.SortGlyphPos.X := MaxRight + FSpacing; + end + else + begin + // Sort glyph on the left hand side. + PaintInfo.SortGlyphPos.X := MinLeft - FSpacing - PaintInfo.SortGlyphSize.cx; + end; + end; + else + // taRightJustify + MaxRight := ClientSize.X - FMargin; + if PaintInfo.ShowSortGlyph and (FBiDiMode = bdLeftToRight) then + begin + // In LTR context is the sort glyph placed on the right hand side. + Dec(MaxRight, PaintInfo.SortGlyphSize.cx); + PaintInfo.SortGlyphPos.X := MaxRight; + Dec(MaxRight, FSpacing); + end; + if Layout in [blGlyphTop, blGlyphBottom] then + begin + TextPos.X := MaxRight - TextSize.cx; + if PaintInfo.ShowHeaderGlyph then + begin + PaintInfo.GlyphPos.X := (ClientSize.X - HeaderGlyphSize.X) div 2; + if PaintInfo.GlyphPos.X + HeaderGlyphSize.X + FSpacing > MaxRight then + PaintInfo.GlyphPos.X := MaxRight - HeaderGlyphSize.X - FSpacing; + MaxRight := Min(TextPos.X - TextSpacing, PaintInfo.GlyphPos.X - FSpacing); + end + else + MaxRight := TextPos.X - TextSpacing; + end + else + begin + // Everything is lined up. TextSpacing might be 0 if there is no text. + // This simplifies the calculation because no extra tests are necessary. + if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphRight) then + begin + PaintInfo.GlyphPos.X := MaxRight - HeaderGlyphSize.X; + MaxRight := PaintInfo.GlyphPos.X - FSpacing; + end; + TextPos.X := MaxRight - TextSize.cx; + MaxRight := TextPos.X - TextSpacing; + if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphLeft) then + begin + PaintInfo.GlyphPos.X := MaxRight - HeaderGlyphSize.X; + MaxRight := PaintInfo.GlyphPos.X - FSpacing; + end; + end; + if PaintInfo.ShowSortGlyph and (FBiDiMode <> bdLeftToRight) then + PaintInfo.SortGlyphPos.X := MaxRight - PaintInfo.SortGlyphSize.cx; + end; + end; + + // Once the position of each element is determined there remains only one but important step. + // The horizontal positions of every element must be adjusted so that it always fits into the + // given header area. This is accomplished by shorten the text appropriately. + + // These are the maximum bounds. Nothing goes beyond them. + MinLeft := FMargin; + MaxRight := ClientSize.X - FMargin; + if PaintInfo.ShowSortGlyph then + begin + if FBiDiMode = bdLeftToRight then + begin + // Sort glyph on the right hand side. + if PaintInfo.SortGlyphPos.X + PaintInfo.SortGlyphSize.cx > MaxRight then + PaintInfo.SortGlyphPos.X := MaxRight - PaintInfo.SortGlyphSize.cx; + MaxRight := PaintInfo.SortGlyphPos.X - FSpacing; + end; + + // Consider also the left side of the sort glyph regardless of the bidi mode. + if PaintInfo.SortGlyphPos.X < MinLeft then + PaintInfo.SortGlyphPos.X := MinLeft; + // Left border needs only adjustment if the sort glyph marks the left border. + if FBiDiMode <> bdLeftToRight then + MinLeft := PaintInfo.SortGlyphPos.X + PaintInfo.SortGlyphSize.cx + FSpacing; + + // Finally transform sort glyph to its actual position. + Inc(PaintInfo.SortGlyphPos.X, PaintInfo.PaintRectangle.Left); + Inc(PaintInfo.SortGlyphPos.Y, PaintInfo.PaintRectangle.Top); + end; + if PaintInfo.ShowHeaderGlyph then + begin + if PaintInfo.GlyphPos.X + HeaderGlyphSize.X > MaxRight then + PaintInfo.GlyphPos.X := MaxRight - HeaderGlyphSize.X; + if Layout = blGlyphRight then + MaxRight := PaintInfo.GlyphPos.X - FSpacing; + if PaintInfo.GlyphPos.X < MinLeft then + PaintInfo.GlyphPos.X := MinLeft; + if Layout = blGlyphLeft then + MinLeft := PaintInfo.GlyphPos.X + HeaderGlyphSize.X + FSpacing; + if FCheckBox and (Header.MainColumn = Self.Index) then + Dec(PaintInfo.GlyphPos.X, 2) + else + if Header.MainColumn <> Self.Index then + Dec(PaintInfo.GlyphPos.X, 2); + + // Finally transform header glyph to its actual position. + Inc(PaintInfo.GlyphPos.X, PaintInfo.PaintRectangle.Left); + Inc(PaintInfo.GlyphPos.Y, PaintInfo.PaintRectangle.Top); + end; + if UseText then + begin + if TextPos.X < MinLeft then + TextPos.X := MinLeft; + OffsetRect(PaintInfo.TextRectangle, TextPos.X, TextPos.Y); + if PaintInfo.TextRectangle.Right > MaxRight then + PaintInfo.TextRectangle.Right := MaxRight; + OffsetRect(PaintInfo.TextRectangle, PaintInfo.PaintRectangle.Left, PaintInfo.PaintRectangle.Top); + + if coWrapCaption in FOptions then + begin + // Wrap the column caption if necessary. + R := PaintInfo.TextRectangle; + FCaptionText := WrapString(PaintInfo.TargetCanvas.Handle, FText, R, DT_RTLREADING and DrawFormat <> 0, DrawFormat); + GetStringDrawRect(PaintInfo.TargetCanvas.Handle, FCaptionText, R, DrawFormat); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.DefineProperties(Filer : TFiler); + +begin + inherited; + + // These properites are remains from non-Unicode Delphi versions, readers remain for backward compatibility. + Filer.DefineProperty('WideText', ReadText, nil, False); + Filer.DefineProperty('WideHint', ReadHint, nil, False); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.GetAbsoluteBounds(var Left, Right : Integer); + +// Returns the column's left and right bounds in header coordinates, that is, independant of the scrolling position. + +begin + Left := FLeft; + Right := FLeft + FWidth; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.GetDisplayName : string; + +// Returns the column text if it only contains ANSI characters, otherwise the column id is returned because the IDE +// still cannot handle Unicode strings. + +var + I : Integer; + +begin + // Check if the text of the column contains characters > 255 + I := 1; + while I <= Length(FText) do + begin + if Ord(FText[I]) > 255 then + Break; + Inc(I); + end; + + if I > Length(FText) then + Result := FText // implicit conversion + else + Result := Format('Column %d', [Index]); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.GetOwner : TVirtualTreeColumns; + +begin + Result := Collection as TVirtualTreeColumns; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.InternalSetWidth(const Value : TDimension); +begin + FWidth := Value; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.ReadText(Reader : TReader); + +begin + case Reader.NextValue of + vaLString, vaString : + SetText(Reader.ReadString); + else + SetText(Reader.ReadString); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.ReadHint(Reader : TReader); + +begin + case Reader.NextValue of + vaLString, vaString : + FHint := Reader.ReadString; + else + FHint := Reader.ReadString; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.Assign(Source : TPersistent); + +var + OldOptions : TVTColumnOptions; + +begin + if Source is TVirtualTreeColumn then + begin + OldOptions := FOptions; + FOptions := []; + + BiDiMode := TVirtualTreeColumn(Source).BiDiMode; + ImageIndex := TVirtualTreeColumn(Source).ImageIndex; + Layout := TVirtualTreeColumn(Source).Layout; + Margin := TVirtualTreeColumn(Source).Margin; + MaxWidth := TVirtualTreeColumn(Source).MaxWidth; + MinWidth := TVirtualTreeColumn(Source).MinWidth; + Position := TVirtualTreeColumn(Source).Position; + Spacing := TVirtualTreeColumn(Source).Spacing; + Style := TVirtualTreeColumn(Source).Style; + Text := TVirtualTreeColumn(Source).Text; + Hint := TVirtualTreeColumn(Source).Hint; + Width := TVirtualTreeColumn(Source).Width; + Alignment := TVirtualTreeColumn(Source).Alignment; + CaptionAlignment := TVirtualTreeColumn(Source).CaptionAlignment; + Color := TVirtualTreeColumn(Source).Color; + Tag := TVirtualTreeColumn(Source).Tag; + EditOptions := TVirtualTreeColumn(Source).EditOptions; + EditNextColumn := TVirtualTreeColumn(Source).EditNextColumn; + + // Order is important. Assign options last. + FOptions := OldOptions; + Options := TVirtualTreeColumn(Source).Options; + + Changed(False); + end + else + inherited Assign(Source); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.Equals(OtherColumnObj : TObject) : Boolean; +var + OtherColumn : TVirtualTreeColumn; +begin + if OtherColumnObj is TVirtualTreeColumn then + begin + OtherColumn := TVirtualTreeColumn(OtherColumnObj); + Result := (BiDiMode = OtherColumn.BiDiMode) and + (ImageIndex = OtherColumn.ImageIndex) and + (Layout = OtherColumn.Layout) and + (Margin = OtherColumn.Margin) and + (MaxWidth = OtherColumn.MaxWidth) and + (MinWidth = OtherColumn.MinWidth) and + (Position = OtherColumn.Position) and + (Spacing = OtherColumn.Spacing) and + (Style = OtherColumn.Style) and + (Text = OtherColumn.Text) and + (Hint = OtherColumn.Hint) and + (Width = OtherColumn.Width) and + (Alignment = OtherColumn.Alignment) and + (CaptionAlignment = OtherColumn.CaptionAlignment) and + (Color = OtherColumn.Color) and + (Tag = OtherColumn.Tag) and + (Options = OtherColumn.Options); + end + else + Result := False; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.GetRect : TRect; + +// Returns the rectangle this column occupies in the header (relative to (0, 0) of the non-client area). + +begin + with TVirtualTreeColumns(GetOwner).FHeader do + Result := TreeView.HeaderRect; + Inc(Result.Left, FLeft); + Result.Right := Result.Left + FWidth; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +// [IPK] +function TVirtualTreeColumn.GetText : string; + +begin + Result := FText; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.LoadFromStream(const Stream : TStream; Version : Integer); +var + Dummy : Integer; + S : string; + +begin + with Stream do + begin + ReadBuffer(Dummy, SizeOf(Dummy)); + SetLength(S, Dummy); + ReadBuffer(PWideChar(S)^, 2 * Dummy); + Text := S; + ReadBuffer(Dummy, SizeOf(Dummy)); + SetLength(FHint, Dummy); + ReadBuffer(PWideChar(FHint)^, 2 * Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + Width := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + MinWidth := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + MaxWidth := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + Style := TVirtualTreeColumnStyle(Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + ImageIndex := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + Layout := TVTHeaderColumnLayout(Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + Margin := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + Spacing := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + BiDiMode := TBiDiMode(Dummy); + + ReadBuffer(Dummy, SizeOf(Dummy)); + if Version >= 3 then + Options := TVTColumnOptions(Dummy); + + if Version > 0 then + begin + // Parts which have been introduced/changed with header stream version 1+. + ReadBuffer(Dummy, SizeOf(Dummy)); + Tag := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + Alignment := TAlignment(Dummy); + + if Version > 1 then + begin + ReadBuffer(Dummy, SizeOf(Dummy)); + Color := TColor(Dummy); + end; + + if Version > 5 then + begin + if coUseCaptionAlignment in FOptions then + begin + ReadBuffer(Dummy, SizeOf(Dummy)); + CaptionAlignment := TAlignment(Dummy); + end; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.ParentBiDiModeChanged; + +var + Columns : TVirtualTreeColumns; + +begin + if coParentBidiMode in FOptions then + begin + Columns := GetOwner as TVirtualTreeColumns; + if Assigned(Columns) and (FBiDiMode <> TreeView.BiDiMode) then + begin + FBiDiMode := TreeView.BiDiMode; + Changed(False); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.ParentColorChanged; + +var + Columns : TVirtualTreeColumns; + +begin + if coParentColor in FOptions then + begin + Columns := GetOwner as TVirtualTreeColumns; + if Assigned(Columns) and (FColor <> TreeView.Color) then + begin + FColor := TreeView.Color; + Changed(False); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.RestoreLastWidth; + +begin + TVirtualTreeColumns(GetOwner).AnimatedResize(Index, FLastWidth); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumn.SaveToStream(const Stream : TStream); + +var + Dummy : Integer; + +begin + with Stream do + begin + Dummy := Length(FText); + WriteBuffer(Dummy, SizeOf(Dummy)); + WriteBuffer(PWideChar(FText)^, 2 * Dummy); + Dummy := Length(FHint); + WriteBuffer(Dummy, SizeOf(Dummy)); + WriteBuffer(PWideChar(FHint)^, 2 * Dummy); + WriteBuffer(FWidth, SizeOf(FWidth)); + WriteBuffer(FMinWidth, SizeOf(FMinWidth)); + WriteBuffer(FMaxWidth, SizeOf(FMaxWidth)); + Dummy := Ord(FStyle); + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := FImageIndex; + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Ord(FLayout); + WriteBuffer(Dummy, SizeOf(Dummy)); + WriteBuffer(FMargin, SizeOf(FMargin)); + WriteBuffer(FSpacing, SizeOf(FSpacing)); + Dummy := Ord(FBiDiMode); + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Integer(FOptions); + WriteBuffer(Dummy, SizeOf(Dummy)); + + // parts introduced with stream version 1 + WriteBuffer(FTag, SizeOf(Dummy)); + Dummy := Cardinal(FAlignment); + WriteBuffer(Dummy, SizeOf(Dummy)); + + // parts introduced with stream version 2 + Dummy := Integer(FColor); + WriteBuffer(Dummy, SizeOf(Dummy)); + + // parts introduced with stream version 6 + if coUseCaptionAlignment in FOptions then + begin + Dummy := Cardinal(FCaptionAlignment); + WriteBuffer(Dummy, SizeOf(Dummy)); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumn.UseRightToLeftReading : Boolean; + +begin + Result := FBiDiMode <> bdLeftToRight; +end; + +//----------------- TVirtualTreeColumns -------------------------------------------------------------------------------- + +constructor TVirtualTreeColumns.Create(AOwner : TPersistent); + +var + ColumnClass : TVirtualTreeColumnClass; + +begin + FHeader := AOwner; + + // Determine column class to be used in the header. + ColumnClass := Self.TreeView.GetColumnClass; + // The owner tree always returns the default tree column class if not changed by application/descendants. + inherited Create(ColumnClass); + + FHeaderBitmap := TBitmap.Create; + FHeaderBitmap.PixelFormat := pf32Bit; + + FHoverIndex := NoColumn; + FDownIndex := NoColumn; + FClickIndex := NoColumn; + FDropTarget := NoColumn; + FTrackIndex := NoColumn; + FDefaultWidth := 50; + Self.FColumnPopupMenu := nil; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +destructor TVirtualTreeColumns.Destroy; + +begin + FreeAndNil(FColumnPopupMenu); + FreeAndNil(FHeaderBitmap); + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetCount : Integer; + +begin + Result := inherited Count; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetItem(Index : TColumnIndex) : TVirtualTreeColumn; + +begin + Result := TVirtualTreeColumn(inherited GetItem(Index)); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetNewIndex(P : TPoint; var OldIndex : TColumnIndex) : Boolean; + +var + NewIndex : Integer; + +begin + Result := False; + // convert to local coordinates + Inc(P.Y, Header.Height); + NewIndex := ColumnFromPosition(P); + if NewIndex <> OldIndex then + begin + if OldIndex > NoColumn then + Header.Invalidate(Items[OldIndex], False, True); + OldIndex := NewIndex; + if OldIndex > NoColumn then + Header.Invalidate(Items[OldIndex], False, True); + Result := True; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.SetDefaultWidth(Value : Integer); + +begin + FDefaultWidth := Value; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.SetItem(Index : TColumnIndex; Value : TVirtualTreeColumn); + +begin + inherited SetItem(Index, Value); +end; + +function TVirtualTreeColumns.StyleServices(AControl : TControl) : TCustomStyleServices; +begin + if AControl = nil then + AControl := TreeView; + Result := VTStyleServices(AControl); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.AdjustAutoSize(CurrentIndex : TColumnIndex; Force : Boolean = False); + +// Called only if the header is in auto-size mode which means a column needs to be so large +// that it fills all the horizontal space not occupied by the other columns. +// CurrentIndex (if not InvalidColumn) describes which column has just been resized. + +var + NewValue, + AutoIndex, + Index, + RestWidth : Integer; + WasUpdating : Boolean; +begin + if Count > 0 then + begin + // Determine index to be used for auto resizing. This is usually given by the owner's AutoSizeIndex, but + // could be different if the column whose resize caused the invokation here is either the auto column itself + // or visually to the right of the auto size column. + AutoIndex := Header.AutoSizeIndex; + if (AutoIndex < 0) or (AutoIndex >= Count) then + AutoIndex := Count - 1; + + if AutoIndex >= 0 then + begin + with TreeView do + begin + if HandleAllocated then + RestWidth := ClientWidth + else + RestWidth := Width; + end; + + // Go through all columns and calculate the rest space remaining. + for Index := 0 to Count - 1 do + if (Index <> AutoIndex) and (coVisible in Items[Index].Options) then + Dec(RestWidth, Items[Index].Width); + + with Items[AutoIndex] do + begin + NewValue := Max(MinWidth, Min(MaxWidth, RestWidth)); + if Force or (FWidth <> NewValue) then + begin + FWidth := NewValue; + UpdatePositions; + WasUpdating := csUpdating in TreeView.ComponentState; + if not WasUpdating then + TreeView.Updating(); // Fixes #398 + try + TreeView.DoColumnResize(AutoIndex); + finally + if not WasUpdating then + TreeView.Updated(); + end; + end; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.AdjustDownColumn(P : TPoint) : TColumnIndex; + +// Determines the column from the given position and returns it. If this column is allowed to be clicked then +// it is also kept for later use. + +begin + // Convert to local coordinates. + Inc(P.Y, Header.Height); + Result := ColumnFromPosition(P); + if (Result > NoColumn) and (Result <> FDownIndex) and (coAllowClick in Items[Result].Options) and + (coEnabled in Items[Result].Options) then + begin + if FDownIndex > NoColumn then + Header.Invalidate(Items[FDownIndex]); + FDownIndex := Result; + FCheckBoxHit := Items[Result].HasImage and PtInRect(Items[Result].ImageRect, P) and Items[Result].CheckBox; + Header.Invalidate(Items[FDownIndex]); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.AdjustHoverColumn(P : TPoint) : Boolean; + +// Determines the new hover column index and returns True if the index actually changed else False. + +begin + Result := GetNewIndex(P, FHoverIndex); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.AdjustPosition(Column : TVirtualTreeColumn; Position : Cardinal); + +// Reorders the column position array so that the given column gets the given position. + +var + OldPosition : Cardinal; + +begin + OldPosition := Column.Position; + if OldPosition <> Position then + begin + if OldPosition < Position then + begin + // column will be moved up so move down other entries + Move(FPositionToIndex[OldPosition + 1], FPositionToIndex[OldPosition], (Position - OldPosition) * SizeOf(Cardinal)); + end + else + begin + // column will be moved down so move up other entries + Move(FPositionToIndex[Position], FPositionToIndex[Position + 1], (OldPosition - Position) * SizeOf(Cardinal)); + end; + FPositionToIndex[Position] := Column.Index; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.CanSplitterResize(P : TPoint; Column : TColumnIndex) : Boolean; + +begin + Result := (Column > NoColumn) and ([coResizable, coVisible] * Items[Column].Options = [coResizable, coVisible]); + DoCanSplitterResize(P, Column, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.DoCanSplitterResize(P : TPoint; Column : TColumnIndex; var Allowed : Boolean); + +begin + if Assigned(TreeView.OnCanSplitterResizeColumn) then + TreeView.OnCanSplitterResizeColumn(Header, P, Column, Allowed); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.DrawButtonText(DC : HDC; Caption : string; Bounds : TRect; Enabled, Hot : Boolean; + DrawFormat : Cardinal; WrapCaption : Boolean); + +var + TextSpace : Integer; + Size : TSize; + +begin + if not WrapCaption then + begin + // Do we need to shorten the caption due to limited space? + GetTextExtentPoint32W(DC, PWideChar(Caption), Length(Caption), Size); + TextSpace := Bounds.Right - Bounds.Left; + if TextSpace < Size.cx then + Caption := ShortenString(DC, Caption, TextSpace); + end; + + SetBkMode(DC, TRANSPARENT); + if not Enabled then + if TreeView.VclStyleEnabled then + begin + SetTextColor(DC, ColorToRGB(TreeView.Colors.HeaderFontColor)); + WinApi.Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat); + end + else + begin + OffsetRect(Bounds, 1, 1); + SetTextColor(DC, ColorToRGB(clBtnHighlight)); + WinApi.Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat); + OffsetRect(Bounds, - 1, - 1); + SetTextColor(DC, ColorToRGB(clBtnShadow)); + WinApi.Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat); + end + else + begin + if Hot then + SetTextColor(DC, ColorToRGB(TreeView.Colors.HeaderHotColor)) + else + SetTextColor(DC, ColorToRGB(TreeView.Colors.HeaderFontColor)); + WinApi.Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.FixPositions; + +// Fixes column positions after loading from DFM or Bidi mode change. + +var + I : Integer; + +begin + for I := 0 to Count - 1 do + FPositionToIndex[Items[I].Position] := I; + + FNeedPositionsFix := False; + UpdatePositions(True); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetColumnAndBounds(P : TPoint; var ColumnLeft, ColumnRight : Integer; + Relative : Boolean = True) : Integer; + +// Returns the column where the mouse is currently in as well as the left and right bound of +// this column (Left and Right are undetermined if no column is involved). + +var + I : Integer; + +begin + Result := InvalidColumn; + if Relative and (P.X >= Header.Columns.GetVisibleFixedWidth) then + ColumnLeft := - TreeView.EffectiveOffsetX + else + ColumnLeft := 0; + + if TreeView.UseRightToLeftAlignment then + Inc(ColumnLeft, TreeView.ComputeRTLOffset(True)); + + for I := 0 to Count - 1 do + with Items[FPositionToIndex[I]] do + if coVisible in FOptions then + begin + ColumnRight := ColumnLeft + FWidth; + + //fix: in right to left alignment, X can be in the + //area on the left of first column which is OUT. + if (P.X < ColumnLeft) and (I = 0) then + begin + Result := InvalidColumn; + Exit; + end; + if P.X < ColumnRight then + begin + Result := FPositionToIndex[I]; + Exit; + end; + ColumnLeft := ColumnRight; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetOwner : TPersistent; + +begin + Result := FHeader; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.HandleClick(P : TPoint; Button : TMouseButton; Force, DblClick : Boolean) : Boolean; + +// Generates a click event if the mouse button has been released over the same column it was pressed first. +// Alternatively, Force might be set to True to indicate that the down index does not matter (right, middle and +// double click). +// Returns true if the click was handled, False otherwise. + +var + HitInfo : TVTHeaderHitInfo; + NewClickIndex : Integer; + Menu : TPopupMenu; +begin + Result := False; + if (csDesigning in TreeView.ComponentState) then + Exit; + // Convert vertical position to local coordinates. + Inc(P.Y, Header.Height); + NewClickIndex := ColumnFromPosition(P); + with HitInfo do + begin + X := P.X; + Y := P.Y; + Shift := Header.GetShiftState; + if DblClick then + Shift := Shift + [ssDouble]; + end; + HitInfo.Button := Button; + + if (NewClickIndex > NoColumn) and (coAllowClick in Items[NewClickIndex].Options) and + ((NewClickIndex = FDownIndex) or Force) then + begin + FClickIndex := NewClickIndex; + HitInfo.Column := NewClickIndex; + HitInfo.HitPosition := [hhiOnColumn]; + + if Items[NewClickIndex].HasImage and PtInRect(Items[NewClickIndex].ImageRect, P) then + begin + Include(HitInfo.HitPosition, hhiOnIcon); + if Items[NewClickIndex].CheckBox then + begin + if Button = mbLeft then + TreeView.UpdateColumnCheckState(Items[NewClickIndex]); + Include(HitInfo.HitPosition, hhiOnCheckbox); + end; + end; + end + else + begin + FClickIndex := NoColumn; + HitInfo.Column := NoColumn; + HitInfo.HitPosition := [hhiNoWhere]; + end; + + if DblClick then + TreeView.DoHeaderDblClick(HitInfo) + else + begin + if (hoHeaderClickAutoSort in Header.Options) and (HitInfo.Button = mbLeft) and not (hhiOnCheckbox in HitInfo.HitPosition) and (HitInfo.Column >= 0) then + begin + // handle automatic setting of SortColumn and toggling of the sort order + if HitInfo.Column <> Header.SortColumn then + begin + // set sort column + Header.DoSetSortColumn(HitInfo.Column, Self[HitInfo.Column].DefaultSortDirection); + end//if + else + begin + // toggle sort direction + if Header.SortDirection = sdDescending then + Header.SortDirection := sdAscending + else + Header.SortDirection := sdDescending; + end; //else + Result := True; + end; //if + + if (Button = mbRight) then + begin + Dec(P.Y, Header.Height); // popup menus at actual clicked point + FreeAndNil(FColumnPopupMenu); // Attention: Do not free the TVTHeaderPopupMenu at the end of this method, otherwise the clikc events of the menu item will not be fired. + Self.FDownIndex := NoColumn; + Self.FTrackIndex := NoColumn; + Self.FCheckBoxHit := False; + Menu := Header.DoGetPopupMenu(Self.ColumnFromPosition(Point(P.X, P.Y + Integer(TreeView.Height))), P); + if Assigned(Menu) then + begin + TreeView.StopTimer(ScrollTimer); + TreeView.StopTimer(HeaderTimer); + Header.Columns.SetHoverIndex(NoColumn); + TreeView.DoStateChange([], [tsScrollPending, tsScrolling]); + + Menu.PopupComponent := TreeView; + With TreeView.ClientToScreen(P) do + Menu.Popup(X, Y); + Result := True; + end + else if (hoAutoColumnPopupMenu in Header.Options) then + begin + FColumnPopupMenu := TVTHeaderPopupMenu.Create(TreeView); + TVTHeaderPopupMenu(FColumnPopupMenu).OnAddHeaderPopupItem := HeaderPopupMenuAddHeaderPopupItem; + TVTHeaderPopupMenu(FColumnPopupMenu).OnColumnChange := HeaderPopupMenuColumnChange; + FColumnPopupMenu.PopupComponent := TreeView; + if (hoDblClickResize in Header.Options) and ((TreeView.ChildCount[nil] > 0) or (hoAutoResizeInclCaption in Header.Options)) then + TVTHeaderPopupMenu(FColumnPopupMenu).Options := TVTHeaderPopupMenu(FColumnPopupMenu).Options + [poResizeToFitItem] + else + TVTHeaderPopupMenu(FColumnPopupMenu).Options := TVTHeaderPopupMenu(FColumnPopupMenu).Options - [poResizeToFitItem]; + With TreeView.ClientToScreen(P) do + FColumnPopupMenu.Popup(X, Y); + Result := True; + end; // if hoAutoColumnPopupMenu + end; //if mbRight + TreeView.DoHeaderClick(HitInfo); + end; //else (not DblClick) + + if not (hhiNoWhere in HitInfo.HitPosition) then + Header.Invalidate(Items[NewClickIndex]); + if (FClickIndex > NoColumn) and (FClickIndex <> NewClickIndex) then + Header.Invalidate(Items[FClickIndex]); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.HeaderPopupMenuAddHeaderPopupItem(const Sender : TObject; const Column : TColumnIndex; var Cmd : TAddPopupItemType); +begin + TBaseVirtualTreeCracker(Sender).DoHeaderAddPopupItem(Column, Cmd); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.HeaderPopupMenuColumnChange(const Sender : TObject; const Column : TColumnIndex; Visible : Boolean); +begin + TBaseVirtualTreeCracker(Sender).DoColumnVisibilityChanged(Column, Visible); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.IndexChanged(OldIndex, NewIndex : Integer); + +// Called by a column when its index in the collection changes. If NewIndex is -1 then the column is +// about to be removed, otherwise it is moved to a new index. +// The method will then update the position array to reflect the change. + +var + I : Integer; + Increment : Integer; + Lower, + Upper : Integer; + +begin + if NewIndex = - 1 then + begin + // Find position in the array with the old index. + Upper := High(FPositionToIndex); + for I := 0 to Upper do + begin + if FPositionToIndex[I] = OldIndex then + begin + // Index found. Move all higher entries one step down and remove the last entry. + if I < Upper then + Move(FPositionToIndex[I + 1], FPositionToIndex[I], (Upper - I) * SizeOf(TColumnIndex)); + end; + // Decrease all indices, which are greater than the index to be deleted. + if FPositionToIndex[I] > OldIndex then + Dec(FPositionToIndex[I]); + end; + SetLength(FPositionToIndex, High(FPositionToIndex)); + end + else + begin + if OldIndex < NewIndex then + Increment := - 1 + else + Increment := 1; + + Lower := Min(OldIndex, NewIndex); + Upper := Max(OldIndex, NewIndex); + for I := 0 to High(FPositionToIndex) do + begin + if (FPositionToIndex[I] >= Lower) and (FPositionToIndex[I] < Upper) then + Inc(FPositionToIndex[I], Increment) + else + if FPositionToIndex[I] = OldIndex then + FPositionToIndex[I] := NewIndex; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.InitializePositionArray; + +// Ensures that the column position array contains as many entries as columns are defined. +// The array is resized and initialized with default values if needed. + +var + I, OldSize : Integer; + Changed : Boolean; + +begin + if Count <> Length(FPositionToIndex) then + begin + OldSize := Length(FPositionToIndex); + SetLength(FPositionToIndex, Count); + if Count > OldSize then + begin + // New items have been added, just set their position to the same as their index. + for I := OldSize to Count - 1 do + FPositionToIndex[I] := I; + end + else + begin + // Items have been deleted, so reindex remaining entries by decrementing values larger than the highest + // possible index until no entry is higher than this limit. + repeat + Changed := False; + for I := 0 to Count - 1 do + if FPositionToIndex[I] >= Count then + begin + Dec(FPositionToIndex[I]); + Changed := True; + end; + until not Changed; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.Notify(Item : TCollectionItem; Action : System.Classes.TCollectionNotification); +var + I : Integer; +begin + if Action in [cnExtracting, cnDeleting] then + begin + // Adjust all positions larger than the deleted column's position. Fixes #959 + for I := 0 to Count - 1 do + begin + if Items[I].Position > TVirtualTreeColumn(Item).Position then + Items[I].Position := Items[I].Position - 1; + end; //for I + + with TreeView do + if not (csLoading in ComponentState) and (FocusedColumn = Item.Index) then + InternalSetFocusedColumn(NoColumn); //bypass side effects in SetFocusedColumn + end; // if cnDeleting +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.ReorderColumns(RTL : Boolean); + +var + I : Integer; + +begin + if RTL then + begin + for I := 0 to Count - 1 do + FPositionToIndex[I] := Count - I - 1; + end + else + begin + for I := 0 to Count - 1 do + FPositionToIndex[I] := I; + end; + + UpdatePositions(True); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.SetHoverIndex(Index : TColumnIndex); +begin + FHoverIndex := index; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.EndUpdate; +begin + InitializePositionArray(); + FixPositions(); // Accept the cuurent order. See issue #753 + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.Update(Item : TCollectionItem); + +begin + // This is the only place which gets notified when a new column has been added or removed + // and we need this event to adjust the column position array. + InitializePositionArray; + if csLoading in TreeView.ComponentState then + FNeedPositionsFix := True + else + UpdatePositions; + + // The first column which is created is by definition also the main column. + if (Count > 0) and (Header.MainColumn < 0) then + Header.MainColumn := 0; + + if not (csLoading in TreeView.ComponentState) and not (hsLoading in Header.States) then + begin + with Header do + begin + if hoAutoResize in Options then + AdjustAutoSize(InvalidColumn); + if Assigned(Item) then + Invalidate(Item as TVirtualTreeColumn) + else + if Self.TreeView.HandleAllocated then + begin + Self.TreeView.UpdateHorizontalScrollBar(False); + Invalidate(nil); + TreeView.Invalidate; + end; + + if not (Self.TreeView.IsUpdating) then + // This is mainly to let the designer know when a change occurs at design time which + // doesn't involve the object inspector (like column resizing with the mouse). + // This does NOT include design time code as the communication is done via an interface. + Self.TreeView.UpdateDesigner; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.UpdatePositions(Force : Boolean = False); + +// Recalculates the left border of every column and updates their position property according to the +// PostionToIndex array which primarily determines where each column is placed visually. + +var + I, RunningPos : Integer; + +begin + if not (csDestroying in TreeView.ComponentState) and not FNeedPositionsFix and (Force or (UpdateCount = 0)) then + begin + RunningPos := 0; + for I := 0 to High(FPositionToIndex) do + with Items[FPositionToIndex[I]] do + begin + FPosition := I; + FLeft := RunningPos; + if coVisible in FOptions then + Inc(RunningPos, FWidth); + end; + TreeView.UpdateHorizontalScrollBar(False); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.Add : TVirtualTreeColumn; + +begin + Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be changed in UI thread.'); + Result := TVirtualTreeColumn(inherited Add); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.AnimatedResize(Column : TColumnIndex; NewWidth : Integer); + +// Resizes the given column animated by scrolling the window DC. + +var + OldWidth : Integer; + DC : HDC; + I, + Steps, + DX : Integer; + HeaderScrollRect, + ScrollRect, + R : TRect; + + NewBrush, + LastBrush : HBRUSH; + +begin + if not IsValidColumn(Column) then + Exit; // Just in case. + + // Make sure the width constrains are considered. + if NewWidth < Items[Column].MinWidth then + NewWidth := Items[Column].MinWidth; + if NewWidth > Items[Column].MaxWidth then + NewWidth := Items[Column].MaxWidth; + + OldWidth := Items[Column].Width; + // Nothing to do if the width is the same. + if OldWidth <> NewWidth then + begin + if not ((hoDisableAnimatedResize in Header.Options) or + (coDisableAnimatedResize in Items[Column].Options)) then + begin + DC := GetWindowDC(TreeView.Handle); + with TreeView do + try + Steps := 32; + DX := (NewWidth - OldWidth) div Steps; + + // Determination of the scroll rectangle is a bit complicated since we neither want + // to scroll the scrollbars nor the border of the treeview window. + HeaderScrollRect := HeaderRect; + ScrollRect := HeaderScrollRect; + // Exclude the header itself from scrolling. + ScrollRect.Top := ScrollRect.Bottom; + ScrollRect.Bottom := ScrollRect.Top + ClientHeight; + ScrollRect.Right := ScrollRect.Left + ClientWidth; + with Items[Column] do + Inc(ScrollRect.Left, FLeft + FWidth); + HeaderScrollRect.Left := ScrollRect.Left; + HeaderScrollRect.Right := ScrollRect.Right; + + // When the new width is larger then avoid artefacts on the left hand side + // by deleting a small stripe + if NewWidth > OldWidth then + begin + R := ScrollRect; + NewBrush := CreateSolidBrush(ColorToRGB(Color)); + LastBrush := SelectObject(DC, NewBrush); + R.Right := R.Left + DX; + FillRect(DC, R, NewBrush); + SelectObject(DC, LastBrush); + DeleteObject(NewBrush); + end + else + begin + Inc(HeaderScrollRect.Left, DX); + Inc(ScrollRect.Left, DX); + end; + + for I := 0 to Steps - 1 do + begin + ScrollDC(DC, DX, 0, HeaderScrollRect, HeaderScrollRect, 0, nil); + Inc(HeaderScrollRect.Left, DX); + ScrollDC(DC, DX, 0, ScrollRect, ScrollRect, 0, nil); + Inc(ScrollRect.Left, DX); + Sleep(1); + end; + finally + ReleaseDC(Handle, DC); + end; + end; + Items[Column].Width := NewWidth; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.Assign(Source : TPersistent); + +begin + // Let the collection class assign the items. + inherited; + + if Source is TVirtualTreeColumns then + begin + // Copying the position array is the only needed task here. + FPositionToIndex := Copy(TVirtualTreeColumns(Source).FPositionToIndex, 0, MaxInt); + + // Make sure the left edges are correct after assignment. + FNeedPositionsFix := False; + UpdatePositions(True); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.Clear; + +begin + FClearing := True; + try + TreeView.CancelEditNode; + + // Since we're freeing all columns, the following have to be true when we're done. + FHoverIndex := NoColumn; + FDownIndex := NoColumn; + FTrackIndex := NoColumn; + FClickIndex := NoColumn; + FCheckBoxHit := False; + + with TVTHeaderCracker(Header) do + if not (hsLoading in States) then + begin + InternalSetAutoSizeIndex(NoColumn); //bypass side effects in SetAutoSizeColumn + MainColumn := NoColumn; + InternalSetSortColumn(NoColumn); //bypass side effects in SetSortColumn + end; + + with TreeView do + if not (csLoading in ComponentState) then + InternalSetFocusedColumn(NoColumn); //bypass side effects in SetFocusedColumn + + inherited Clear; + finally + FClearing := False; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.ColumnFromPosition(P : TPoint; Relative : Boolean = True) : TColumnIndex; + +// Determines the current column based on the position passed in P. + +var + I, Sum : Integer; + +begin + Result := InvalidColumn; + + // The position must be within the header area, but we extend the vertical bounds to the entire treeview area. + if (P.X >= 0) and (P.Y >= 0) and (P.Y <= TreeView.Height) then + with FHeader, TreeView do + begin + if Relative and (P.X >= GetVisibleFixedWidth) then + Sum := - EffectiveOffsetX + else + Sum := 0; + + if UseRightToLeftAlignment then + Inc(Sum, ComputeRTLOffset(True)); + + for I := 0 to Count - 1 do + if coVisible in Items[FPositionToIndex[I]].Options then + begin + Inc(Sum, Items[FPositionToIndex[I]].Width); + if P.X < Sum then + begin + Result := FPositionToIndex[I]; + Break; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.ColumnFromPosition(PositionIndex : TColumnPosition) : TColumnIndex; + +// Returns the index of the column at the given position. + +begin + if Integer(PositionIndex) < Length(FPositionToIndex) then + Result := FPositionToIndex[PositionIndex] + else + Result := NoColumn; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.Equals(OtherColumnsObj : TObject) : Boolean; + +// Compares itself with the given set of columns and returns True if all published properties are the same +// (including column order), otherwise False is returned. + +var + I : Integer; + OtherColumns : TVirtualTreeColumns; + +begin + if not (OtherColumnsObj is TVirtualTreeColumns) then + begin + Result := False; + Exit; + end; + + OtherColumns := TVirtualTreeColumns(OtherColumnsObj); + + // Same number of columns? + Result := OtherColumns.Count = Count; + if Result then + begin + // Same order of columns? + Result := CompareMem(Pointer(FPositionToIndex), Pointer(OtherColumns.FPositionToIndex), + Length(FPositionToIndex) * SizeOf(TColumnIndex)); + if Result then + begin + for I := 0 to Count - 1 do + if not Items[I].Equals(OtherColumns[I]) then + begin + Result := False; + Break; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.GetColumnBounds(Column : TColumnIndex; var Left, Right : Integer); + +// Returns the left and right bound of the given column. If Column is NoColumn then the entire client width is returned. + +begin + if Column <= NoColumn then + begin + Left := 0; + Right := TreeView.ClientWidth; + end + else + begin + Left := Items[Column].Left; + Right := Left + Items[Column].Width; + if TreeView.UseRightToLeftAlignment then + begin + Inc(Left, TreeView.ComputeRTLOffset(True)); + Inc(Right, TreeView.ComputeRTLOffset(True)); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetScrollWidth : Integer; + +// Returns the average width of all visible, non-fixed columns. If there is no such column the indent is returned. + +var + I : Integer; + ScrollColumnCount : Integer; + +begin + + Result := 0; + + ScrollColumnCount := 0; + for I := 0 to Header.Columns.Count - 1 do + begin + if ([coVisible, coFixed] * Header.Columns[I].Options = [coVisible]) then + begin + Inc(Result, Header.Columns[I].Width); + Inc(ScrollColumnCount); + end; + end; + + if ScrollColumnCount > 0 then // use average width + Result := Round(Result / ScrollColumnCount) + else // use indent + Result := Integer(TreeView.Indent); + +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetFirstVisibleColumn(ConsiderAllowFocus : Boolean = False) : TColumnIndex; + +// Returns the index of the first visible column or "InvalidColumn" if either no columns are defined or +// all columns are hidden. +// If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed. + +var + I : Integer; + +begin + Result := InvalidColumn; + if (UpdateCount > 0) or (csLoading in TreeView.ComponentState) then + Exit; // See issue #760 + for I := 0 to Count - 1 do + if (coVisible in Items[FPositionToIndex[I]].Options) and + ((not ConsiderAllowFocus) or + (coAllowFocus in Items[FPositionToIndex[I]].Options) + ) then + begin + Result := FPositionToIndex[I]; + Break; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetLastVisibleColumn(ConsiderAllowFocus : Boolean = False) : TColumnIndex; + +// Returns the index of the last visible column or "InvalidColumn" if either no columns are defined or +// all columns are hidden. +// If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed. + +var + I : Integer; + +begin + Result := InvalidColumn; + if (UpdateCount > 0) or (csLoading in TreeView.ComponentState) then + Exit; // See issue #760 + for I := Count - 1 downto 0 do + if (coVisible in Items[FPositionToIndex[I]].Options) and + ((not ConsiderAllowFocus) or + (coAllowFocus in Items[FPositionToIndex[I]].Options) + ) then + begin + Result := FPositionToIndex[I]; + Break; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetFirstColumn : TColumnIndex; + +// Returns the first column in display order. + +begin + if Count = 0 then + Result := InvalidColumn + else + Result := FPositionToIndex[0]; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetNextColumn(Column : TColumnIndex) : TColumnIndex; + +// Returns the next column in display order. Column is the index of an item in the collection (a column). + +var + Position : Integer; + +begin + if Column < 0 then + Result := InvalidColumn + else + begin + Position := Items[Column].Position; + if Position < Count - 1 then + Result := FPositionToIndex[Position + 1] + else + Result := InvalidColumn; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetNextVisibleColumn(Column : TColumnIndex; ConsiderAllowFocus : Boolean = False) : TColumnIndex; + +// Returns the next visible column in display order, Column is an index into the columns list. +// If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed. + +begin + Result := Column; + repeat + Result := GetNextColumn(Result); + until (Result = InvalidColumn) or + ((coVisible in Items[Result].Options) and + ((not ConsiderAllowFocus) or + (coAllowFocus in Items[Result].Options) + ) + ); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetPreviousColumn(Column : TColumnIndex) : TColumnIndex; + +// Returns the previous column in display order, Column is an index into the columns list. + +var + Position : Integer; + +begin + if Column < 0 then + Result := InvalidColumn + else + begin + Position := Items[Column].Position; + if Position > 0 then + Result := FPositionToIndex[Position - 1] + else + Result := InvalidColumn; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetPreviousVisibleColumn(Column : TColumnIndex; ConsiderAllowFocus : Boolean = False) : TColumnIndex; + +// Returns the previous visible column in display order, Column is an index into the columns list. +// If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed. + +begin + Result := Column; + repeat + Result := GetPreviousColumn(Result); + until (Result = InvalidColumn) or + ((coVisible in Items[Result].Options) and + ((not ConsiderAllowFocus) or + (coAllowFocus in Items[Result].Options) + ) + ); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetVisibleColumns : TColumnsArray; + +// Returns a list of all currently visible columns in actual order. + +var + I, Counter : Integer; + +begin + SetLength(Result, Count); + Counter := 0; + + for I := 0 to Count - 1 do + if coVisible in Items[FPositionToIndex[I]].Options then + begin + Result[Counter] := Items[FPositionToIndex[I]]; + Inc(Counter); + end; + // Set result length to actual visible count. + SetLength(Result, Counter); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.GetVisibleFixedWidth : Integer; + +// Determines the horizontal space all visible and fixed columns occupy. + +var + I : Integer; + +begin + Result := 0; + for I := 0 to Count - 1 do + begin + if Items[I].Options * [coVisible, coFixed] = [coVisible, coFixed] then + Inc(Result, Items[I].Width); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.IsValidColumn(Column : TColumnIndex) : Boolean; + +// Determines whether the given column is valid or not, that is, whether it is one of the current columns. + +begin + Result := (Column > NoColumn) and (Column < Count); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.LoadFromStream(const Stream : TStream; Version : Integer); + +var + I, + ItemCount : Integer; + +begin + Clear; + Stream.ReadBuffer(ItemCount, SizeOf(ItemCount)); + // number of columns + if ItemCount > 0 then + begin + BeginUpdate; + try + for I := 0 to ItemCount - 1 do + Add.LoadFromStream(Stream, Version); + SetLength(FPositionToIndex, ItemCount); + Stream.ReadBuffer(FPositionToIndex[0], ItemCount * SizeOf(TColumnIndex)); + UpdatePositions(True); + finally + EndUpdate; + end; + end; + + // Data introduced with header stream version 5 + if Version > 4 then + Stream.ReadBuffer(FDefaultWidth, SizeOf(FDefaultWidth)); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.PaintHeader(DC : HDC; R : TRect; HOffset : Integer); + +// Backward compatible header paint method. This method takes care of visually moving floating columns + +var + VisibleFixedWidth : Integer; + RTLOffset : Integer; + + procedure PaintFixedArea; + + begin + if VisibleFixedWidth > 0 then + PaintHeader(FHeaderBitmap.Canvas, + Rect(0, 0, Min(R.Right, VisibleFixedWidth), R.Bottom - R.Top), + Point(R.Left, R.Top), RTLOffset); + end; + +begin + // Adjust size of the header bitmap + with TWithSafeRect(TreeView.HeaderRect) do + begin + FHeaderBitmap.SetSize(Max(Right, R.Right - R.Left), Bottom); + end; + + VisibleFixedWidth := GetVisibleFixedWidth; + + // Consider right-to-left directionality. + if TreeView.UseRightToLeftAlignment then + RTLOffset := TreeView.ComputeRTLOffset + else + RTLOffset := 0; + + if RTLOffset = 0 then + PaintFixedArea; + + // Paint the floating part of the header. + PaintHeader(FHeaderBitmap.Canvas, + Rect(VisibleFixedWidth - HOffset, 0, R.Right + VisibleFixedWidth - HOffset, R.Bottom - R.Top), + Point(R.Left + VisibleFixedWidth, R.Top), RTLOffset); + + // In case of right-to-left directionality we paint the fixed part last. + if RTLOffset <> 0 then + PaintFixedArea; + + // Blit the result to target. + with TWithSafeRect(R) do + BitBlt(DC, Left, Top, Right - Left, Bottom - Top, FHeaderBitmap.Canvas.Handle, Left, Top, SRCCOPY); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.PaintHeader(TargetCanvas : TCanvas; R : TRect; const Target : TPoint; + RTLOffset : Integer = 0); + +// Main paint method to draw the header. +// This procedure will paint the a slice (given in R) out of HeaderRect into TargetCanvas starting at position Target. +// This function does not offer the option to visually move floating columns due to scrolling. To accomplish this you +// need to call this method twice. + +var + Run : TColumnIndex; + RightBorderFlag, + NormalButtonStyle, + NormalButtonFlags, + PressedButtonStyle, + PressedButtonFlags, + RaisedButtonStyle, + RaisedButtonFlags : Cardinal; + Images : TCustomImageList; + OwnerDraw, + AdvancedOwnerDraw : Boolean; + PaintInfo : THeaderPaintInfo; + RequestedElements, + ActualElements : THeaderPaintElements; + + //--------------- local functions ------------------------------------------- + + procedure PrepareButtonStyles; + + // Prepare the button styles and flags for later usage. + + begin + RaisedButtonStyle := 0; + RaisedButtonFlags := 0; + case Header.Style of + hsThickButtons : + begin + NormalButtonStyle := BDR_RAISEDINNER or BDR_RAISEDOUTER; + NormalButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_MIDDLE or BF_SOFT or BF_ADJUST; + PressedButtonStyle := BDR_RAISEDINNER or BDR_RAISEDOUTER; + PressedButtonFlags := NormalButtonFlags or BF_RIGHT or BF_FLAT or BF_ADJUST; + end; + hsFlatButtons : + begin + NormalButtonStyle := BDR_RAISEDINNER; + NormalButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_MIDDLE or BF_ADJUST; + PressedButtonStyle := BDR_SUNKENOUTER; + PressedButtonFlags := BF_RECT or BF_MIDDLE or BF_ADJUST; + end; + else + // hsPlates or hsXPStyle, values are not used in the latter case + begin + NormalButtonStyle := BDR_RAISEDINNER; + NormalButtonFlags := BF_RECT or BF_MIDDLE or BF_SOFT or BF_ADJUST; + PressedButtonStyle := BDR_SUNKENOUTER; + PressedButtonFlags := BF_RECT or BF_MIDDLE or BF_ADJUST; + RaisedButtonStyle := BDR_RAISEDINNER; + RaisedButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_MIDDLE or BF_ADJUST; + end; + end; + end; + + //--------------------------------------------------------------------------- + + procedure DrawBackground; + + // Draw the header background. + + var + BackgroundRect : TRect; + Details : TThemedElementDetails; + Theme : HTHEME; + begin + BackgroundRect := Rect(Target.X, Target.Y, Target.X + R.Right - R.Left, Target.Y + Header.Height); + + with TargetCanvas do + begin + if hpeBackground in RequestedElements then + begin + PaintInfo.PaintRectangle := BackgroundRect; + TreeView.DoAdvancedHeaderDraw(PaintInfo, [hpeBackground]); + end + else + begin + if (TreeView.VclStyleEnabled and (seClient in TreeView.StyleElements)) then + begin + Details := StyleServices.GetElementDetails(thHeaderItemRightNormal); + StyleServices.DrawElement(Handle, Details, BackgroundRect, @BackgroundRect {$IF CompilerVersion >= 34}, TreeView.FCurrentPPI{$IFEND}); + end + else + if tsUseThemes in TreeView.TreeStates then + begin + Theme := OpenThemeData(TreeView.Handle, 'HEADER'); + DrawThemeBackground(Theme, Handle, HP_HEADERITEM, HIS_NORMAL, BackgroundRect, nil); + CloseThemeData(Theme); + end + else + begin + Brush.Color := Header.Background; + FillRect(BackgroundRect); + end; + end; + end; + end; + + //--------------------------------------------------------------------------- + + procedure PaintColumnHeader(AColumn : TColumnIndex; ATargetRect : TRect); + + // Draw a single column to TargetRect. The clipping rect needs to be set before + // this procedure is called. + + var + SavedDC : Integer; + ColCaptionText : string; + ColImageInfo : TVTImageInfo; + Glyph : TThemedHeader; + Details : TThemedElementDetails; + WrapCaption : Boolean; + DrawFormat : Cardinal; + Pos : TRect; + DrawHot : Boolean; + ImageWidth : Integer; + Theme : HTHEME; + IdState : Integer; + begin + ColImageInfo.Ghosted := False; + PaintInfo.Column := Items[AColumn]; + with PaintInfo, Column do + begin + IsHoverIndex := (AColumn = FHoverIndex) and (hoHotTrack in Header.Options) and (coEnabled in Options); + IsDownIndex := (AColumn = FDownIndex) and not FCheckBoxHit; + + if (coShowDropMark in FOptions) and (AColumn = FDropTarget) and (AColumn <> FDragIndex) then + begin + if FDropBefore then + DropMark := dmmLeft + else + DropMark := dmmRight; + end + else + DropMark := dmmNone; + + //Fix for issue 643 + //Do not show the left drop mark if the position to drop is just preceding the target which means + //the dragged column will stay where it is + if (DropMark = dmmLeft) and (Items[FDragIndex].Position = TColumnPosition(Max(Integer(Items[FDropTarget].Position) - 1, 0))) + then + DropMark := dmmNone + else + //Do not show the right drop mark if the position to drop is just following the target which means + //the dragged column will stay where it is + if (DropMark = dmmRight) and (Items[FDragIndex].Position = Items[FDropTarget].Position + 1) + then + DropMark := dmmNone; + + IsEnabled := (coEnabled in FOptions) and (TreeView.Enabled); + ShowHeaderGlyph := (hoShowImages in Header.Options) and ((Assigned(Images) and (FImageIndex > - 1)) or FCheckBox); + ShowSortGlyph := (AColumn = Header.SortColumn) and (hoShowSortGlyphs in Header.Options); + WrapCaption := coWrapCaption in FOptions; + + PaintRectangle := ATargetRect; + + // This path for text columns or advanced owner draw. + if (Style = vsText) or not OwnerDraw or AdvancedOwnerDraw then + begin + // See if the application wants to draw part of the header itself. + RequestedElements := []; + if AdvancedOwnerDraw then + begin + PaintInfo.Column := Items[AColumn]; + TreeView.DoHeaderDrawQueryElements(PaintInfo, RequestedElements); + end; + + if ShowRightBorder or (AColumn < Count - 1) then + RightBorderFlag := BF_RIGHT + else + RightBorderFlag := 0; + + if hpeBackground in RequestedElements then + TreeView.DoAdvancedHeaderDraw(PaintInfo, [hpeBackground]) + else + begin + if TreeView.VclStyleEnabled and (seClient in TreeView.StyleElements) then + begin + if IsDownIndex then + Details := StyleServices.GetElementDetails(thHeaderItemPressed) + else + if IsHoverIndex then + Details := StyleServices.GetElementDetails(thHeaderItemHot) + else + Details := StyleServices.GetElementDetails(thHeaderItemNormal); + StyleServices.DrawElement(TargetCanvas.Handle, Details, PaintRectangle, @PaintRectangle{$IF CompilerVersion >= 34}, TreeView.CurrentPPI{$IFEND}); + end + else + begin + if tsUseThemes in TreeView.TreeStates then + begin + Theme := OpenThemeData(TreeView.Handle, 'HEADER'); + if IsDownIndex then + IdState := HIS_PRESSED + else + if IsHoverIndex then + IdState := HIS_HOT + else + IdState := HIS_NORMAL; + DrawThemeBackground(Theme, TargetCanvas.Handle, HP_HEADERITEM, IdState, PaintRectangle, nil); + CloseThemeData(Theme); + end + else + if IsDownIndex then + DrawEdge(TargetCanvas.Handle, PaintRectangle, PressedButtonStyle, PressedButtonFlags) + else + // Plates have the special case of raising on mouse over. + if (Header.Style = hsPlates) and IsHoverIndex and + (coAllowClick in FOptions) and (coEnabled in FOptions) then + DrawEdge(TargetCanvas.Handle, PaintRectangle, RaisedButtonStyle, + RaisedButtonFlags or RightBorderFlag) + else + DrawEdge(TargetCanvas.Handle, PaintRectangle, NormalButtonStyle, + NormalButtonFlags or RightBorderFlag); + end; + end; + + PaintRectangle := ATargetRect; + + // calculate text and glyph position + InflateRect(PaintRectangle, - 2, - 2); + DrawFormat := DT_TOP or DT_NOPREFIX; + case CaptionAlignment of + taLeftJustify : + DrawFormat := DrawFormat or DT_LEFT; + taRightJustify : + DrawFormat := DrawFormat or DT_RIGHT; + taCenter : + DrawFormat := DrawFormat or DT_CENTER; + end; + if UseRightToLeftReading then + DrawFormat := DrawFormat + DT_RTLREADING; + ComputeHeaderLayout(PaintInfo, DrawFormat); + + // Move glyph and text one pixel to the right and down to simulate a pressed button. + if IsDownIndex then + begin + OffsetRect(TextRectangle, 1, 1); + Inc(GlyphPos.X); + Inc(GlyphPos.Y); + Inc(SortGlyphPos.X); + Inc(SortGlyphPos.Y); + end; + + // Advanced owner draw allows to paint elements, which would normally not be painted (because of space + // limitations, empty captions etc.). + ActualElements := RequestedElements * [hpeHeaderGlyph, hpeSortGlyph, hpeDropMark, hpeText, hpeOverlay]; + + // main glyph + FHasImage := False; + if Assigned(Images) then + ImageWidth := Images.Width + else + ImageWidth := 0; + + if not (hpeHeaderGlyph in ActualElements) and ShowHeaderGlyph and + (not ShowSortGlyph or (FBiDiMode <> bdLeftToRight) or (GlyphPos.X + ImageWidth <= SortGlyphPos.X)) then + begin + if not FCheckBox then + begin + ColImageInfo.Images := Images; + Images.Draw(TargetCanvas, GlyphPos.X, GlyphPos.Y, FImageIndex, IsEnabled); + end + else + begin + with TreeView do + begin + ColImageInfo.Images := CheckImages; + ColImageInfo.Index := GetCheckImage(nil, FCheckType, FCheckState, IsEnabled); + ColImageInfo.XPos := GlyphPos.X; + ColImageInfo.YPos := GlyphPos.Y; + PaintCheckImage(TargetCanvas, ColImageInfo, False); + end; + end; + + FHasImage := True; + with TWithSafeRect(FImageRect) do + begin + Left := GlyphPos.X; + Top := GlyphPos.Y; + Right := Left + ColImageInfo.Images.Width; + Bottom := Top + ColImageInfo.Images.Height; + end; + end; + + // caption + if WrapCaption then + ColCaptionText := FCaptionText + else + ColCaptionText := Text; + if IsHoverIndex and TreeView.VclStyleEnabled then + DrawHot := True + else + DrawHot := (IsHoverIndex and (hoHotTrack in Header.Options) and not (tsUseThemes in TreeView.TreeStates)); + if not (hpeText in ActualElements) and (Length(Text) > 0) then + DrawButtonText(TargetCanvas.Handle, ColCaptionText, TextRectangle, IsEnabled, DrawHot, DrawFormat, WrapCaption); + + // sort glyph + if not (hpeSortGlyph in ActualElements) and ShowSortGlyph then + begin + if tsUseExplorerTheme in TreeView.TreeStates then + begin + Pos.TopLeft := SortGlyphPos; + Pos.Right := Pos.Left + SortGlyphSize.cx; + Pos.Bottom := Pos.Top + SortGlyphSize.cy; + if Header.SortDirection = sdAscending then + Glyph := thHeaderSortArrowSortedUp + else + Glyph := thHeaderSortArrowSortedDown; + Details := StyleServices.GetElementDetails(Glyph); + if not StyleServices.DrawElement(TargetCanvas.Handle, Details, Pos, @Pos {$IF CompilerVersion >= 34}, TreeView.CurrentPPI {$IFEND}) then + PaintInfo.DrawSortArrow(Header.SortDirection); + end + else + begin + PaintInfo.DrawSortArrow(Header.SortDirection); + end; + end; + + // Show an indication if this column is the current drop target in a header drag operation. + if not (hpeDropMark in ActualElements) and (DropMark <> dmmNone) then + begin + PaintInfo.DrawDropMark(); + end; + + if ActualElements <> [] then + begin + SavedDC := SaveDC(TargetCanvas.Handle); + TreeView.DoAdvancedHeaderDraw(PaintInfo, ActualElements); + RestoreDC(TargetCanvas.Handle, SavedDC); + end; + end + else // Let application draw the header. + TreeView.DoHeaderDraw(TargetCanvas, Items[AColumn], PaintRectangle, IsHoverIndex, IsDownIndex, + DropMark); + end; + end; + + //--------------- end local functions --------------------------------------- + +var + TargetRect : TRect; + MaxX : Integer; + +begin + if IsRectEmpty(R) then + Exit; + + // If both draw posibillities are specified then prefer the advanced way. + AdvancedOwnerDraw := (hoOwnerDraw in Header.Options) and Assigned(TreeView.OnAdvancedHeaderDraw) and + Assigned(TreeView.OnHeaderDrawQueryElements) and not (csDesigning in TreeView.ComponentState); + OwnerDraw := (hoOwnerDraw in Header.Options) and Assigned(TreeView.OnHeaderDraw) and + not (csDesigning in TreeView.ComponentState) and not AdvancedOwnerDraw; + + ZeroMemory(@PaintInfo, SizeOf(PaintInfo)); + PaintInfo.TargetCanvas := TargetCanvas; + + with PaintInfo, TargetCanvas do + begin + // Use shortcuts for the images and the font. + Images := Header.Images; + Font := Header.Font; + + PrepareButtonStyles; + + // At first, query the application which parts of the header it wants to draw on its own. + RequestedElements := []; + if AdvancedOwnerDraw then + begin + PaintRectangle := R; + Column := nil; + TreeView.DoHeaderDrawQueryElements(PaintInfo, RequestedElements); + end; + + // Draw the background. + DrawBackground; + + // Now that we have drawn the background, we apply the header's dimensions to R. + R := Rect(Max(R.Left, 0), Max(R.Top, 0), Min(R.Right, TotalWidth), Min(R.Bottom, Header.Height)); + + // Determine where to stop. + MaxX := Target.X + R.Right - R.Left + //Fixes issues #544, #427 -- MaxX should also shift on BidiMode bdRightToLeft + + RTLOffset; //added for fix + + // Determine the start column. + Run := ColumnFromPosition(Point(R.Left + RTLOffset, 0), False); + if Run <= NoColumn then + Exit; + + TargetRect.Top := Target.Y; + TargetRect.Bottom := Target.Y + R.Bottom - R.Top; + TargetRect.Left := Target.X - R.Left + Items[Run].Left + RTLOffset; + // TargetRect.Right will be set in the loop + + ShowRightBorder := (Header.Style = hsThickButtons) or not (hoAutoResize in Header.Options) or (TreeView.BevelKind = bkNone); + + // Now go for each button. + while (Run > NoColumn) and (TargetRect.Left < MaxX) do + begin + TargetRect.Right := TargetRect.Left + Items[Run].Width; + + // create a clipping rect to limit painting to button area + ClipCanvas(TargetCanvas, Rect(Max(TargetRect.Left, Target.X), Target.Y + R.Top, + Min(TargetRect.Right, MaxX), TargetRect.Bottom)); + + PaintColumnHeader(Run, TargetRect); + + SelectClipRgn(Handle, 0); + + TargetRect.Left := TargetRect.Right; + Run := GetNextVisibleColumn(Run); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVirtualTreeColumns.SaveToStream(const Stream : TStream); + +var + I : Integer; + +begin + I := Count; + Stream.WriteBuffer(I, SizeOf(I)); + if I > 0 then + begin + for I := 0 to Count - 1 do + TVirtualTreeColumn(Items[I]).SaveToStream(Stream); + + Stream.WriteBuffer(FPositionToIndex[0], Count * SizeOf(TColumnIndex)); + end; + + // Data introduced with header stream version 5. + Stream.WriteBuffer(DefaultWidth, SizeOf(DefaultWidth)); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVirtualTreeColumns.TotalWidth : Integer; + +var + LastColumn : TColumnIndex; + +begin + Result := 0; + if (Count > 0) and (Length(FPositionToIndex) > 0) then + begin + LastColumn := FPositionToIndex[Count - 1]; + if not (coVisible in Items[LastColumn].Options) then + LastColumn := GetPreviousVisibleColumn(LastColumn); + if LastColumn > NoColumn then + with Items[LastColumn] do + Result := FLeft + FWidth; + end; +end; + +{ THeaderPaintInfo } + +procedure THeaderPaintInfo.DrawDropMark(); +var + Y : Integer; + lArrowWidth : Integer; +begin + lArrowWidth := TBaseVirtualTreeCracker(Self.Column.TreeView).ScaledPixels(5); + Y := (PaintRectangle.Top + PaintRectangle.Bottom - 3 * lArrowWidth) div 2; + if DropMark = dmmLeft then + DrawArrow(TargetCanvas, TScrollDirection.sdLeft, Point(PaintRectangle.Left, Y), lArrowWidth) + else + DrawArrow(TargetCanvas, TScrollDirection.sdRight, Point(PaintRectangle.Right - lArrowWidth - (lArrowWidth div 2) {spacing}, Y), lArrowWidth); +end; + +procedure THeaderPaintInfo.DrawSortArrow(pDirection : TSortDirection); +const + cDirection : array [TSortDirection] of TScrollDirection = (TScrollDirection.sdUp, TScrollDirection.sdDown); +var + lOldColor : TColor; +begin + lOldColor := TargetCanvas.Pen.Color; + TargetCanvas.Pen.Color := clDkGray; + DrawArrow(TargetCanvas, cDirection[pDirection], Point(SortGlyphPos.X, SortGlyphPos.Y), SortGlyphSize.cy); + TargetCanvas.Pen.Color := lOldColor; +end; + +{ TVirtualTreeColumnHelper } + +function TVirtualTreeColumnHelper.Header : TVTHeaderCracker; +begin + Result := TVTHeaderCracker(Owner.Header); +end; + +function TVirtualTreeColumnHelper.TreeView : TBaseVirtualTreeCracker; +begin + Result := TBaseVirtualTreeCracker(TVTHeaderCracker(Owner.Header).GetOwner); +end; + +{ TVirtualTreeColumnsHelper } + +function TVirtualTreeColumnsHelper.Header : TVTHeaderCracker; +begin + Result := TVTHeaderCracker(FHeader); +end; + +function TVirtualTreeColumnsHelper.TreeView : TBaseVirtualTreeCracker; +begin + Result := TBaseVirtualTreeCracker(Header.GetOwner); +end; + +end. diff --git a/Source/VirtualTrees.Constants.pas b/Source/VirtualTrees.Constants.pas new file mode 100644 index 000000000..89bdba8c0 --- /dev/null +++ b/Source/VirtualTrees.Constants.pas @@ -0,0 +1,110 @@ +unit VirtualTrees.Constants; + +interface + +uses + System.UITypes, + Vcl.Themes; + +const + VTVersion = '7.5.0' deprecated 'This const is going to be removed in a future version'; + +const + VTTreeStreamVersion = 3; + VTHeaderStreamVersion = 6; // The header needs an own stream version to indicate changes only relevant to the header. + + CacheThreshold = 2000; // Number of nodes a tree must at least have to start caching and at the same + // time the maximum number of nodes between two cache entries. + FadeAnimationStepCount = 255; // Number of animation steps for hint fading (0..255). + ShadowSize = 5; // Size in pixels of the hint shadow. This value has no influence on Win2K and XP systems + // as those OSes have native shadow support. + + // Special identifiers for columns. + NoColumn = - 1; + InvalidColumn = - 2; + + // Indices for check state images used for checking. + ckEmpty = 0; // an empty image used as place holder + // radio buttons + ckRadioUncheckedNormal = 1; + ckRadioUncheckedHot = 2; + ckRadioUncheckedPressed = 3; + ckRadioUncheckedDisabled = 4; + ckRadioCheckedNormal = 5; + ckRadioCheckedHot = 6; + ckRadioCheckedPressed = 7; + ckRadioCheckedDisabled = 8; + // check boxes + ckCheckUncheckedNormal = 9; + ckCheckUncheckedHot = 10; + ckCheckUncheckedPressed = 11; + ckCheckUncheckedDisabled = 12; + ckCheckCheckedNormal = 13; + ckCheckCheckedHot = 14; + ckCheckCheckedPressed = 15; + ckCheckCheckedDisabled = 16; + ckCheckMixedNormal = 17; + ckCheckMixedHot = 18; + ckCheckMixedPressed = 19; + ckCheckMixedDisabled = 20; + // simple button + ckButtonNormal = 21; + ckButtonHot = 22; + ckButtonPressed = 23; + ckButtonDisabled = 24; + + // Instead using a TTimer class for each of the various events I use Windows timers with messages + // as this is more economical. + ExpandTimer = 1; + EditTimer = 2; + HeaderTimer = 3; + ScrollTimer = 4; + ChangeTimer = 5; + StructureChangeTimer = 6; + SearchTimer = 7; + ThemeChangedTimer = 8; + + ThemeChangedTimerDelay = 500; + + // Virtual Treeview does not need to be subclassed by an eventual Theme Manager instance as it handles + // Windows XP theme painting itself. Hence the special message is used to prevent subclassing. + CM_DENYSUBCLASSING = CM_BASE + 2000; + + // Decoupling message for auto-adjusting the internal edit window. + CM_AUTOADJUST = CM_BASE + 2005; + + // Drag image helpers for Windows 2000 and up. + IID_IDropTargetHelper : TGUID = (D1 : $4657278B; D2 : $411B; D3 : $11D2; D4 : ($83, $9A, $00, $C0, $4F, $D9, $18, $D0)); + IID_IDragSourceHelper : TGUID = (D1 : $DE5BF786; D2 : $477A; D3 : $11D2; D4 : ($83, $9D, $00, $C0, $4F, $D9, $18, $D0)); + IID_IDropTarget : TGUID = (D1 : $00000122; D2 : $0000; D3 : $0000; D4 : ($C0, $00, $00, $00, $00, $00, $00, $46)); + + // VT's own clipboard formats, + // Note: The reference format is used internally to allow to link to a tree reference + // to implement optimized moves and other back references. + CFSTR_VIRTUALTREE = 'Virtual Tree Data'; + CFSTR_VTREFERENCE = 'Virtual Tree Reference'; + CFSTR_HTML = 'HTML Format'; + CFSTR_RTF = 'Rich Text Format'; + CFSTR_RTFNOOBJS = 'Rich Text Format Without Objects'; + CFSTR_CSV = 'CSV'; + + // Help identifiers for exceptions. Application developers are responsible to link them with actual help topics. + hcTFEditLinkIsNil = 2000; + hcTFWrongMoveError = 2001; + hcTFWrongStreamFormat = 2002; + hcTFWrongStreamVersion = 2003; + hcTFStreamTooSmall = 2004; + hcTFCorruptStream1 = 2005; + hcTFCorruptStream2 = 2006; + hcTFClipboardFailed = 2007; + hcTFCannotSetUserData = 2008; + + // Header standard split cursor. + crHeaderSplit = TCursor(63); + + // Height changing cursor. + crVertSplit = TCursor(62); + +implementation + +end. diff --git a/Source/VirtualTrees.DataObject.pas b/Source/VirtualTrees.DataObject.pas new file mode 100644 index 000000000..368e3763d --- /dev/null +++ b/Source/VirtualTrees.DataObject.pas @@ -0,0 +1,482 @@ +unit VirtualTrees.DataObject; + +interface + +uses + WinApi.ActiveX, + WinApi.Windows, + VirtualTrees.Types, + Vcl.Controls; + +type + // IDataObject.SetData support + TInternalStgMedium = packed record + Format : TClipFormat; + Medium : TStgMedium; + end; + + TInternalStgMediumArray = array of TInternalStgMedium; + + // This data object is used in two different places. One is for clipboard operations and the other while dragging. + TVTDataObject = class(TInterfacedObject, IDataObject) + private + FOwner : TCustomControl; // The tree which provides clipboard or drag data. + FForClipboard : Boolean; // Determines which data to render with GetData. + FFormatEtcArray : TFormatEtcArray; + FInternalStgMediumArray : TInternalStgMediumArray; // The available formats in the DataObject + FAdviseHolder : IDataAdviseHolder; // Reference to an OLE supplied implementation for advising. + protected + function CanonicalIUnknown(const TestUnknown : IUnknown) : IUnknown; + function EqualFormatEtc(FormatEtc1, FormatEtc2 : TFormatEtc) : Boolean; + function FindFormatEtc(TestFormatEtc : TFormatEtc; const FormatEtcArray : TFormatEtcArray) : integer; + function FindInternalStgMedium(Format : TClipFormat) : PStgMedium; + function HGlobalClone(HGlobal : THandle) : THandle; + function RenderInternalOLEData(const FormatEtcIn : TFormatEtc; var Medium : TStgMedium; var OLEResult : HResult) : Boolean; + function StgMediumIncRef(const InStgMedium : TStgMedium; var OutStgMedium : TStgMedium; CopyInMedium : Boolean; const DataObject : IDataObject) : HResult; + + property ForClipboard : Boolean read FForClipboard; + property FormatEtcArray : TFormatEtcArray read FFormatEtcArray write FFormatEtcArray; + property InternalStgMediumArray : TInternalStgMediumArray read FInternalStgMediumArray write FInternalStgMediumArray; + property Owner : TCustomControl read FOwner; + public + constructor Create(AOwner : TCustomControl; ForClipboard : Boolean); virtual; + destructor Destroy; override; + + function DAdvise(const FormatEtc : TFormatEtc; advf : integer; const advSink : IAdviseSink; out dwConnection : integer) : HResult; virtual; stdcall; + function DUnadvise(dwConnection : integer) : HResult; virtual; stdcall; + function EnumDAdvise(out enumAdvise : IEnumStatData) : HResult; virtual; stdcall; + function EnumFormatEtc(Direction : integer; out EnumFormatEtc : IEnumFormatEtc) : HResult; virtual; stdcall; + function GetCanonicalFormatEtc(const FormatEtc : TFormatEtc; out FormatEtcOut : TFormatEtc) : HResult; virtual; stdcall; + function GetData(const FormatEtcIn : TFormatEtc; out Medium : TStgMedium) : HResult; virtual; stdcall; + function GetDataHere(const FormatEtc : TFormatEtc; out Medium : TStgMedium) : HResult; virtual; stdcall; + function QueryGetData(const FormatEtc : TFormatEtc) : HResult; virtual; stdcall; + function SetData(const FormatEtc : TFormatEtc; var Medium : TStgMedium; DoRelease : BOOL) : HResult; virtual; stdcall; + end; + +implementation + +uses + VirtualTrees, + VirtualTrees.ClipBoard, + VirtualTrees.DragnDrop; + +type + TVTCracker = class(TBaseVirtualTree); + + //----------------- TVTDataObject -------------------------------------------------------------------------------------- + +constructor TVTDataObject.Create(AOwner : TCustomControl; ForClipboard : Boolean); +begin + inherited Create; + + FOwner := AOwner; + FForClipboard := ForClipboard; + TVTCracker(FOwner).GetNativeClipboardFormats(FFormatEtcArray); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +destructor TVTDataObject.Destroy; +var + I : integer; + StgMedium : PStgMedium; +begin + // Cancel a pending clipboard operation if this data object was created for the clipboard and + // is freed because something else is placed there. + if FForClipboard and not (tsClipboardFlushing in TBaseVirtualTree(FOwner).TreeStates) then + TBaseVirtualTree(FOwner).CancelCutOrCopy; + + // Release any internal clipboard formats + for I := 0 to High(FormatEtcArray) do + begin + StgMedium := FindInternalStgMedium(FormatEtcArray[I].cfFormat); + if Assigned(StgMedium) then + ReleaseStgMedium(StgMedium^); + end; + + FormatEtcArray := nil; + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.CanonicalIUnknown(const TestUnknown : IUnknown) : IUnknown; +// Uses COM object identity: An explicit call to the IUnknown::QueryInterface method, requesting the IUnknown +// interface, will always return the same pointer. +begin + if Assigned(TestUnknown) then + begin + if TestUnknown.QueryInterface(IUnknown, Result) = 0 then + Result._Release // Don't actually need it just need the pointer value + else + Result := TestUnknown; + end + else + Result := TestUnknown; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.EqualFormatEtc(FormatEtc1, FormatEtc2 : TFormatEtc) : Boolean; +begin + Result := (FormatEtc1.cfFormat = FormatEtc2.cfFormat) and (FormatEtc1.ptd = FormatEtc2.ptd) and (FormatEtc1.dwAspect = FormatEtc2.dwAspect) and + (FormatEtc1.lindex = FormatEtc2.lindex) and (FormatEtc1.tymed and FormatEtc2.tymed <> 0); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.FindFormatEtc(TestFormatEtc : TFormatEtc; const FormatEtcArray : TFormatEtcArray) : integer; +var + I : integer; +begin + Result := - 1; + for I := 0 to High(FormatEtcArray) do + begin + if EqualFormatEtc(TestFormatEtc, FormatEtcArray[I]) then + begin + Result := I; + Break; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.FindInternalStgMedium(Format : TClipFormat) : PStgMedium; +var + I : integer; +begin + Result := nil; + for I := 0 to High(InternalStgMediumArray) do + begin + if Format = InternalStgMediumArray[I].Format then + begin + Result := @InternalStgMediumArray[I].Medium; + Break; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.HGlobalClone(HGlobal : THandle) : THandle; +// Returns a global memory block that is a copy of the passed memory block. +var + Size : Cardinal; + Data, NewData : PByte; +begin + Size := GlobalSize(HGlobal); + Result := GlobalAlloc(GPTR, Size); + Data := GlobalLock(HGlobal); + try + NewData := GlobalLock(Result); + try + Move(Data^, NewData^, Size); + finally + GlobalUnLock(Result); + end; + finally + GlobalUnLock(HGlobal); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.RenderInternalOLEData(const FormatEtcIn : TFormatEtc; var Medium : TStgMedium; var OLEResult : HResult) : Boolean; +// Tries to render one of the formats which have been stored via the SetData method. +// Since this data is already there it is just copied or its reference count is increased (depending on storage medium). +var + InternalMedium : PStgMedium; +begin + Result := True; + InternalMedium := FindInternalStgMedium(FormatEtcIn.cfFormat); + if Assigned(InternalMedium) then + OLEResult := StgMediumIncRef(InternalMedium^, Medium, False, Self as IDataObject) + else + Result := False; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.StgMediumIncRef(const InStgMedium : TStgMedium; var OutStgMedium : TStgMedium; CopyInMedium : Boolean; const DataObject : IDataObject) : HResult; +// InStgMedium is the data that is requested, OutStgMedium is the data that we are to return either a copy of or +// increase the IDataObject's reference and send ourselves back as the data (unkForRelease). The InStgMedium is usually +// the result of a call to find a particular FormatEtc that has been stored locally through a call to SetData. +// If CopyInMedium is not true we already have a local copy of the data when the SetData function was called (during +// that call the CopyInMedium must be true). Then as the caller asks for the data through GetData we do not have to make +// copy of the data for the caller only to have them destroy it then need us to copy it again if necessary. +// This way we increase the reference count to ourselves and pass the STGMEDIUM structure initially stored in SetData. +// This way when the caller frees the structure it sees the unkForRelease is not nil and calls Release on the object +// instead of destroying the actual data. +var + Len : integer; +begin + Result := S_OK; + + // Simply copy all fields to start with. + OutStgMedium := InStgMedium; + // The data handled here always results from a call of SetData we got. This ensures only one storage format + // is indicated and hence the case statement below is safe (IDataObject.GetData can optionally use several + // storage formats). + case InStgMedium.tymed of + TYMED_HGLOBAL : + begin + if CopyInMedium then + begin + // Generate a unique copy of the data passed + OutStgMedium.HGlobal := HGlobalClone(InStgMedium.HGlobal); + if OutStgMedium.HGlobal = 0 then + Result := E_OUTOFMEMORY; + end + else + // Don't generate a copy just use ourselves and the copy previously saved. + OutStgMedium.unkForRelease := Pointer(DataObject); // Does not increase RefCount. + end; + TYMED_FILE : + begin + Len := lstrLenW(InStgMedium.lpszFileName) + 1; // Don't forget the terminating null character. + OutStgMedium.lpszFileName := CoTaskMemAlloc(2 * Len); + Move(InStgMedium.lpszFileName^, OutStgMedium.lpszFileName^, 2 * Len); + end; + TYMED_ISTREAM : + IUnknown(OutStgMedium.stm)._AddRef; + TYMED_ISTORAGE : + IUnknown(OutStgMedium.stg)._AddRef; + TYMED_GDI : + if not CopyInMedium then + // Don't generate a copy just use ourselves and the previously saved data. + OutStgMedium.unkForRelease := Pointer(DataObject) // Does not increase RefCount. + else + Result := DV_E_TYMED; // Don't know how to copy GDI objects right now. + TYMED_MFPICT : + if not CopyInMedium then + // Don't generate a copy just use ourselves and the previously saved data. + OutStgMedium.unkForRelease := Pointer(DataObject) // Does not increase RefCount. + else + Result := DV_E_TYMED; // Don't know how to copy MetaFile objects right now. + TYMED_ENHMF : + if not CopyInMedium then + // Don't generate a copy just use ourselves and the previously saved data. + OutStgMedium.unkForRelease := Pointer(DataObject) // Does not increase RefCount. + else + Result := DV_E_TYMED; // Don't know how to copy enhanced metafiles objects right now. + else + Result := DV_E_TYMED; + end; + + if (Result = S_OK) and Assigned(OutStgMedium.unkForRelease) then + IUnknown(OutStgMedium.unkForRelease)._AddRef; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.DAdvise(const FormatEtc : TFormatEtc; advf : integer; const advSink : IAdviseSink; out dwConnection : integer) : HResult; +// Advise sink management is greatly simplified by the IDataAdviseHolder interface. +// We use this interface and forward all concerning calls to it. +begin + Result := S_OK; + if FAdviseHolder = nil then + Result := CreateDataAdviseHolder(FAdviseHolder); + if Result = S_OK then + Result := FAdviseHolder.Advise(Self as IDataObject, FormatEtc, advf, advSink, dwConnection); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.DUnadvise(dwConnection : integer) : HResult; +begin + if FAdviseHolder = nil then + Result := E_NOTIMPL + else + Result := FAdviseHolder.Unadvise(dwConnection); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.EnumDAdvise(out enumAdvise : IEnumStatData) : HResult; +begin + if FAdviseHolder = nil then + Result := OLE_E_ADVISENOTSUPPORTED + else + Result := FAdviseHolder.enumAdvise(enumAdvise); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.EnumFormatEtc(Direction : integer; out EnumFormatEtc : IEnumFormatEtc) : HResult; +var + NewList : TEnumFormatEtc; +begin + Result := E_FAIL; + if Direction = DATADIR_GET then + begin + NewList := TEnumFormatEtc.Create(FormatEtcArray); + EnumFormatEtc := NewList as IEnumFormatEtc; + Result := S_OK; + end + else + EnumFormatEtc := nil; + if EnumFormatEtc = nil then + Result := OLE_S_USEREG; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.GetCanonicalFormatEtc(const FormatEtc : TFormatEtc; out FormatEtcOut : TFormatEtc) : HResult; +begin + Result := DATA_S_SAMEFORMATETC; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.GetData(const FormatEtcIn : TFormatEtc; out Medium : TStgMedium) : HResult; +// Data is requested by clipboard or drop target. This method dispatchs the call +// depending on the data being requested. +var + I : integer; + Data : PVTReference; +begin + // The tree reference format is always supported and returned from here. + if FormatEtcIn.cfFormat = CF_VTREFERENCE then + begin + // Note: this format is not used while flushing the clipboard to avoid a dangling reference + // when the owner tree is destroyed before the clipboard data is replaced with something else. + if tsClipboardFlushing in TBaseVirtualTree(FOwner).TreeStates then + Result := E_FAIL + else + begin + Medium.HGlobal := GlobalAlloc(GHND or GMEM_SHARE, SizeOf(TVTReference)); + Data := GlobalLock(Medium.HGlobal); + Data.Process := GetCurrentProcessID; + Data.Tree := TBaseVirtualTree(FOwner); + GlobalUnLock(Medium.HGlobal); + Medium.tymed := TYMED_HGLOBAL; + Medium.unkForRelease := nil; + Result := S_OK; + end; + end + else + begin + try + // See if we accept this type and if not get the correct return value. + Result := QueryGetData(FormatEtcIn); + if Result = S_OK then + begin + for I := 0 to High(FormatEtcArray) do + begin + if EqualFormatEtc(FormatEtcIn, FormatEtcArray[I]) then + begin + if not RenderInternalOLEData(FormatEtcIn, Medium, Result) then + Result := TVTCracker(FOwner).RenderOLEData(FormatEtcIn, Medium, FForClipboard); + Break; + end; + end; + end; + except + ZeroMemory(@Medium, SizeOf(Medium)); + Result := E_FAIL; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.GetDataHere(const FormatEtc : TFormatEtc; out Medium : TStgMedium) : HResult; +begin + Result := E_NOTIMPL; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.QueryGetData(const FormatEtc : TFormatEtc) : HResult; +var + I : integer; +begin + Result := DV_E_CLIPFORMAT; + for I := 0 to High(FFormatEtcArray) do + begin + if FormatEtc.cfFormat = FFormatEtcArray[I].cfFormat then + begin + if (FormatEtc.tymed and FFormatEtcArray[I].tymed) <> 0 then + begin + if FormatEtc.dwAspect = FFormatEtcArray[I].dwAspect then + begin + if FormatEtc.lindex = FFormatEtcArray[I].lindex then + begin + Result := S_OK; + Break; + end + else + Result := DV_E_LINDEX; + end + else + Result := DV_E_DVASPECT; + end + else + Result := DV_E_TYMED; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDataObject.SetData(const FormatEtc : TFormatEtc; var Medium : TStgMedium; DoRelease : BOOL) : HResult; +// Allows dynamic adding to the IDataObject during its existance. Most noteably it is used to implement +// IDropSourceHelper and allows to set a special format for optimized moves during a shell transfer. +var + Index : integer; + LocalStgMedium : PStgMedium; +begin + // See if we already have a format of that type available. + Index := FindFormatEtc(FormatEtc, FormatEtcArray); + if Index > - 1 then + begin + // Just use the TFormatEct in the array after releasing the data. + LocalStgMedium := FindInternalStgMedium(FormatEtcArray[Index].cfFormat); + if Assigned(LocalStgMedium) then + begin + ReleaseStgMedium(LocalStgMedium^); + ZeroMemory(LocalStgMedium, SizeOf(LocalStgMedium^)); + end; + end + else + begin + // It is a new format so create a new TFormatCollectionItem, copy the + // FormatEtc parameter into the new object and and put it in the list. + SetLength(FFormatEtcArray, Length(FormatEtcArray) + 1); + FormatEtcArray[High(FormatEtcArray)] := FormatEtc; + + // Create a new InternalStgMedium and initialize it and associate it with the format. + SetLength(FInternalStgMediumArray, Length(InternalStgMediumArray) + 1); + InternalStgMediumArray[High(InternalStgMediumArray)].Format := FormatEtc.cfFormat; + LocalStgMedium := @InternalStgMediumArray[High(InternalStgMediumArray)].Medium; + ZeroMemory(LocalStgMedium, SizeOf(LocalStgMedium^)); + end; + + if DoRelease then + begin + // We are simply being given the data and we take control of it. + LocalStgMedium^ := Medium; + Result := S_OK; + end + else + begin + // We need to reference count or copy the data and keep our own references to it. + Result := StgMediumIncRef(Medium, LocalStgMedium^, True, Self as IDataObject); + + // Can get a circular reference if the client calls GetData then calls SetData with the same StgMedium. + // Because the unkForRelease for the IDataObject can be marshalled it is necessary to get pointers that + // can be correctly compared. See the IDragSourceHelper article by Raymond Chen at MSDN. + if Assigned(LocalStgMedium.unkForRelease) then + begin + if CanonicalIUnknown(Self) = CanonicalIUnknown(IUnknown(LocalStgMedium.unkForRelease)) then + IUnknown(LocalStgMedium.unkForRelease) := nil; // release the interface + end; + end; + + // Tell all registered advice sinks about the data change. + if Assigned(FAdviseHolder) then + FAdviseHolder.SendOnDataChange(Self as IDataObject, 0, 0); +end; + +end. diff --git a/Source/VirtualTrees.DragImage.pas b/Source/VirtualTrees.DragImage.pas new file mode 100644 index 000000000..6048edef2 --- /dev/null +++ b/Source/VirtualTrees.DragImage.pas @@ -0,0 +1,572 @@ +unit VirtualTrees.DragImage; + +interface + +uses + WinApi.Windows, + WinApi.ActiveX, + System.Types, + Vcl.Controls, + Vcl.Graphics; + +{$MINENUMSIZE 1, make enumerations as small as possible} + + +type + // Drag image support for the tree. + TVTTransparency = 0 .. 255; + TVTBias = - 128 .. 127; + + // Simple move limitation for the drag image. + TVTDragMoveRestriction = (dmrNone, dmrHorizontalOnly, dmrVerticalOnly); + + TVTDragImageStates = set of (disHidden, // Internal drag image is currently hidden (always hidden if drag image helper interfaces are used). + disInDrag, // Drag image class is currently being used. + disPrepared, // Drag image class is prepared. + disSystemSupport // Running on Windows 2000 or higher. System supports drag images natively. + ); + + // Class to manage header and tree drag image during a drag'n drop operation. + TVTDragImage = class + private + FOwner : TCustomControl; + FBackImage, // backup of overwritten screen area + FAlphaImage, // target for alpha blending + FDragImage : TBitmap; // the actual drag image to blend to screen + FImagePosition, // position of image (upper left corner) in screen coordinates + FLastPosition : TPoint; // last mouse position in screen coordinates + FTransparency : TVTTransparency; // alpha value of the drag image (0 - fully transparent, 255 - fully opaque) + FPreBlendBias, // value to darken or lighten the drag image before it is blended + FPostBlendBias : TVTBias; // value to darken or lighten the alpha blend result + FFade : Boolean; // determines whether to fade the drag image from center to borders or not + FRestriction : TVTDragMoveRestriction; // determines in which directions the drag image can be moved + FColorKey : TColor; // color to make fully transparent regardless of any other setting + FStates : TVTDragImageStates; // Determines the states of the drag image class. + function GetVisible : Boolean; // True if the drag image is currently hidden (used only when dragging) + procedure InternalShowDragImage(ScreenDC : HDC); + procedure MakeAlphaChannel(Source, Target : TBitmap); + public + constructor Create(AOwner : TCustomControl); + destructor Destroy; override; + + function DragTo(P : TPoint; ForceRepaint : Boolean) : Boolean; + procedure EndDrag; + function GetDragImageRect : TRect; + procedure HideDragImage; + procedure PrepareDrag(DragImage : TBitmap; ImagePosition, HotSpot : TPoint; const DataObject : IDataObject); + procedure RecaptureBackground(Tree : TCustomControl; R : TRect; VisibleRegion : HRGN; CaptureNCArea, ReshowDragImage : Boolean); + procedure ShowDragImage; + function WillMove(P : TPoint) : Boolean; + property ColorKey : TColor read FColorKey write FColorKey default clWindow; + property Fade : Boolean read FFade write FFade default False; + property ImagePosition : TPoint read FImagePosition; + property LastPosition : TPoint read FLastPosition; + property MoveRestriction : TVTDragMoveRestriction read FRestriction write FRestriction default dmrNone; + property PreBlendBias : TVTBias read FPreBlendBias write FPreBlendBias default 0; + property Transparency : TVTTransparency read FTransparency write FTransparency default 128; + property Visible : Boolean read GetVisible; + end; + +implementation + +uses + WinApi.ShlObj, + WinApi.Messages, + System.SysUtils, + System.Math, + VirtualTrees, + VirtualTrees.DragnDrop, + VirtualTrees.Types, + VirtualTrees.Utils; + +//----------------- TVTDragImage --------------------------------------------------------------------------------------- + +constructor TVTDragImage.Create(AOwner : TCustomControl); +begin + FOwner := AOwner; + FTransparency := 128; + FPreBlendBias := 0; + FPostBlendBias := 0; + FFade := False; + FRestriction := dmrNone; + FColorKey := clNone; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +destructor TVTDragImage.Destroy; +begin + EndDrag; + + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragImage.GetVisible : Boolean; +// Returns True if the internal drag image is used (i.e. the system does not natively support drag images) and +// the internal image is currently visible on screen. +begin + Result := FStates * [disHidden, disInDrag, disPrepared, disSystemSupport] = [disInDrag, disPrepared]; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTDragImage.InternalShowDragImage(ScreenDC : HDC); +// Frequently called helper routine to actually do the blend and put it onto the screen. +// Only used if the system does not support drag images. +var + BlendMode : TBlendMode; +begin + with FAlphaImage do + BitBlt(Canvas.Handle, 0, 0, Width, Height, FBackImage.Canvas.Handle, 0, 0, SRCCOPY); + if not FFade and (FColorKey = clNone) then + BlendMode := bmConstantAlpha + else + BlendMode := bmMasterAlpha; + with FDragImage do + AlphaBlend(Canvas.Handle, FAlphaImage.Canvas.Handle, Rect(0, 0, Width, Height), Point(0, 0), BlendMode, FTransparency, FPostBlendBias); + + with FAlphaImage do + BitBlt(ScreenDC, FImagePosition.X, FImagePosition.Y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTDragImage.MakeAlphaChannel(Source, Target : TBitmap); +// Helper method to create a proper alpha channel in Target (which must be in 32 bit pixel format), depending +// on the settings for the drag image and the color values in Source. +// Only used if the system does not support drag images. +type + PBGRA = ^TBGRA; + + TBGRA = packed record + case Boolean of + False : + (Color : Cardinal); + True : + (BGR : array [0 .. 2] of Byte; + Alpha : Byte); + end; + +var + Color, ColorKeyRef : COLORREF; + UseColorKey : Boolean; + SourceRun, TargetRun : PBGRA; + X, Y, MaxDimension, HalfWidth, HalfHeight : Integer; + T : Extended; +begin + UseColorKey := ColorKey <> clNone; + ColorKeyRef := ColorToRGB(ColorKey) and $FFFFFF; + // Color values are in the form BGR (red on LSB) while bitmap colors are in the form ARGB (blue on LSB) + // hence we have to swap red and blue in the color key. + with TBGRA(ColorKeyRef) do + begin + X := BGR[0]; + BGR[0] := BGR[2]; + BGR[2] := X; + end; + + with Target do + begin + MaxDimension := Max(Width, Height); + + HalfWidth := Width div 2; + HalfHeight := Height div 2; + for Y := 0 to Height - 1 do + begin + TargetRun := Scanline[Y]; + SourceRun := Source.Scanline[Y]; + for X := 0 to Width - 1 do + begin + Color := SourceRun.Color and $FFFFFF; + if UseColorKey and (Color = ColorKeyRef) then + TargetRun.Alpha := 0 + else + begin + // If the color is not the given color key (or none is used) then do full calculation of a bell curve. + T := Exp( - 8 * Sqrt(Sqr((X - HalfWidth) / MaxDimension) + Sqr((Y - HalfHeight) / MaxDimension))); + TargetRun.Alpha := Round(255 * T); + end; + Inc(SourceRun); + Inc(TargetRun); + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragImage.DragTo(P : TPoint; ForceRepaint : Boolean) : Boolean; +// Moves the drag image to a new position, which is determined from the passed point P and the previous +// mouse position. +// ForceRepaint is True if something on the screen changed and the back image must be refreshed. + +var + ScreenDC : HDC; + DeltaX, DeltaY : Integer; + + // optimized drag image move support + RSamp1, RSamp2, // newly added parts from screen which will be overwritten + RDraw1, RDraw2, // parts to be restored to screen + RScroll, RClip : TRect; // ScrollDC of the existent background +begin + // Determine distances to move the drag image. Take care for restrictions. + case FRestriction of + dmrHorizontalOnly : + begin + DeltaX := FLastPosition.X - P.X; + DeltaY := 0; + end; + dmrVerticalOnly : + begin + DeltaX := 0; + DeltaY := FLastPosition.Y - P.Y; + end; + else // dmrNone + DeltaX := FLastPosition.X - P.X; + DeltaY := FLastPosition.Y - P.Y; + end; + + Result := (DeltaX <> 0) or (DeltaY <> 0) or ForceRepaint; + if Result then + begin + if Visible then + begin + // All this stuff is only called if we have to handle the drag image ourselves. If the system supports + // drag image then this is all never executed. + ScreenDC := GetDC(0); + try + if (Abs(DeltaX) >= FDragImage.Width) or (Abs(DeltaY) >= FDragImage.Height) or ForceRepaint then + begin + // If moved more than image size then just restore old screen and blit image to new position. + BitBlt(ScreenDC, FImagePosition.X, FImagePosition.Y, FBackImage.Width, FBackImage.Height, FBackImage.Canvas.Handle, 0, 0, SRCCOPY); + + if ForceRepaint then + UpdateWindow(FOwner.Handle); + + Inc(FImagePosition.X, - DeltaX); + Inc(FImagePosition.Y, - DeltaY); + + BitBlt(FBackImage.Canvas.Handle, 0, 0, FBackImage.Width, FBackImage.Height, ScreenDC, FImagePosition.X, FImagePosition.Y, SRCCOPY); + end + else + begin + // overlapping copy + FillDragRectangles(FDragImage.Width, FDragImage.Height, DeltaX, DeltaY, RClip, RScroll, RSamp1, RSamp2, RDraw1, RDraw2); + + with FBackImage.Canvas do + begin + // restore uncovered areas of the screen + if DeltaX = 0 then + begin + with TWithSafeRect(RDraw2) do + BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top, SRCCOPY); + end + else + begin + if DeltaY = 0 then + begin + with TWithSafeRect(RDraw1) do + BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top, SRCCOPY); + end + else + begin + with TWithSafeRect(RDraw1) do + BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top, SRCCOPY); + with TWithSafeRect(RDraw2) do + BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top, SRCCOPY); + end; + end; + + // move existent background + ScrollDC(Handle, DeltaX, DeltaY, RScroll, RClip, 0, nil); + + Inc(FImagePosition.X, - DeltaX); + Inc(FImagePosition.Y, - DeltaY); + + // Get first and second additional rectangle from screen. + if DeltaX = 0 then + begin + with TWithSafeRect(RSamp2) do + BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, SRCCOPY); + end + else if DeltaY = 0 then + begin + with TWithSafeRect(RSamp1) do + BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, SRCCOPY); + end + else + begin + with TWithSafeRect(RSamp1) do + BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, SRCCOPY); + with TWithSafeRect(RSamp2) do + BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, SRCCOPY); + end; + end; + end; + InternalShowDragImage(ScreenDC); + finally + ReleaseDC(0, ScreenDC); + end; + end; + FLastPosition.X := P.X; + FLastPosition.Y := P.Y; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTDragImage.EndDrag; +begin + HideDragImage; + FStates := FStates - [disInDrag, disPrepared]; + + FBackImage.Free; + FBackImage := nil; + FDragImage.Free; + FDragImage := nil; + FAlphaImage.Free; + FAlphaImage := nil; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragImage.GetDragImageRect : TRect; +// Returns the current size and position of the drag image (screen coordinates). +begin + if Visible then + begin + with FBackImage do + Result := Rect(FImagePosition.X, FImagePosition.Y, FImagePosition.X + Width, FImagePosition.Y + Height); + end + else + Result := Rect(0, 0, 0, 0); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTDragImage.HideDragImage; +var + ScreenDC : HDC; +begin + if Visible then + begin + Include(FStates, disHidden); + ScreenDC := GetDC(0); + try + // restore screen + with FBackImage do + BitBlt(ScreenDC, FImagePosition.X, FImagePosition.Y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY); + finally + ReleaseDC(0, ScreenDC); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTDragImage.PrepareDrag(DragImage : TBitmap; ImagePosition, HotSpot : TPoint; const DataObject : IDataObject); +// Creates all necessary structures to do alpha blended dragging using the given image. +// ImagePostion and HotSpot are given in screen coordinates. The first determines where to place the drag image while +// the second is the initial mouse position. +// This method also determines whether the system supports drag images natively. If so then only minimal structures +// are created. + +var + Width, Height : Integer; + DragSourceHelper : IDragSourceHelper; + DragInfo : TSHDragImage; + lDragSourceHelper2 : IDragSourceHelper2; // Needed to get Windows Vista+ style drag hints. + lNullPoint : TPoint; +begin + Width := DragImage.Width; + Height := DragImage.Height; + + // Determine whether the system supports the drag helper interfaces. + if Assigned(DataObject) and Succeeded(CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER, IDragSourceHelper, DragSourceHelper)) then + begin + Include(FStates, disSystemSupport); + lNullPoint := Point(0, 0); + if Supports(DragSourceHelper, IDragSourceHelper2, lDragSourceHelper2) then + lDragSourceHelper2.SetFlags(DSH_ALLOWDROPDESCRIPTIONTEXT); // Show description texts + // First let the system try to initialze the DragSourceHelper, this works fine for file system objects (CF_HDROP) + StandardOLEFormat.cfFormat := CF_HDROP; + if not Succeeded(DataObject.QueryGetData(StandardOLEFormat)) or not Succeeded(DragSourceHelper.InitializeFromWindow(0, lNullPoint, DataObject)) then + begin + // Supply the drag source helper with our drag image. + DragInfo.sizeDragImage.cx := Width; + DragInfo.sizeDragImage.cy := Height; + DragInfo.ptOffset.X := Width div 2; + DragInfo.ptOffset.Y := Height div 2; + DragInfo.hbmpDragImage := CopyImage(DragImage.Handle, IMAGE_BITMAP, Width, Height, LR_COPYRETURNORG); + DragInfo.crColorKey := ColorToRGB(FColorKey); + if not Succeeded(DragSourceHelper.InitializeFromBitmap(@DragInfo, DataObject)) then + begin + DeleteObject(DragInfo.hbmpDragImage); + Exclude(FStates, disSystemSupport); + end; + end; + end + else + Exclude(FStates, disSystemSupport); + + if not (disSystemSupport in FStates) then + begin + FLastPosition := HotSpot; + + FDragImage := TBitmap.Create; + FDragImage.PixelFormat := pf32Bit; + FDragImage.SetSize(Width, Height); + + FAlphaImage := TBitmap.Create; + FAlphaImage.PixelFormat := pf32Bit; + FAlphaImage.SetSize(Width, Height); + + FBackImage := TBitmap.Create; + FBackImage.PixelFormat := pf32Bit; + FBackImage.SetSize(Width, Height); + + // Copy the given drag image and apply pre blend bias if required. + if FPreBlendBias = 0 then + with FDragImage do + BitBlt(Canvas.Handle, 0, 0, Width, Height, DragImage.Canvas.Handle, 0, 0, SRCCOPY) + else + AlphaBlend(DragImage.Canvas.Handle, FDragImage.Canvas.Handle, Rect(0, 0, Width, Height), Point(0, 0), bmConstantAlpha, 255, FPreBlendBias); + + // Create a proper alpha channel also if no fading is required (transparent parts). + MakeAlphaChannel(DragImage, FDragImage); + + FImagePosition := ImagePosition; + + // Initially the drag image is hidden and will be shown during the immediately following DragEnter event. + FStates := FStates + [disInDrag, disHidden, disPrepared]; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTDragImage.RecaptureBackground(Tree : TCustomControl; R : TRect; VisibleRegion : HRGN; CaptureNCArea, ReshowDragImage : Boolean); +// Notification by the drop target tree to update the background image because something in the tree has changed. +// Note: The passed rectangle is given in client coordinates of the current drop target tree (given in Tree). +// The caller does not check if the given rectangle is actually within the drag image. Hence this method must do +// all the checks. +// This method does nothing if the system manages the drag image. + +var + DragRect, ClipRect : TRect; + PaintTarget : TPoint; + PaintOptions : TVTInternalPaintOptions; + ScreenDC : HDC; + +begin + // Recapturing means we want the tree to paint the new part into our back bitmap instead to the screen. + if Visible then + begin + // Create the minimum rectangle to be recaptured. + MapWindowPoints(Tree.Handle, 0, R, 2); + DragRect := GetDragImageRect; + IntersectRect(R, R, DragRect); + + OffsetRgn(VisibleRegion, - DragRect.Left, - DragRect.Top); + + // The target position for painting in the drag image is relative and can be determined from screen coordinates too. + PaintTarget.X := R.Left - DragRect.Left; + PaintTarget.Y := R.Top - DragRect.Top; + + // The source rectangle is determined by the offsets in the tree. + MapWindowPoints(0, Tree.Handle, R, 2); + OffsetRect(R, - TBaseVirtualTree(Tree).OffsetX, - TBaseVirtualTree(Tree).OffsetY); + + // Finally let the tree paint the relevant part and upate the drag image on screen. + PaintOptions := [poBackground, poColumnColor, poDrawFocusRect, poDrawDropMark, poDrawSelection, poGridLines]; + with FBackImage do + begin + ClipRect.TopLeft := PaintTarget; + ClipRect.Right := ClipRect.Left + R.Right - R.Left; + ClipRect.Bottom := ClipRect.Top + R.Bottom - R.Top; + // TODO: somehow with clipping, the background image is not drawn on the + // backup image. Need to be diagnosed and fixed. For now, we have coded + // a work around in DragTo where this is used by using the condition + // IsInHeader. (found when solving issue 248) + ClipCanvas(Canvas, ClipRect, VisibleRegion); + TBaseVirtualTree(Tree).PaintTree(Canvas, R, PaintTarget, PaintOptions); + + if CaptureNCArea then + begin + // Header is painted in this part only so when you use this routine and want + // to capture the header in backup image, this flag should be ON. + // For the non-client area we only need the visible region of the window as limit for painting. + SelectClipRgn(Canvas.Handle, VisibleRegion); + // Since WM_PRINT cannot be given a position where to draw we simply move the window origin and + // get the same effect. + GetWindowRect(Tree.Handle, ClipRect); + SetCanvasOrigin(Canvas, DragRect.Left - ClipRect.Left, DragRect.Top - ClipRect.Top); + Tree.Perform(WM_PRINT, WPARAM(Canvas.Handle), PRF_NONCLIENT); + SetCanvasOrigin(Canvas, 0, 0); + end; + SelectClipRgn(Canvas.Handle, 0); + + if ReshowDragImage then + begin + GDIFlush; + ScreenDC := GetDC(0); + try + InternalShowDragImage(ScreenDC); + finally + ReleaseDC(0, ScreenDC); + end; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTDragImage.ShowDragImage; +// Shows the drag image after it has been hidden by HideDragImage. +// Note: there might be a new background now. +// Also this method does nothing if the system manages the drag image. + +var + ScreenDC : HDC; +begin + if FStates * [disInDrag, disHidden, disPrepared, disSystemSupport] = [disInDrag, disHidden, disPrepared] then + begin + Exclude(FStates, disHidden); + + GDIFlush; + ScreenDC := GetDC(0); + try + BitBlt(FBackImage.Canvas.Handle, 0, 0, FBackImage.Width, FBackImage.Height, ScreenDC, FImagePosition.X, FImagePosition.Y, SRCCOPY); + + InternalShowDragImage(ScreenDC); + finally + ReleaseDC(0, ScreenDC); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragImage.WillMove(P : TPoint) : Boolean; +// This method determines whether the drag image would "physically" move when DragTo would be called with the same +// target point. +// Always returns False if the system drag image support is available. +begin + Result := Visible; + if Result then + begin + // Determine distances to move the drag image. Take care for restrictions. + case FRestriction of + dmrHorizontalOnly : + Result := FLastPosition.X <> P.X; + dmrVerticalOnly : + Result := FLastPosition.Y <> P.Y; + else // dmrNone + Result := (FLastPosition.X <> P.X) or (FLastPosition.Y <> P.Y); + end; + end; +end; + +end. diff --git a/Source/VirtualTrees.DragnDrop.pas b/Source/VirtualTrees.DragnDrop.pas new file mode 100644 index 000000000..898063676 --- /dev/null +++ b/Source/VirtualTrees.DragnDrop.pas @@ -0,0 +1,343 @@ +unit VirtualTrees.DragnDrop; + +interface + +uses + WinApi.Windows, + WinApi.ActiveX, + WinApi.ShlObj, + System.Types, + Vcl.Graphics, + Vcl.Controls, + VirtualTrees.Types, + VirtualTrees; + +type + TEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc) + private + FFormatEtcArray : TFormatEtcArray; + FCurrentIndex : Integer; + public + constructor Create(const AFormatEtcArray : TFormatEtcArray); + + function Clone(out Enum : IEnumFormatEtc) : HResult; stdcall; + function Next(celt : Integer; out elt; pceltFetched : PLongint) : HResult; stdcall; + function Reset : HResult; stdcall; + function Skip(celt : Integer) : HResult; stdcall; + end; + + // ----- OLE drag'n drop handling + + IVTDragManager = interface(IUnknown) + ['{C4B25559-14DA-446B-8901-0C879000EB16}'] + procedure ForceDragLeave; stdcall; + function GetDataObject : IDataObject; stdcall; + function GetDragSource : TBaseVirtualTree; stdcall; + function GetDropTargetHelperSupported : Boolean; stdcall; + function GetIsDropTarget : Boolean; stdcall; + + property DataObject : IDataObject read GetDataObject; + property DragSource : TBaseVirtualTree read GetDragSource; + property DropTargetHelperSupported : Boolean read GetDropTargetHelperSupported; + property IsDropTarget : Boolean read GetIsDropTarget; + end; + + // TVTDragManager is a class to manage drag and drop in a Virtual Treeview. + TVTDragManager = class(TInterfacedObject, IVTDragManager, IDropSource, IDropTarget) + private + FOwner, // The tree which is responsible for drag management. + FDragSource : TBaseVirtualTree; // Reference to the source tree if the source was a VT, might be different than the owner tree. + FIsDropTarget : Boolean; // True if the owner is currently the drop target. + FDataObject : IDataObject; // A reference to the data object passed in by DragEnter (only used when the owner tree is the current drop target). + FDropTargetHelper : IDropTargetHelper; // Win2k > Drag image support + FFullDragging : BOOL; // True, if full dragging is currently enabled in the system. + + function GetDataObject : IDataObject; stdcall; + function GetDragSource : TBaseVirtualTree; stdcall; + function GetDropTargetHelperSupported : Boolean; stdcall; + function GetIsDropTarget : Boolean; stdcall; + public + constructor Create(AOwner : TBaseVirtualTree); virtual; + destructor Destroy; override; + + function DragEnter(const DataObject : IDataObject; KeyState : Integer; Pt : TPoint; var Effect : Longint) : HResult; stdcall; + function DragLeave : HResult; stdcall; + function DragOver(KeyState : Integer; Pt : TPoint; var Effect : Longint) : HResult; stdcall; + function Drop(const DataObject : IDataObject; KeyState : Integer; Pt : TPoint; var Effect : Integer) : HResult; stdcall; + procedure ForceDragLeave; stdcall; + function GiveFeedback(Effect : Integer) : HResult; stdcall; + function QueryContinueDrag(EscapePressed : BOOL; KeyState : Integer) : HResult; stdcall; + end; + +var + StandardOLEFormat : TFormatEtc = ( + // Format must later be set. + cfFormat : 0; + // No specific target device to render on. + ptd : nil; + // Normal content to render. + dwAspect : DVASPECT_CONTENT; + // No specific page of multipage data (we don't use multipage data by default). + lindex : - 1; + // Acceptable storage formats are IStream and global memory. The first is preferred. + tymed : TYMED_ISTREAM or TYMED_HGLOBAL;); + +implementation + +uses + VirtualTrees.Options, + VirtualTrees.DataObject; + +type + TBaseVirtualTreeCracker = class(TBaseVirtualTree); + + TVTDragManagerHelper = class helper for TVTDragManager + function TreeView : TBaseVirtualTreeCracker; + end; + + + //----------------- TEnumFormatEtc ------------------------------------------------------------------------------------- + +constructor TEnumFormatEtc.Create(const AFormatEtcArray : TFormatEtcArray); +var + I : Integer; +begin + inherited Create; + // Make a local copy of the format data. + SetLength(FFormatEtcArray, Length(AFormatEtcArray)); + for I := 0 to High(AFormatEtcArray) do + FFormatEtcArray[I] := AFormatEtcArray[I]; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TEnumFormatEtc.Clone(out Enum : IEnumFormatEtc) : HResult; +var + AClone : TEnumFormatEtc; +begin + Result := S_OK; + try + AClone := TEnumFormatEtc.Create(FFormatEtcArray); + AClone.FCurrentIndex := FCurrentIndex; + Enum := AClone as IEnumFormatEtc; + except + Result := E_FAIL; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TEnumFormatEtc.Next(celt : Integer; out elt; pceltFetched : PLongint) : HResult; +var + CopyCount : Integer; +begin + Result := S_FALSE; + CopyCount := Length(FFormatEtcArray) - FCurrentIndex; + if celt < CopyCount then + CopyCount := celt; + if CopyCount > 0 then + begin + Move(FFormatEtcArray[FCurrentIndex], elt, CopyCount * SizeOf(TFormatEtc)); + Inc(FCurrentIndex, CopyCount); + Result := S_OK; + end; + if Assigned(pceltFetched) then + pceltFetched^ := CopyCount; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TEnumFormatEtc.Reset : HResult; +begin + FCurrentIndex := 0; + Result := S_OK; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TEnumFormatEtc.Skip(celt : Integer) : HResult; +begin + if FCurrentIndex + celt < High(FFormatEtcArray) then + begin + Inc(FCurrentIndex, celt); + Result := S_OK; + end + else + Result := S_FALSE; +end; + + +//---------------------------------------------------------------------------------------------------------------------- + +// OLE drag and drop support classes +// This is quite heavy stuff (compared with the VCL implementation) but is much better suited to fit the needs +// of DD'ing various kinds of virtual data and works also between applications. + + +//----------------- TVTDragManager ------------------------------------------------------------------------------------- + +constructor TVTDragManager.Create(AOwner : TBaseVirtualTree); +begin + inherited Create; + FOwner := AOwner; + + // Create an instance of the drop target helper interface. This will fail but not harm on systems which do + // not support this interface (everything below Windows 2000); + CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, FDropTargetHelper); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +destructor TVTDragManager.Destroy; +begin + // Set the owner's reference to us to nil otherwise it will access an invalid pointer + // after our desctruction is complete. + TreeView.ClearDragManager; + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.GetDataObject : IDataObject; +begin + // When the owner tree starts a drag operation then it gets a data object here to pass it to the OLE subsystem. + // In this case there is no local reference to a data object and one is created (but not stored). + // If there is a local reference then the owner tree is currently the drop target and the stored interface is + // that of the drag initiator. + if Assigned(FDataObject) then + Result := FDataObject + else + begin + Result := TreeView.DoCreateDataObject; + if (Result = nil) and not Assigned(TreeView.OnCreateDataObject) then + // Do not create a TVTDataObject if the event handler explicitely decided not to supply one, issue #736. + Result := TVTDataObject.Create(FOwner, False) as IDataObject; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.GetDragSource : TBaseVirtualTree; +begin + Result := FDragSource; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.GetDropTargetHelperSupported : Boolean; +begin + Result := Assigned(FDropTargetHelper); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.GetIsDropTarget : Boolean; +begin + Result := FIsDropTarget; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.DragEnter(const DataObject : IDataObject; KeyState : Integer; Pt : TPoint; var Effect : Integer) : HResult; +begin + FDataObject := DataObject; + FIsDropTarget := True; + + SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, @FFullDragging, 0); + // If full dragging of window contents is disabled in the system then our tree windows will be locked + // and cannot be updated during a drag operation. With the following call painting is again enabled. + if not FFullDragging then + LockWindowUpdate(0); + if Assigned(FDropTargetHelper) and FFullDragging then + begin + if toAutoScroll in TreeView.TreeOptions.AutoOptions then + FDropTargetHelper.DragEnter(FOwner.Handle, DataObject, Pt, Effect) + else + FDropTargetHelper.DragEnter(0, DataObject, Pt, Effect); // Do not pass handle, otherwise the IDropTargetHelper will perform autoscroll. Issue #486 + end; + FDragSource := TreeView.GetTreeFromDataObject(DataObject); + Result := TreeView.DragEnter(KeyState, Pt, Effect); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.DragLeave : HResult; +begin + if Assigned(FDropTargetHelper) and FFullDragging then + FDropTargetHelper.DragLeave; + + TreeView.DragLeave; + FIsDropTarget := False; + FDragSource := nil; + FDataObject := nil; + Result := NOERROR; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.DragOver(KeyState : Integer; Pt : TPoint; var Effect : Integer) : HResult; +begin + if Assigned(FDropTargetHelper) and FFullDragging then + FDropTargetHelper.DragOver(Pt, Effect); + + Result := TreeView.DragOver(FDragSource, KeyState, dsDragMove, Pt, Effect); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.Drop(const DataObject : IDataObject; KeyState : Integer; Pt : TPoint; var Effect : Integer) : HResult; +begin + if Assigned(FDropTargetHelper) and FFullDragging then + FDropTargetHelper.Drop(DataObject, Pt, Effect); + + Result := TreeView.DragDrop(DataObject, KeyState, Pt, Effect); + FIsDropTarget := False; + FDataObject := nil; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTDragManager.ForceDragLeave; +// Some drop targets, e.g. Internet Explorer leave a drag image on screen instead removing it when they receive +// a drop action. This method calls the drop target helper's DragLeave method to ensure it removes the drag image from +// screen. Unfortunately, sometimes not even this does help (e.g. when dragging text from VT to a text field in IE). +begin + if Assigned(FDropTargetHelper) and FFullDragging then + FDropTargetHelper.DragLeave; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.GiveFeedback(Effect : Integer) : HResult; +begin + Result := DRAGDROP_S_USEDEFAULTCURSORS; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTDragManager.QueryContinueDrag(EscapePressed : BOOL; KeyState : Integer) : HResult; +var + RButton, LButton : Boolean; +begin + LButton := (KeyState and MK_LBUTTON) <> 0; + RButton := (KeyState and MK_RBUTTON) <> 0; + + // Drag'n drop canceled by pressing both mouse buttons or Esc? + if (LButton and RButton) or EscapePressed then + Result := DRAGDROP_S_CANCEL + else + // Drag'n drop finished? + if not (LButton or RButton) then + Result := DRAGDROP_S_DROP + else + Result := S_OK; +end; + +{ TVTDragManagerHelper } + +function TVTDragManagerHelper.TreeView : TBaseVirtualTreeCracker; +begin + Result := TBaseVirtualTreeCracker(FOwner); +end; + +end. diff --git a/Source/VirtualTrees.EditLink.pas b/Source/VirtualTrees.EditLink.pas new file mode 100644 index 000000000..c048717d0 --- /dev/null +++ b/Source/VirtualTrees.EditLink.pas @@ -0,0 +1,670 @@ +unit VirtualTrees.EditLink; + +interface + +uses + WinApi.Messages, + System.Types, + System.Classes, + Vcl.Controls, + Vcl.StdCtrls, + VirtualTrees, + VirtualTrees.Constants; + +type + //Edit support Classes. + TStringEditLink = class; + + TVTEdit = class(TCustomEdit) + private + procedure CMAutoAdjust(var Message : TMessage); message CM_AUTOADJUST; + procedure CMExit(var Message : TMessage); message CM_EXIT; + procedure CMRelease(var Message : TMessage); message CM_RELEASE; + procedure CNCommand(var Message : TWMCommand); message CN_COMMAND; + procedure WMChar(var Message : TWMChar); message WM_CHAR; + procedure WMDestroy(var Message : TWMDestroy); message WM_DESTROY; + procedure WMGetDlgCode(var Message : TWMGetDlgCode); message WM_GETDLGCODE; + procedure WMKeyDown(var Message : TWMKeyDown); message WM_KEYDOWN; + protected + FRefLink : IVTEditLink; + FLink : TStringEditLink; + procedure AutoAdjustSize; virtual; + function CalcMinHeight : Integer; virtual; + procedure CreateParams(var Params : TCreateParams); override; + function GetTextSize : TSize; virtual; + procedure KeyPress(var Key : Char); override; + public + constructor Create(Link : TStringEditLink); reintroduce; + procedure ClearLink; + procedure ClearRefLink; + procedure Release; virtual; + + property AutoSelect; + property AutoSize; + property BorderStyle; + property CharCase; + property HideSelection; + property MaxLength; + property OEMConvert; + property PasswordChar; + end; + + TStringEditLink = class(TInterfacedObject, IVTEditLink) + private + FEdit : TVTEdit; //A normal custom edit control. + protected + FTree : TCustomVirtualStringTree; //A back reference to the tree calling. + FNode : PVirtualNode; //The node to be edited. + FColumn : TColumnIndex; //The column of the node. + FAlignment : TAlignment; + FTextBounds : TRect; //Smallest rectangle around the text. + FStopping : Boolean; //Set to True when the edit link requests stopping the edit action. + procedure SetEdit(const Value : TVTEdit); //Setter for the FEdit member; + public + constructor Create; virtual; + destructor Destroy; override; + property Alignment : TAlignment read FAlignment; + property Node : PVirtualNode read FNode; //[IPK] Make FNode accessible + property Column : TColumnIndex read FColumn; //[IPK] Make Column(Index) accessible + + function BeginEdit : Boolean; virtual; stdcall; + function CancelEdit : Boolean; virtual; stdcall; + property Edit : TVTEdit read FEdit write SetEdit; + function EndEdit : Boolean; virtual; stdcall; + function GetBounds : TRect; virtual; stdcall; + function PrepareEdit(Tree : TBaseVirtualTree; Node : PVirtualNode; Column : TColumnIndex) : Boolean; virtual; stdcall; + procedure ProcessMessage(var Message : TMessage); virtual; stdcall; + procedure SetBounds(R : TRect); virtual; stdcall; + property Stopping : Boolean read FStopping; + property Tree : TCustomVirtualStringTree read FTree; + end; + +implementation + +uses + WinApi.Windows, + System.SysUtils, + System.Math, + Vcl.Graphics, + Vcl.Forms, + VirtualTrees.Options; + +type + TCustomVirtualStringTreeCracker = class(TCustomVirtualStringTree); + + //----------------- TVTEdit -------------------------------------------------------------------------------------------- + + //Implementation of a generic node caption editor. + +constructor TVTEdit.Create(Link : TStringEditLink); +begin + inherited Create(nil); + if not Assigned(Link) then + raise EArgumentException.Create('Paramter Link must not be nil.'); + ShowHint := False; + ParentShowHint := False; + //This assignment increases the reference count for the interface. + FRefLink := Link; + //This reference is used to access the link. + FLink := Link; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.ClearLink; +begin + FLink := nil +end; + +//---------------------------------------------------------------------------------------------------------------------- +procedure TVTEdit.ClearRefLink; +begin + FRefLink := nil +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTEdit.CalcMinHeight : Integer; +var + textHeight : Integer; +begin + //Get the actual text height. + textHeight := GetTextSize.cy; + //The minimal height is the actual text height in pixels plus the the non client area. + Result := textHeight + (Height - ClientHeight); + //Also, proportionally to the text size, additional pixel(s) needs to be added for the caret. + Result := Result + Trunc(textHeight * 0.05); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.CMAutoAdjust(var Message : TMessage); +begin + AutoAdjustSize; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.CMExit(var Message : TMessage); +begin + if Assigned(FLink) and not FLink.Stopping then + with FLink, TCustomVirtualStringTreeCracker(FTree) do + begin + if (toAutoAcceptEditChange in TreeOptions.StringOptions) then + DoEndEdit + else + DoCancelEdit; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.CMRelease(var Message : TMessage); +begin + Free; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.CNCommand(var Message : TWMCommand); +begin + if Assigned(FLink) and Assigned(FLink.Tree) and (Message.NotifyCode = EN_UPDATE) and not (vsMultiline in FLink.Node.States) then + //Instead directly calling AutoAdjustSize it is necessary on Win9x/Me to decouple this notification message + //and eventual resizing. Hence we use a message to accomplish that. + AutoAdjustSize() + else + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.WMChar(var Message : TWMChar); +begin + if not (Message.CharCode in [VK_ESCAPE, VK_TAB]) then + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.WMDestroy(var Message : TWMDestroy); +begin + //If editing stopped by other means than accept or cancel then we have to do default processing for + //pending changes. + if Assigned(FLink) and not FLink.Stopping and not (csRecreating in Self.ControlState) then + begin + with FLink, TCustomVirtualStringTreeCracker(FTree) do + begin + if (toAutoAcceptEditChange in TreeOptions.StringOptions) and Modified then + Text[FNode, FColumn] := FEdit.Text; + end; + FLink := nil; + FRefLink := nil; + end; + + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.WMGetDlgCode(var Message : TWMGetDlgCode); +begin + inherited; + + Message.Result := Message.Result or DLGC_WANTALLKEYS or DLGC_WANTTAB or DLGC_WANTARROWS; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.WMKeyDown(var Message : TWMKeyDown); +//Handles some control keys. + +var + Shift : TShiftState; + EndEdit : Boolean; + Tree : TBaseVirtualTree; + NextNode : PVirtualNode; + ColumnCandidate : Integer; + EditOptions : TVTEditOptions; + Column : TVirtualTreeColumn; +begin + Tree := FLink.Tree; + case Message.CharCode of + VK_ESCAPE : + begin + TCustomVirtualStringTreeCracker(Tree).DoCancelEdit; + end; + VK_RETURN : + begin + EndEdit := not (vsMultiline in FLink.Node.States); + if not EndEdit then + begin + //If a multiline node is being edited the finish editing only if Ctrl+Enter was pressed, + //otherwise allow to insert line breaks into the text. + Shift := KeyDataToShiftState(Message.KeyData); + EndEdit := ssCtrl in Shift; + end; + if EndEdit then + begin + Tree := FLink.Tree; + FLink.Tree.InvalidateNode(FLink.Node); + NextNode := Tree.GetNextVisible(FLink.Node, True); + TCustomVirtualStringTreeCracker(FLink.Tree).DoEndEdit; + + //get edit options for column as priority. If column has toDefaultEdit + //use global edit options for tree + EditOptions := TCustomVirtualStringTreeCracker(Tree).TreeOptions.EditOptions; //default + ColumnCandidate := - 1; + if Tree.Header.Columns.Count > 0 then //are there any columns? + begin + Column := Tree.Header.Columns[Tree.FocusedColumn]; + if Column.EditOptions <> toDefaultEdit then + EditOptions := Column.EditOptions; + + //next column candidate for toVerticalEdit and toHorizontalEdit + if Column.EditNextColumn <> - 1 then + ColumnCandidate := Column.EditNextColumn; + end; + + case EditOptions of + toDefaultEdit : + TCustomVirtualStringTreeCracker(Tree).TrySetFocus; + toVerticalEdit : + if NextNode <> nil then + begin + Tree.FocusedNode := NextNode; + + //for toVerticalEdit ColumnCandidate is also proper, + //select ColumnCandidate column in row below + if ColumnCandidate <> - 1 then + begin + Tree.FocusedColumn := ColumnCandidate; + TCustomVirtualStringTreeCracker(Tree).EditColumn := ColumnCandidate; + end; + + if Tree.CanEdit(Tree.FocusedNode, Tree.FocusedColumn) then + TCustomVirtualStringTreeCracker(Tree).DoEdit; + end; + toHorizontalEdit : + begin + if ColumnCandidate = - 1 then + begin + //for toHorizontalEdit if property EditNextColumn is not used + //try to use just next column + ColumnCandidate := Tree.FocusedColumn + 1; + while (ColumnCandidate < Tree.Header.Columns.Count) and not Tree.CanEdit(Tree.FocusedNode, ColumnCandidate) do + Inc(ColumnCandidate); + end + else if not Tree.CanEdit(Tree.FocusedNode, ColumnCandidate) then + ColumnCandidate := Tree.Header.Columns.Count; //omit "focus/edit column" (see below) + + if ColumnCandidate < Tree.Header.Columns.Count then + begin + Tree.FocusedColumn := ColumnCandidate; + TCustomVirtualStringTreeCracker(Tree).EditColumn := ColumnCandidate; + TCustomVirtualStringTreeCracker(Tree).DoEdit; + end; + end; + end; + end; + end; + VK_UP : + begin + if not (vsMultiline in FLink.Node.States) then + Message.CharCode := VK_LEFT; + inherited; + end; + VK_DOWN : + begin + if not (vsMultiline in FLink.Node.States) then + Message.CharCode := VK_RIGHT; + inherited; + end; + VK_TAB : + begin + if Tree.IsEditing then + begin + Tree.InvalidateNode(FLink.Node); + if ssShift in KeyDataToShiftState(Message.KeyData) then + NextNode := Tree.GetPreviousVisible(FLink.Node, True)//Shift+Tab goes to previous mode + else + NextNode := Tree.GetNextVisible(FLink.Node, True); + Tree.EndEditNode; + //check NextNode, otherwise we got AV + if NextNode <> nil then + begin + //Continue editing next node + Tree.ClearSelection(); + Tree.Selected[NextNode] := True; + if Tree.CanEdit(Tree.FocusedNode, Tree.FocusedColumn) then + TCustomVirtualStringTreeCracker(Tree).DoEdit; + end; + end; + end; + Ord('A') : + begin + if Tree.IsEditing and ([ssCtrl] = KeyboardStateToShiftState) then + begin + Self.SelectAll(); + Message.CharCode := 0; + end; + end; + else + inherited; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.AutoAdjustSize; +//Changes the size of the edit to accomodate as much as possible of its text within its container window. +//NewChar describes the next character which will be added to the edit's text. + +var + Size : TSize; +begin + if not (vsMultiline in FLink.Node.States) and not (toGridExtensions in TCustomVirtualStringTreeCracker(FLink.Tree).TreeOptions.MiscOptions { see issue #252 } ) then + begin + //avoid flicker + SendMessage(Handle, WM_SETREDRAW, 0, 0); + try + Size := GetTextSize; + Inc(Size.cx, 2 * TCustomVirtualStringTreeCracker(FLink.Tree).TextMargin); + //Repaint associated node if the edit becomes smaller. + if Size.cx < Width then + FLink.Tree.Invalidate(); + + if FLink.Alignment = taRightJustify then + FLink.SetBounds(Rect(Left + Width - Size.cx, Top, Left + Width, Top + Max(Size.cy, Height))) + else + FLink.SetBounds(Rect(Left, Top, Left + Size.cx, Top + Max(Size.cy, Height))); + finally + SendMessage(Handle, WM_SETREDRAW, 1, 0); + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.CreateParams(var Params : TCreateParams); +begin + inherited; + if not Assigned(FLink.Node) then + exit; //Prevent AV exceptions occasionally seen in code below + + //Only with multiline style we can use the text formatting rectangle. + //This does not harm formatting as single line control, if we don't use word wrapping. + with Params do + begin + Style := Style or ES_MULTILINE; + if vsMultiline in FLink.Node.States then + Style := Style and not (ES_AUTOHSCROLL or WS_HSCROLL) or WS_VSCROLL or ES_AUTOVSCROLL; + if tsUseThemes in FLink.Tree.TreeStates then + begin + Style := Style and not WS_BORDER; + ExStyle := ExStyle or WS_EX_CLIENTEDGE; + end + else + begin + Style := Style or WS_BORDER; + ExStyle := ExStyle and not WS_EX_CLIENTEDGE; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTEdit.GetTextSize : TSize; +var + DC : HDC; + LastFont : THandle; +begin + DC := GetDC(Handle); + LastFont := SelectObject(DC, Font.Handle); + try + //Read needed space for the current text. + GetTextExtentPoint32(DC, PChar(Text + 'yG'), Length(Text) + 2, Result); + finally + SelectObject(DC, LastFont); + ReleaseDC(Handle, DC); + end; +end; + +procedure TVTEdit.KeyPress(var Key : Char); +begin + if (Key = #13) and Assigned(FLink) and not (vsMultiline in FLink.Node.States) then + Key := #0; //Filter out return keys as they will be added to the text, avoids #895 + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTEdit.Release; +begin + if HandleAllocated then + PostMessage(Handle, CM_RELEASE, 0, 0); +end; + +//----------------- TStringEditLink ------------------------------------------------------------------------------------ + +constructor TStringEditLink.Create; + begin + inherited; + FEdit := TVTEdit.Create(Self); + with FEdit do + begin + Visible := False; + BorderStyle := bsSingle; + AutoSize := False; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +destructor TStringEditLink.Destroy; +begin + if Assigned(FEdit) then + FEdit.Release; + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TStringEditLink.BeginEdit : Boolean; +//Notifies the edit link that editing can start now. descendants may cancel node edit +//by returning False. + +begin + Result := not FStopping; + if Result then + begin + FEdit.Show; + FEdit.SelectAll; + FEdit.SetFocus; + FEdit.AutoAdjustSize; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TStringEditLink.SetEdit(const Value : TVTEdit); +begin + if Assigned(FEdit) then + FEdit.Free; + FEdit := Value; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TStringEditLink.CancelEdit : Boolean; +begin + Result := not FStopping; + if Result then + begin + FStopping := True; + FEdit.Hide; + FTree.CancelEditNode; + FEdit.ClearLink; + FEdit.ClearRefLink; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TStringEditLink.EndEdit : Boolean; +begin + Result := not FStopping; + if Result then + try + FStopping := True; + if FEdit.Modified then + FTree.Text[FNode, FColumn] := FEdit.Text; + FEdit.Hide; + FEdit.ClearLink; + FEdit.ClearRefLink; + except + FStopping := False; + raise; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TStringEditLink.GetBounds : TRect; +begin + Result := FEdit.BoundsRect; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TStringEditLink.PrepareEdit(Tree : TBaseVirtualTree; Node : PVirtualNode; Column : TColumnIndex) : Boolean; +//Retrieves the true text bounds from the owner tree. + +var + Text : string; +begin + Result := Tree is TCustomVirtualStringTree; + if Result then + begin + if not Assigned(FEdit) then + begin + FEdit := TVTEdit.Create(Self); + FEdit.Visible := False; + FEdit.BorderStyle := bsSingle; + end; + FEdit.AutoSize := True; + FTree := Tree as TCustomVirtualStringTree; + FNode := Node; + FColumn := Column; + FEdit.Parent := Tree; + //Initial size, font and text of the node. + FTree.GetTextInfo(Node, Column, FEdit.Font, FTextBounds, Text); + FEdit.Font.Color := clWindowText; + FEdit.RecreateWnd; + FEdit.AutoSize := False; + FEdit.Text := Text; + + if Column <= NoColumn then + begin + FEdit.BidiMode := FTree.BidiMode; + FAlignment := TCustomVirtualStringTreeCracker(FTree).Alignment; + end + else + begin + FEdit.BidiMode := FTree.Header.Columns[Column].BidiMode; + FAlignment := FTree.Header.Columns[Column].Alignment; + end; + + if FEdit.BidiMode <> bdLeftToRight then + ChangeBidiModeAlignment(FAlignment); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TStringEditLink.ProcessMessage(var Message : TMessage); +begin + FEdit.WindowProc(Message); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TStringEditLink.SetBounds(R : TRect); +//Sets the outer bounds of the edit control and the actual edit area in the control. + +var + lOffset, tOffset, Height : Integer; + offsets : TVTOffsets; +begin + if not FStopping then + begin + //Check if the provided rect height is smaller than the edit control height. + Height := R.Bottom - R.Top; + if Height < FEdit.ClientHeight then + begin + //If the height is smaller than the minimal height we must correct it, otherwise the caret will be invisible. + tOffset := FEdit.CalcMinHeight - Height; + if tOffset > 0 then + Inc(R.Bottom, tOffset); + end; + + //Set the edit's bounds but make sure there's a minimum width and the right border does not + //extend beyond the parent's left/right border. + if R.Left < 0 then + R.Left := 0; + if R.Right - R.Left < 30 then + begin + if FAlignment = taRightJustify then + R.Left := R.Right - 30 + else + R.Right := R.Left + 30; + end; + if R.Right > FTree.ClientWidth then + R.Right := FTree.ClientWidth; + FEdit.BoundsRect := R; + + //The selected text shall exclude the text margins and be centered vertically. + //We have to take out the two pixel border of the edit control as well as a one pixel "edit border" the + //control leaves around the (selected) text. + R := FEdit.ClientRect; + + //If toGridExtensions are turned on, we can fine tune the left margin (or the right margin if RTL is on) + //of the text to exactly match the text in the tree cell. + if (toGridExtensions in TCustomVirtualStringTreeCracker(FTree).TreeOptions.MiscOptions) and + ((FAlignment = taLeftJustify) and (FEdit.BidiMode = bdLeftToRight) or (FAlignment = taRightJustify) and (FEdit.BidiMode <> bdLeftToRight)) then + begin + //Calculate needed text area offset. + FTree.GetOffsets(FNode, offsets, ofsText, FColumn); + if FColumn = FTree.Header.MainColumn then + begin + if offsets[ofsToggleButton] < 0 then + lOffset := - (offsets[ofsToggleButton] + 2) + else + lOffset := 0; + end + else + lOffset := offsets[ofsText] - offsets[ofsMargin] + 1; + //Apply the offset. + if FEdit.BidiMode = bdLeftToRight then + Inc(R.Left, lOffset) + else + Dec(R.Right, lOffset); + end; + + lOffset := IfThen(vsMultiline in FNode.States, 0, 2); + if tsUseThemes in FTree.TreeStates then + Inc(lOffset); + InflateRect(R, - TCustomVirtualStringTreeCracker(FTree).TextMargin + lOffset, lOffset); + if not (vsMultiline in FNode.States) then + begin + tOffset := FTextBounds.Top - FEdit.Top; + //Do not apply a negative offset, the cursor will disappear. + if tOffset > 0 then + OffsetRect(R, 0, tOffset); + end; + R.Top := Max( - 1, R.Top); //A value smaller than -1 will prevent the edit cursor from being shown by Windows, see issue #159 + R.Left := Max( - 1, R.Left); + SendMessage(FEdit.Handle, EM_SETRECTNP, 0, LPARAM(@R)); + end; +end; + +end. diff --git a/Source/VirtualTrees.Export.pas b/Source/VirtualTrees.Export.pas index 8ae32cd0f..a60582fc9 100644 --- a/Source/VirtualTrees.Export.pas +++ b/Source/VirtualTrees.Export.pas @@ -8,7 +8,8 @@ interface uses Winapi.Windows, VirtualTrees, - VirtualTrees.Classes; + VirtualTrees.Classes, + VirtualTrees.Options; function ContentToHTML(Tree: TCustomVirtualStringTree; Source: TVSTTextSourceType; const Caption: string = ''): String; function ContentToRTF(Tree: TCustomVirtualStringTree; Source: TVSTTextSourceType): RawByteString; @@ -26,7 +27,11 @@ implementation System.SysUtils, System.StrUtils, System.Generics.Collections, - System.UITypes; + System.UITypes, + VirtualTrees.Constants, + VirtualTrees.ClipBoard, + VirtualTrees.Columns, + VirtualTrees.Header; type TCustomVirtualStringTreeCracker = class(TCustomVirtualStringTree) diff --git a/Source/VirtualTrees.Header.pas b/Source/VirtualTrees.Header.pas new file mode 100644 index 000000000..02fde6bd1 --- /dev/null +++ b/Source/VirtualTrees.Header.pas @@ -0,0 +1,2529 @@ +unit VirtualTrees.Header; + +interface + +uses + System.Classes, + System.Types, + WinApi.Windows, + WinApi.Messages, + Vcl.Graphics, + Vcl.Menus, + Vcl.ImgList, + Vcl.Controls, + VirtualTrees.Constants, + VirtualTrees.Types, + VirtualTrees.Options, + VirtualTrees.DragImage, + VirtualTrees.Columns; + +{$MINENUMSIZE 1, make enumerations as small as possible} + + +type + //tree columns implementation + TVTHeader = class; + + TSmartAutoFitType = (smaAllColumns, //consider nodes in view only for all columns + smaNoColumn, //consider nodes in view only for no column + smaUseColumnOption //use coSmartResize of the corresponding column + ); //describes the used column resize behaviour for AutoFitColumns + + TVTConstraintPercent = 0 .. 100; + + TVTFixedAreaConstraints = class(TPersistent) + private + FHeader : TVTHeader; + FMaxHeightPercent, FMaxWidthPercent, FMinHeightPercent, FMinWidthPercent : TVTConstraintPercent; + FOnChange : TNotifyEvent; + procedure SetConstraints(Index : Integer; Value : TVTConstraintPercent); + protected + procedure Change; + property Header : TVTHeader read FHeader; + public + constructor Create(AOwner : TVTHeader); + + procedure Assign(Source : TPersistent); override; + property OnChange : TNotifyEvent read FOnChange write FOnChange; + published + property MaxHeightPercent : TVTConstraintPercent index 0 read FMaxHeightPercent write SetConstraints default 0; + property MaxWidthPercent : TVTConstraintPercent index 1 read FMaxWidthPercent write SetConstraints default 95; + property MinHeightPercent : TVTConstraintPercent index 2 read FMinHeightPercent write SetConstraints default 0; + property MinWidthPercent : TVTConstraintPercent index 3 read FMinWidthPercent write SetConstraints default 0; + end; + + TVTHeaderStyle = (hsThickButtons, //TButton look and feel + hsFlatButtons, //flatter look than hsThickButton, like an always raised flat TToolButton + hsPlates //flat TToolButton look and feel (raise on hover etc.) + ); + + TVTHeaderOption = (hoAutoResize, //Adjust a column so that the header never exceeds the client width of the owner control. + hoColumnResize, //Resizing columns with the mouse is allowed. + hoDblClickResize, //Allows a column to resize itself to its largest entry. + hoDrag, //Dragging columns is allowed. + hoHotTrack, //Header captions are highlighted when mouse is over a particular column. + hoOwnerDraw, //Header items with the owner draw style can be drawn by the application via event. + hoRestrictDrag, //Header can only be dragged horizontally. + hoShowHint, //Show application defined header hint. + hoShowImages, //Show header images. + hoShowSortGlyphs, //Allow visible sort glyphs. + hoVisible, //Header is visible. + hoAutoSpring, //Distribute size changes of the header to all columns, which are sizable and have the coAutoSpring option enabled. + hoFullRepaintOnResize, //Fully invalidate the header (instead of subsequent columns only) when a column is resized. + hoDisableAnimatedResize, //Disable animated resize for all columns. + hoHeightResize, //Allow resizing header height via mouse. + hoHeightDblClickResize, //Allow the header to resize itself to its default height. + hoHeaderClickAutoSort, //Clicks on the header will make the clicked column the SortColumn or toggle sort direction if it already was the sort column + hoAutoColumnPopupMenu, //Show a context menu for activating and deactivating columns on right click + hoAutoResizeInclCaption //Includes the header caption for the auto resizing + ); + TVTHeaderOptions = set of TVTHeaderOption; + + THeaderState = (hsAutoSizing, //auto size chain is in progess, do not trigger again on WM_SIZE + hsDragging, //header dragging is in progress (only if enabled) + hsDragPending, //left button is down, user might want to start dragging a column + hsLoading, //The header currently loads from stream, so updates are not necessary. + hsColumnWidthTracking, //column resizing is in progress + hsColumnWidthTrackPending, //left button is down, user might want to start resize a column + hsHeightTracking, //height resizing is in progress + hsHeightTrackPending, //left button is down, user might want to start changing height + hsResizing, //multi column resizing in progress + hsScaling, //the header is scaled after a change of FixedAreaConstraints or client size + hsNeedScaling //the header needs to be scaled + ); + THeaderStates = set of THeaderState; + + TVTHeader = class(TPersistent) + private + FOwner : TCustomControl; + FColumns : TVirtualTreeColumns; + FHeight : TDimension; + FFont : TFont; + FParentFont : Boolean; + FOptions : TVTHeaderOptions; + FStyle : TVTHeaderStyle; //button style + FBackgroundColor : TColor; + FAutoSizeIndex : TColumnIndex; + FPopupMenu : TPopupMenu; + FMainColumn : TColumnIndex; //the column which holds the tree + FMaxHeight : TDimension; + FMinHeight : TDimension; + FDefaultHeight : TDimension; + FFixedAreaConstraints : TVTFixedAreaConstraints; //Percentages for the fixed area (header, fixed columns). + FImages : TCustomImageList; + FImageChangeLink : TChangeLink; //connections to the image list to get notified about changes + fSplitterHitTolerance : TDimension; //For property SplitterHitTolerance + FSortColumn : TColumnIndex; + FSortDirection : TSortDirection; + FDragImage : TVTDragImage; //drag image management during header drag + FLastWidth : TDimension; //Used to adjust spring columns. This is the width of all visible columns, not the header rectangle. + FRestoreSelectionColumnIndex : Integer; //The column that is used to implement the coRestoreSelection option + function GetMainColumn : TColumnIndex; + function GetUseColumns : Boolean; + function IsFontStored : Boolean; + procedure SetAutoSizeIndex(Value : TColumnIndex); + procedure SetBackground(Value : TColor); + procedure SetColumns(Value : TVirtualTreeColumns); + procedure SetDefaultHeight(Value : TDimension); + procedure SetFont(const Value : TFont); + procedure SetHeight(Value : TDimension); + procedure SetImages(const Value : TCustomImageList); + procedure SetMainColumn(Value : TColumnIndex); + procedure SetMaxHeight(Value : TDimension); + procedure SetMinHeight(Value : TDimension); + procedure SetOptions(Value : TVTHeaderOptions); + procedure SetParentFont(Value : Boolean); + procedure SetSortColumn(Value : TColumnIndex); + procedure SetSortDirection(const Value : TSortDirection); + procedure SetStyle(Value : TVTHeaderStyle); + function GetRestoreSelectionColumnIndex : Integer; + protected + FStates : THeaderStates; //Used to keep track of internal states the header can enter. + FDragStart : TPoint; //initial mouse drag position + FTrackStart : TPoint; //client coordinates of the tracking start point + FTrackPoint : TPoint; //Client coordinate where the tracking started. + FDoingAutoFitColumns : Boolean; //Flag to avoid using the stored width for Main column + + procedure FontChanged(Sender : TObject); virtual; + procedure AutoScale(); virtual; + function CanSplitterResize(P : TPoint) : Boolean; + function CanWriteColumns : Boolean; virtual; + procedure ChangeScale(M, D : TDimension; isDpiChange : Boolean); virtual; + function DetermineSplitterIndex(P : TPoint) : Boolean; virtual; + procedure DoAfterAutoFitColumn(Column : TColumnIndex); virtual; + procedure DoAfterColumnWidthTracking(Column : TColumnIndex); virtual; + procedure DoAfterHeightTracking; virtual; + function DoBeforeAutoFitColumn(Column : TColumnIndex; SmartAutoFitType : TSmartAutoFitType) : Boolean; virtual; + procedure DoBeforeColumnWidthTracking(Column : TColumnIndex; Shift : TShiftState); virtual; + procedure DoBeforeHeightTracking(Shift : TShiftState); virtual; + procedure DoCanSplitterResize(P : TPoint; var Allowed : Boolean); virtual; + function DoColumnWidthDblClickResize(Column : TColumnIndex; P : TPoint; Shift : TShiftState) : Boolean; virtual; + function DoColumnWidthTracking(Column : TColumnIndex; Shift : TShiftState; var TrackPoint : TPoint; P : TPoint) : Boolean; virtual; + function DoGetPopupMenu(Column : TColumnIndex; Position : TPoint) : TPopupMenu; virtual; + function DoHeightTracking(var P : TPoint; Shift : TShiftState) : Boolean; virtual; + function DoHeightDblClickResize(var P : TPoint; Shift : TShiftState) : Boolean; virtual; + procedure DoSetSortColumn(Value : TColumnIndex; pSortDirection : TSortDirection); virtual; + procedure DragTo(P : TPoint); virtual; + procedure FixedAreaConstraintsChanged(Sender : TObject); + function GetColumnsClass : TVirtualTreeColumnsClass; virtual; + function GetOwner : TPersistent; override; + function GetShiftState : TShiftState; + function HandleHeaderMouseMove(var Message : TWMMouseMove) : Boolean; + function HandleMessage(var Message : TMessage) : Boolean; virtual; + procedure ImageListChange(Sender : TObject); + procedure PrepareDrag(P, Start : TPoint); + procedure ReadColumns(Reader : TReader); + procedure RecalculateHeader; virtual; + procedure RescaleHeader; + procedure UpdateMainColumn; + procedure UpdateSpringColumns; + procedure WriteColumns(Writer : TWriter); + procedure InternalSetMainColumn(const Index : TColumnIndex); + procedure InternalSetAutoSizeIndex(const Index : TColumnIndex); + procedure InternalSetSortColumn(const Index : TColumnIndex); + public + constructor Create(AOwner : TCustomControl); virtual; + destructor Destroy; override; + + function AllowFocus(ColumnIndex : TColumnIndex) : Boolean; + procedure Assign(Source : TPersistent); override; + procedure AutoFitColumns(Animated : Boolean = True; SmartAutoFitType : TSmartAutoFitType = smaUseColumnOption; RangeStartCol : Integer = NoColumn; + RangeEndCol : Integer = NoColumn); virtual; + function InHeader(P : TPoint) : Boolean; virtual; + function InHeaderSplitterArea(P : TPoint) : Boolean; virtual; + procedure Invalidate(Column : TVirtualTreeColumn; ExpandToBorder : Boolean = False; UpdateNowFlag : Boolean = False); + procedure LoadFromStream(const Stream : TStream); virtual; + function ResizeColumns(ChangeBy : TDimension; RangeStartCol : TColumnIndex; RangeEndCol : TColumnIndex; Options : TVTColumnOptions = [coVisible]) : TDimension; + procedure RestoreColumns; + procedure SaveToStream(const Stream : TStream); virtual; + procedure StyleChanged(); virtual; + + property DragImage : TVTDragImage read FDragImage; + property RestoreSelectionColumnIndex : Integer read GetRestoreSelectionColumnIndex write FRestoreSelectionColumnIndex default NoColumn; + property States : THeaderStates read FStates; + //property Treeview : TCustomControl read FOwner; + property UseColumns : Boolean read GetUseColumns; + property doingAutoFitColumns : Boolean read FDoingAutoFitColumns; + published + property AutoSizeIndex : TColumnIndex read FAutoSizeIndex write SetAutoSizeIndex; + property Background : TColor read FBackgroundColor write SetBackground default clBtnFace; + property Columns : TVirtualTreeColumns read FColumns write SetColumns stored False; //Stored by the owner tree to support VFI. + property DefaultHeight : Integer read FDefaultHeight write SetDefaultHeight default 19; + property Font : TFont read FFont write SetFont stored IsFontStored; + property FixedAreaConstraints : TVTFixedAreaConstraints read FFixedAreaConstraints write FFixedAreaConstraints; + property Height : Integer read FHeight write SetHeight default 19; + property Images : TCustomImageList read FImages write SetImages; + property MainColumn : TColumnIndex read GetMainColumn write SetMainColumn default 0; + property MaxHeight : Integer read FMaxHeight write SetMaxHeight default 10000; + property MinHeight : Integer read FMinHeight write SetMinHeight default 10; + property Options : TVTHeaderOptions read FOptions write SetOptions default [hoColumnResize, hoDrag, hoShowSortGlyphs]; + property ParentFont : Boolean read FParentFont write SetParentFont default True; + property PopupMenu : TPopupMenu read FPopupMenu write FPopupMenu; + property SortColumn : TColumnIndex read FSortColumn write SetSortColumn default NoColumn; + property SortDirection : TSortDirection read FSortDirection write SetSortDirection default sdAscending; + property SplitterHitTolerance : Integer read fSplitterHitTolerance write fSplitterHitTolerance default 8; + //The area in pixels around a spliter which is sensitive for resizing + property Style : TVTHeaderStyle read FStyle write SetStyle default hsThickButtons; + end; + + TVTHeaderClass = class of TVTHeader; + +implementation + +uses + System.Math, + Vcl.Forms, + VirtualTrees; + +type + TVirtualTreeColumnsCracker = class(TVirtualTreeColumns); + TVirtualTreeColumnCracker = class(TVirtualTreeColumn); + TBaseVirtualTreeCracker = class(TBaseVirtualTree); + + TVTHeaderHelper = class helper for TVTHeader + public + function Tree : TBaseVirtualTreeCracker; + end; + + + + //----------------- TVTFixedAreaConstraints ---------------------------------------------------------------------------- + +constructor TVTFixedAreaConstraints.Create(AOwner : TVTHeader); + +begin + inherited Create; + FMaxWidthPercent := 95; + FHeader := AOwner; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTFixedAreaConstraints.SetConstraints(Index : Integer; Value : TVTConstraintPercent); + +begin + case Index of + 0 : + if Value <> FMaxHeightPercent then + begin + FMaxHeightPercent := Value; + if (Value > 0) and (Value < FMinHeightPercent) then + FMinHeightPercent := Value; + Change; + end; + 1 : + if Value <> FMaxWidthPercent then + begin + FMaxWidthPercent := Value; + if (Value > 0) and (Value < FMinWidthPercent) then + FMinWidthPercent := Value; + Change; + end; + 2 : + if Value <> FMinHeightPercent then + begin + FMinHeightPercent := Value; + if (FMaxHeightPercent > 0) and (Value > FMaxHeightPercent) then + FMaxHeightPercent := Value; + Change; + end; + 3 : + if Value <> FMinWidthPercent then + begin + FMinWidthPercent := Value; + if (FMaxWidthPercent > 0) and (Value > FMaxWidthPercent) then + FMaxWidthPercent := Value; + Change; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTFixedAreaConstraints.Change; + +begin + if Assigned(FOnChange) then + FOnChange(Self); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTFixedAreaConstraints.Assign(Source : TPersistent); + +begin + if Source is TVTFixedAreaConstraints then + begin + FMaxHeightPercent := TVTFixedAreaConstraints(Source).FMaxHeightPercent; + FMaxWidthPercent := TVTFixedAreaConstraints(Source).FMaxWidthPercent; + FMinHeightPercent := TVTFixedAreaConstraints(Source).FMinHeightPercent; + FMinWidthPercent := TVTFixedAreaConstraints(Source).FMinWidthPercent; + Change; + end + else + inherited; +end; + +//----------------- TVTHeader ----------------------------------------------------------------------------------------- + +constructor TVTHeader.Create(AOwner : TCustomControl); + +begin + inherited Create; + FOwner := AOwner; + FColumns := GetColumnsClass.Create(Self); + FHeight := 19; + FDefaultHeight := FHeight; + FMinHeight := 10; + FMaxHeight := 10000; + FFont := TFont.Create; + FFont.OnChange := FontChanged; + FParentFont := True; + FBackgroundColor := clBtnFace; + FOptions := [hoColumnResize, hoDrag, hoShowSortGlyphs]; + + FImageChangeLink := TChangeLink.Create; + FImageChangeLink.OnChange := ImageListChange; + + FSortColumn := NoColumn; + FSortDirection := sdAscending; + FMainColumn := NoColumn; + + FDragImage := TVTDragImage.Create(AOwner); + with FDragImage do + begin + Fade := False; + PreBlendBias := - 50; + Transparency := 140; + end; + + fSplitterHitTolerance := 8; + FFixedAreaConstraints := TVTFixedAreaConstraints.Create(Self); + FFixedAreaConstraints.OnChange := FixedAreaConstraintsChanged; + + FDoingAutoFitColumns := False; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +destructor TVTHeader.Destroy; + +begin + FDragImage.Free; + FFixedAreaConstraints.Free; + FImageChangeLink.Free; + FFont.Free; + FColumns.Clear; //TCollection's Clear method is not virtual, so we have to call our own Clear method manually. + FColumns.Free; + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.FontChanged(Sender : TObject); +begin + inherited; + AutoScale(); +end; + +procedure TVTHeader.AutoScale(); +var + I : Integer; + lMaxHeight : Integer; +begin + if toAutoChangeScale in TBaseVirtualTreeCracker(Tree).TreeOptions.AutoOptions then + begin + //Ensure a minimum header size based on the font, so that all text is visible. + //First find the largest Columns[].Spacing + lMaxHeight := 0; + for I := 0 to Self.Columns.Count - 1 do + lMaxHeight := Max(lMaxHeight, Columns[I].Spacing); + //Calculate the required height based on the font, this is important as the user might just have increased the size of the system icon font. + with TBitmap.Create do + try + Canvas.Font.Assign(FFont); + lMaxHeight := lMaxHeight { top spacing } + (lMaxHeight div 2) { minimum bottom spacing } + Canvas.TextHeight('Q'); + finally + Free; + end; + //Get the maximum of the scaled original value and the minimum needed header height. + lMaxHeight := Max(lMaxHeight, FHeight); + //Set the calculated size + Self.SetHeight(lMaxHeight); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.GetMainColumn : TColumnIndex; +begin + if FColumns.Count > 0 then + Result := FMainColumn + else + Result := NoColumn; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.GetUseColumns : Boolean; +begin + Result := FColumns.Count > 0; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.IsFontStored : Boolean; +begin + Result := not ParentFont; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetAutoSizeIndex(Value : TColumnIndex); +begin + if FAutoSizeIndex <> Value then + begin + FAutoSizeIndex := Value; + if hoAutoResize in FOptions then + TVirtualTreeColumnsCracker(Columns).AdjustAutoSize(InvalidColumn); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetBackground(Value : TColor); +begin + if FBackgroundColor <> Value then + begin + FBackgroundColor := Value; + Invalidate(nil); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetColumns(Value : TVirtualTreeColumns); + +begin + FColumns.Assign(Value); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetDefaultHeight(Value : Integer); +begin + if Value < FMinHeight then + Value := FMinHeight; + if Value > FMaxHeight then + Value := FMaxHeight; + + if FHeight = FDefaultHeight then + SetHeight(Value); + FDefaultHeight := Value; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetFont(const Value : TFont); +begin + FFont.Assign(Value); + FParentFont := False; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetHeight(Value : Integer); +var + RelativeMaxHeight, RelativeMinHeight, EffectiveMaxHeight, EffectiveMinHeight : Integer; +begin + if not Tree.HandleAllocated then + begin + FHeight := Value; + Include(FStates, hsNeedScaling); + end + else + begin + with FFixedAreaConstraints do + begin + RelativeMaxHeight := ((Tree.ClientHeight + FHeight) * FMaxHeightPercent) div 100; + RelativeMinHeight := ((Tree.ClientHeight + FHeight) * FMinHeightPercent) div 100; + + EffectiveMinHeight := IfThen(FMaxHeightPercent > 0, Min(RelativeMaxHeight, FMinHeight), FMinHeight); + EffectiveMaxHeight := IfThen(FMinHeightPercent > 0, Max(RelativeMinHeight, FMaxHeight), FMaxHeight); + + Value := Min(Max(Value, EffectiveMinHeight), EffectiveMaxHeight); + if FMinHeightPercent > 0 then + Value := Max(RelativeMinHeight, Value); + if FMaxHeightPercent > 0 then + Value := Min(RelativeMaxHeight, Value); + end; + + if FHeight <> Value then + begin + FHeight := Value; + if not (csLoading in Tree.ComponentState) and not (hsScaling in FStates) then + RecalculateHeader; + Tree.Invalidate; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetImages(const Value : TCustomImageList); + +begin + if FImages <> Value then + begin + if Assigned(FImages) then + begin + FImages.UnRegisterChanges(FImageChangeLink); + FImages.RemoveFreeNotification(FOwner); + end; + FImages := Value; + if Assigned(FImages) then + begin + FImages.RegisterChanges(FImageChangeLink); + FImages.FreeNotification(FOwner); + end; + if not (csLoading in Tree.ComponentState) then + Invalidate(nil); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetMainColumn(Value : TColumnIndex); + +begin + if csLoading in Tree.ComponentState then + FMainColumn := Value + else + begin + if Value < 0 then + Value := 0; + if Value > FColumns.Count - 1 then + Value := FColumns.Count - 1; + if Value <> FMainColumn then + begin + FMainColumn := Value; + if not (csLoading in Tree.ComponentState) then + begin + Tree.MainColumnChanged; + if not (toExtendedFocus in Tree.TreeOptions.SelectionOptions) then + Tree.FocusedColumn := FMainColumn; + Tree.Invalidate; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetMaxHeight(Value : Integer); + +begin + if Value < FMinHeight then + Value := FMinHeight; + FMaxHeight := Value; + SetHeight(FHeight); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetMinHeight(Value : Integer); + +begin + if Value < 0 then + Value := 0; + if Value > FMaxHeight then + Value := FMaxHeight; + FMinHeight := Value; + SetHeight(FHeight); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetOptions(Value : TVTHeaderOptions); + +var + ToBeSet, ToBeCleared : TVTHeaderOptions; + +begin + ToBeSet := Value - FOptions; + ToBeCleared := FOptions - Value; + FOptions := Value; + + if (hoAutoResize in (ToBeSet + ToBeCleared)) and (FColumns.Count > 0) then + begin + TVirtualTreeColumnsCracker(FColumns).AdjustAutoSize(InvalidColumn); + if Tree.HandleAllocated then + begin + Tree.UpdateHorizontalScrollBar(False); + if hoAutoResize in ToBeSet then + Tree.Invalidate; + end; + end; + + if not (csLoading in Tree.ComponentState) and Tree.HandleAllocated then + begin + if hoVisible in (ToBeSet + ToBeCleared) then + RecalculateHeader; + Invalidate(nil); + Tree.Invalidate; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetParentFont(Value : Boolean); + +begin + if FParentFont <> Value then + begin + FParentFont := Value; + if FParentFont then + FFont.Assign(TBaseVirtualTree(FOwner).Font); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetSortColumn(Value : TColumnIndex); + +begin + if csLoading in Tree.ComponentState then + FSortColumn := Value + else + DoSetSortColumn(Value, FSortDirection); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetSortDirection(const Value : TSortDirection); + +begin + if Value <> FSortDirection then + begin + FSortDirection := Value; + Invalidate(nil); + if ((toAutoSort in Tree.TreeOptions.AutoOptions) or (hoHeaderClickAutoSort in Options)) and (Tree.UpdateCount = 0) then + Tree.SortTree(FSortColumn, FSortDirection, True); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.CanSplitterResize(P : TPoint) : Boolean; + +begin + Result := hoHeightResize in FOptions; + DoCanSplitterResize(P, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SetStyle(Value : TVTHeaderStyle); + +begin + if FStyle <> Value then + begin + FStyle := Value; + if not (csLoading in Tree.ComponentState) then + Invalidate(nil); + end; +end; + +procedure TVTHeader.StyleChanged(); +begin + AutoScale(); //Elements may have chnaged in size +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.CanWriteColumns : Boolean; + +//descendants may override this to optionally prevent column writing (e.g. if they are build dynamically). + +begin + Result := True; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.ChangeScale(M, D : Integer; isDpiChange : Boolean); +var + I : Integer; +begin + //This method is only executed if toAutoChangeScale is set + FMinHeight := MulDiv(FMinHeight, M, D); + FMaxHeight := MulDiv(FMaxHeight, M, D); + Self.Height := MulDiv(FHeight, M, D); + if not ParentFont then + Font.Height := MulDiv(Font.Height, M, D); + //Scale the columns widths too + for I := 0 to FColumns.Count - 1 do + TVirtualTreeColumnCracker(Self.FColumns[I]).ChangeScale(M, D, isDpiChange); + if not isDpiChange then + AutoScale(); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.DetermineSplitterIndex(P : TPoint) : Boolean; + +//Tries to find the index of that column whose right border corresponds to P. +//Result is True if column border was hit (with -3..+5 pixels tolerance). +//For continuous resizing the current track index and the column's left/right border are set. +//Note: The hit test is checking from right to left (or left to right in RTL mode) to make enlarging of zero-sized +//columns possible. + +var + VisibleFixedWidth : Integer; + SplitPoint : Integer; + + //--------------- local function -------------------------------------------- + + function IsNearBy(IsFixedCol : Boolean; LeftTolerance, RightTolerance : Integer) : Boolean; + + begin + if IsFixedCol then + Result := (P.X < SplitPoint + Tree.EffectiveOffsetX + RightTolerance) and (P.X > SplitPoint + Tree.EffectiveOffsetX - LeftTolerance) + else + Result := (P.X > VisibleFixedWidth) and (P.X < SplitPoint + RightTolerance) and (P.X > SplitPoint - LeftTolerance); + end; + +//--------------- end local function ---------------------------------------- + +var + I : Integer; + LeftTolerance : Integer; //The area left of the column divider which allows column resizing +begin + Result := False; + + if FColumns.Count > 0 then + begin + FColumns.TrackIndex := NoColumn; + VisibleFixedWidth := FColumns.GetVisibleFixedWidth; + LeftTolerance := Round(SplitterHitTolerance * 0.6); + if Tree.UseRightToLeftAlignment then + begin + SplitPoint := - Tree.EffectiveOffsetX; + if FColumns.TotalWidth < Tree.ClientWidth then + Inc(SplitPoint, Tree.ClientWidth - FColumns.TotalWidth); + + for I := 0 to FColumns.Count - 1 do + with TVirtualTreeColumnsCracker(FColumns), Items[PositionToIndex[I]] do + if coVisible in Options then + begin + if IsNearBy(coFixed in Options, LeftTolerance, SplitterHitTolerance - LeftTolerance) then + begin + if CanSplitterResize(P, PositionToIndex[I]) then + begin + Result := True; + TrackIndex := PositionToIndex[I]; + + //Keep the right border of this column. This and the current mouse position + //directly determine the current column width. + FTrackPoint.X := SplitPoint + IfThen(coFixed in Options, Tree.EffectiveOffsetX) + Width; + FTrackPoint.Y := P.Y; + Break; + end; + end; + Inc(SplitPoint, Width); + end; + end + else + begin + SplitPoint := - Tree.EffectiveOffsetX + FColumns.TotalWidth; + + for I := FColumns.Count - 1 downto 0 do + with TVirtualTreeColumnsCracker(FColumns), Items[PositionToIndex[I]] do + if coVisible in Options then + begin + if IsNearBy(coFixed in Options, SplitterHitTolerance - LeftTolerance, LeftTolerance) then + begin + if CanSplitterResize(P, PositionToIndex[I]) then + begin + Result := True; + TrackIndex := PositionToIndex[I]; + + //Keep the left border of this column. This and the current mouse position + //directly determine the current column width. + FTrackPoint.X := SplitPoint + IfThen(coFixed in Options, Tree.EffectiveOffsetX) - Width; + FTrackPoint.Y := P.Y; + Break; + end; + end; + Dec(SplitPoint, Width); + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.DoAfterAutoFitColumn(Column : TColumnIndex); + +begin + if Assigned(Tree.OnAfterAutoFitColumn) then + Tree.OnAfterAutoFitColumn(Self, Column); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.DoAfterColumnWidthTracking(Column : TColumnIndex); + +//Tell the application that a column width tracking operation has been finished. + +begin + if Assigned(Tree.OnAfterColumnWidthTracking) then + Tree.OnAfterColumnWidthTracking(Self, Column); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.DoAfterHeightTracking; + +//Tell the application that a height tracking operation has been finished. + +begin + if Assigned(Tree.OnAfterHeaderHeightTracking) then + Tree.OnAfterHeaderHeightTracking(Self); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.DoBeforeAutoFitColumn(Column : TColumnIndex; SmartAutoFitType : TSmartAutoFitType) : Boolean; + +//Query the application if we may autofit a column. + +begin + Result := True; + if Assigned(Tree.OnBeforeAutoFitColumn) then + Tree.OnBeforeAutoFitColumn(Self, Column, SmartAutoFitType, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.DoBeforeColumnWidthTracking(Column : TColumnIndex; Shift : TShiftState); + +//Tell the a application that a column width tracking operation may begin. + +begin + if Assigned(Tree.OnBeforeColumnWidthTracking) then + Tree.OnBeforeColumnWidthTracking(Self, Column, Shift); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.DoBeforeHeightTracking(Shift : TShiftState); + +//Tell the application that a height tracking operation may begin. + +begin + if Assigned(Tree.OnBeforeHeaderHeightTracking) then + Tree.OnBeforeHeaderHeightTracking(Self, Shift); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.DoCanSplitterResize(P : TPoint; var Allowed : Boolean); +begin + if Assigned(Tree.OnCanSplitterResizeHeader) then + Tree.OnCanSplitterResizeHeader(Self, P, Allowed); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.DoColumnWidthDblClickResize(Column : TColumnIndex; P : TPoint; Shift : TShiftState) : Boolean; + +//Queries the application whether a double click on the column splitter should resize the column. + +begin + Result := True; + if Assigned(Tree.OnColumnWidthDblClickResize) then + Tree.OnColumnWidthDblClickResize(Self, Column, Shift, P, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.DoColumnWidthTracking(Column : TColumnIndex; Shift : TShiftState; var TrackPoint : TPoint; P : TPoint) : Boolean; + +begin + Result := True; + if Assigned(Tree.OnColumnWidthTracking) then + Tree.OnColumnWidthTracking(Self, Column, Shift, TrackPoint, P, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.DoGetPopupMenu(Column : TColumnIndex; Position : TPoint) : TPopupMenu; + +//Queries the application whether there is a column specific header popup menu. + +var + AskParent : Boolean; + +begin + Result := PopupMenu; + if Assigned(Tree.OnGetPopupMenu) then + Tree.OnGetPopupMenu(TBaseVirtualTree(FOwner), nil, Column, Position, AskParent, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.DoHeightTracking(var P : TPoint; Shift : TShiftState) : Boolean; + +begin + Result := True; + if Assigned(Tree.OnHeaderHeightTracking) then + Tree.OnHeaderHeightTracking(Self, P, Shift, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.DoHeightDblClickResize(var P : TPoint; Shift : TShiftState) : Boolean; + +begin + Result := True; + if Assigned(Tree.OnHeaderHeightDblClickResize) then + Tree.OnHeaderHeightDblClickResize(Self, P, Shift, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.DoSetSortColumn(Value : TColumnIndex; pSortDirection : TSortDirection); + +begin + if Value < NoColumn then + Value := NoColumn; + if Value > Columns.Count - 1 then + Value := Columns.Count - 1; + if FSortColumn <> Value then + begin + if FSortColumn > NoColumn then + Invalidate(Columns[FSortColumn]); + FSortColumn := Value; + FSortDirection := pSortDirection; + if FSortColumn > NoColumn then + Invalidate(Columns[FSortColumn]); + if ((toAutoSort in Tree.TreeOptions.AutoOptions) or (hoHeaderClickAutoSort in Options)) and (Tree.UpdateCount = 0) then + Tree.SortTree(FSortColumn, FSortDirection, True); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.DragTo(P : TPoint); + +//Moves the drag image to a new position, which is determined from the passed point P and the previous +//mouse position. + +var + I, NewTarget : Integer; + //optimized drag image move support + ClientP : TPoint; + Left, Right : Integer; + NeedRepaint : Boolean; //True if the screen needs an update (changed drop target or drop side) + +begin + //Determine new drop target and which side of it is prefered. + ClientP := Tree.ScreenToClient(P); + //Make coordinates relative to (0, 0) of the non-client area. + Inc(ClientP.Y, FHeight); + NewTarget := FColumns.ColumnFromPosition(ClientP); + NeedRepaint := (NewTarget <> InvalidColumn) and (NewTarget <> FColumns.DropTarget); + if NewTarget >= 0 then + begin + FColumns.GetColumnBounds(NewTarget, Left, Right); + if (ClientP.X < ((Left + Right) div 2)) <> FColumns.DropBefore then + begin + NeedRepaint := True; + FColumns.DropBefore := not FColumns.DropBefore; + end; + end; + + if NeedRepaint then + begin + //Invalidate columns which need a repaint. + if FColumns.DropTarget > NoColumn then + begin + I := FColumns.DropTarget; + FColumns.DropTarget := NoColumn; + Invalidate(FColumns.Items[I]); + end; + if (NewTarget > NoColumn) and (NewTarget <> FColumns.DropTarget) then + begin + Invalidate(FColumns.Items[NewTarget]); + FColumns.DropTarget := NewTarget; + end; + end; + + //Fix for various problems mentioned in issue 248. + if NeedRepaint then + begin + UpdateWindow(FOwner.Handle); + //The new routine recaptures the backup image after the updatewindow + //Note: We could have called this unconditionally but when called + //over the tree, doesn't capture the background image. Since our + //problems are in painting of the header, we call it only when the + //drag image is over the header. + if + //determine the case when the drag image is or was on the header area + (InHeader(FOwner.ScreenToClient(FDragImage.LastPosition)) or InHeader(FOwner.ScreenToClient(FDragImage.ImagePosition))) then + begin + GDIFlush; + TBaseVirtualTreeCracker(FOwner).UpdateWindowAndDragImage(TBaseVirtualTree(FOwner), TBaseVirtualTreeCracker(FOwner).HeaderRect, True, True); + end; + //since we took care of UpdateWindow above, there is no need to do an + //update window again by sending NeedRepaint. So switch off the second parameter. + NeedRepaint := False; + end; + + FDragImage.DragTo(P, NeedRepaint); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.FixedAreaConstraintsChanged(Sender : TObject); + +//This method gets called when FFixedAreaConstraints is changed. + +begin + if Tree.HandleAllocated then + RescaleHeader + else + Include(FStates, hsNeedScaling); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.GetColumnsClass : TVirtualTreeColumnsClass; + +//Returns the class to be used for the actual column implementation. descendants may optionally override this and +//return their own class. + +begin + Result := TVirtualTreeColumns; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.GetOwner : TPersistent; + +begin + Result := FOwner; +end; + +function TVTHeader.GetRestoreSelectionColumnIndex : Integer; +begin + if FRestoreSelectionColumnIndex >= 0 then + Result := FRestoreSelectionColumnIndex + else + Result := MainColumn; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.GetShiftState : TShiftState; + +begin + Result := []; + if GetKeyState(VK_SHIFT) < 0 then + Include(Result, ssShift); + if GetKeyState(VK_CONTROL) < 0 then + Include(Result, ssCtrl); + if GetKeyState(VK_MENU) < 0 then + Include(Result, ssAlt); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.HandleHeaderMouseMove(var Message : TWMMouseMove) : Boolean; + +var + P : TPoint; + NextColumn, I : TColumnIndex; + NewWidth : Integer; + +begin + Result := False; + with Message do + begin + P := Point(XPos, YPos); + if hsColumnWidthTrackPending in FStates then + begin + Tree.StopTimer(HeaderTimer); + FStates := FStates - [hsColumnWidthTrackPending] + [hsColumnWidthTracking]; + HandleHeaderMouseMove := True; + Result := 0; + end + else if hsHeightTrackPending in FStates then + begin + Tree.StopTimer(HeaderTimer); + FStates := FStates - [hsHeightTrackPending] + [hsHeightTracking]; + HandleHeaderMouseMove := True; + Result := 0; + end + else if hsColumnWidthTracking in FStates then + begin + if DoColumnWidthTracking(FColumns.TrackIndex, GetShiftState, FTrackPoint, P) then + begin + if Tree.UseRightToLeftAlignment then + begin + NewWidth := FTrackPoint.X - XPos; + NextColumn := FColumns.GetPreviousVisibleColumn(FColumns.TrackIndex); + end + else + begin + NewWidth := XPos - FTrackPoint.X; + NextColumn := FColumns.GetNextVisibleColumn(FColumns.TrackIndex); + end; + + //The autosized column cannot be resized using the mouse normally. Instead we resize the next + //visible column, so it look as we directly resize the autosized column. + if (hoAutoResize in FOptions) and (FColumns.TrackIndex = FAutoSizeIndex) and (NextColumn > NoColumn) and (coResizable in FColumns[NextColumn].Options) and + (FColumns[FColumns.TrackIndex].MinWidth < NewWidth) and (FColumns[FColumns.TrackIndex].MaxWidth > NewWidth) then + FColumns[NextColumn].Width := FColumns[NextColumn].Width - NewWidth + FColumns[FColumns.TrackIndex].Width + else + FColumns[FColumns.TrackIndex].Width := NewWidth; //1 EListError seen here (List index out of bounds (-1)) since 10/2013 + end; + HandleHeaderMouseMove := True; + Result := 0; + end + else if hsHeightTracking in FStates then + begin + if DoHeightTracking(P, GetShiftState) then + SetHeight(Integer(FHeight) + P.Y); + HandleHeaderMouseMove := True; + Result := 0; + end + else + begin + if hsDragPending in FStates then + begin + P := Tree.ClientToScreen(P); + //start actual dragging if allowed + if (hoDrag in FOptions) and Tree.DoHeaderDragging(TVirtualTreeColumnsCracker(FColumns).DownIndex) then + begin + if ((Abs(FDragStart.X - P.X) > Mouse.DragThreshold) or (Abs(FDragStart.Y - P.Y) > Mouse.DragThreshold)) then + begin + Tree.StopTimer(HeaderTimer); + with TVirtualTreeColumnsCracker(FColumns) do + begin + I := DownIndex; + DownIndex := NoColumn; + HoverIndex := NoColumn; + if I > NoColumn then + Invalidate(FColumns[I]); + end; + PrepareDrag(P, FDragStart); + FStates := FStates - [hsDragPending] + [hsDragging]; + HandleHeaderMouseMove := True; + Result := 0; + end; + end; + end + else if hsDragging in FStates then + begin + DragTo(Tree.ClientToScreen(Point(XPos, YPos))); + HandleHeaderMouseMove := True; + Result := 0; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.HandleMessage(var Message : TMessage) : Boolean; + +//The header gets here the opportunity to handle certain messages before they reach the tree. This is important +//because the tree needs to handle various non-client area messages for the header as well as some dragging/tracking +//events. +//By returning True the message will not be handled further, otherwise the message is then dispatched +//to the proper message handlers. + +var + P : TPoint; + R : TRect; + I : TColumnIndex; + OldPosition : Integer; + HitIndex : TColumnIndex; + NewCursor : HCURSOR; + Button : TMouseButton; + IsInHeader, IsHSplitterHit, IsVSplitterHit : Boolean; + + //--------------- local function -------------------------------------------- + + function HSplitterHit : Boolean; + + var + NextCol : TColumnIndex; + + begin + Result := (hoColumnResize in FOptions) and DetermineSplitterIndex(P); + if Result and not InHeader(P) then + begin + NextCol := FColumns.GetNextVisibleColumn(FColumns.TrackIndex); + if not (coFixed in FColumns[FColumns.TrackIndex].Options) or (NextCol <= NoColumn) or (coFixed in FColumns[NextCol].Options) or (P.Y > Integer(Tree.RangeY)) then + Result := False; + end; + end; + +//--------------- end local function ---------------------------------------- + +begin + Result := False; + case Message.Msg of + WM_SIZE : + begin + if not (tsWindowCreating in TBaseVirtualTreeCracker(FOwner).TreeStates) then + if (hoAutoResize in FOptions) and not (hsAutoSizing in FStates) then + begin + TVirtualTreeColumnsCracker(FColumns).AdjustAutoSize(InvalidColumn); + Invalidate(nil); + end + else if not (hsScaling in FStates) then + begin + RescaleHeader; + Invalidate(nil); + end; + end; + CM_PARENTFONTCHANGED : + if FParentFont then + FFont.Assign(TBaseVirtualTreeCracker(FOwner).Font); + CM_BIDIMODECHANGED : + for I := 0 to FColumns.Count - 1 do + if coParentBiDiMode in FColumns[I].Options then + FColumns[I].ParentBiDiModeChanged; + WM_NCMBUTTONDOWN : + begin + with TWMNCMButtonDown(Message) do + P := Tree.ScreenToClient(Point(XCursor, YCursor)); + if InHeader(P) then + TBaseVirtualTreeCracker(FOwner).DoHeaderMouseDown(mbMiddle, GetShiftState, P.X, P.Y + Integer(FHeight)); + end; + WM_NCMBUTTONUP : + begin + with TWMNCMButtonUp(Message) do + P := FOwner.ScreenToClient(Point(XCursor, YCursor)); + if InHeader(P) then + begin + with TVirtualTreeColumnsCracker(FColumns) do + begin + HandleClick(P, mbMiddle, True, False); + TBaseVirtualTreeCracker(FOwner).DoHeaderMouseUp(mbMiddle, GetShiftState, P.X, P.Y + Integer(Self.FHeight)); + DownIndex := NoColumn; + CheckBoxHit := False; + end; + end; + end; + WM_LBUTTONDBLCLK, WM_NCLBUTTONDBLCLK, WM_NCMBUTTONDBLCLK, WM_NCRBUTTONDBLCLK : + begin + if Message.Msg <> WM_LBUTTONDBLCLK then + with TWMNCLButtonDblClk(Message) do + P := FOwner.ScreenToClient(Point(XCursor, YCursor)) + else + with TWMLButtonDblClk(Message) do + P := Point(XPos, YPos); + + if (hoHeightDblClickResize in FOptions) and InHeaderSplitterArea(P) and (FDefaultHeight > 0) then + begin + if DoHeightDblClickResize(P, GetShiftState) and (FDefaultHeight > 0) then + SetHeight(FMinHeight); + Result := True; + end + else if HSplitterHit and ((Message.Msg = WM_NCLBUTTONDBLCLK) or (Message.Msg = WM_LBUTTONDBLCLK)) and (hoDblClickResize in FOptions) and (FColumns.TrackIndex > NoColumn) + then + begin + //If the click was on a splitter then resize column to smallest width. + if DoColumnWidthDblClickResize(FColumns.TrackIndex, P, GetShiftState) then + AutoFitColumns(True, smaUseColumnOption, FColumns[FColumns.TrackIndex].Position, FColumns[FColumns.TrackIndex].Position); + Message.Result := 0; + Result := True; + end + else if InHeader(P) and (Message.Msg <> WM_LBUTTONDBLCLK) then + begin + case Message.Msg of + WM_NCMBUTTONDBLCLK : + Button := mbMiddle; + WM_NCRBUTTONDBLCLK : + Button := mbRight; + else + //WM_NCLBUTTONDBLCLK + Button := mbLeft; + end; + if Button = mbLeft then + TVirtualTreeColumnsCracker(FColumns).AdjustDownColumn(P); + TVirtualTreeColumnsCracker(FColumns).HandleClick(P, Button, True, True); + end; + end; + //The "hot" area of the headers horizontal splitter is partly within the client area of the the tree, so we need + //to handle WM_LBUTTONDOWN here, too. + WM_LBUTTONDOWN, WM_NCLBUTTONDOWN : + begin + + Application.CancelHint; + + if not (csDesigning in Tree.ComponentState) then + begin + with Tree do + begin + //make sure no auto scrolling is active... + StopTimer(ScrollTimer); + DoStateChange([], [tsScrollPending, tsScrolling]); + //... pending editing is cancelled (actual editing remains active) + StopTimer(EditTimer); + DoStateChange([], [tsEditPending]); + end; + end; + + if Message.Msg = WM_LBUTTONDOWN then + //Coordinates are already client area based. + with TWMLButtonDown(Message) do + begin + P := Point(XPos, YPos); + //#909 + FDragStart := Tree.ClientToScreen(P); + end + else + with TWMNCLButtonDown(Message) do + begin + //want the drag start point in screen coordinates + FDragStart := Point(XCursor, YCursor); + P := Tree.ScreenToClient(FDragStart); + end; + + IsInHeader := InHeader(P); + //in design-time header columns are always resizable + if (csDesigning in Tree.ComponentState) then + IsVSplitterHit := InHeaderSplitterArea(P) + else + IsVSplitterHit := InHeaderSplitterArea(P) and CanSplitterResize(P); + IsHSplitterHit := HSplitterHit; + + if IsVSplitterHit or IsHSplitterHit then + begin + FTrackStart := P; + TVirtualTreeColumnsCracker(FColumns).HoverIndex := NoColumn; + if IsVSplitterHit then + begin + if not (csDesigning in Tree.ComponentState) then + DoBeforeHeightTracking(GetShiftState); + Include(FStates, hsHeightTrackPending); + end + else + begin + if not (csDesigning in Tree.ComponentState) then + DoBeforeColumnWidthTracking(FColumns.TrackIndex, GetShiftState); + Include(FStates, hsColumnWidthTrackPending); + end; + + SetCapture(Tree.Handle); + Result := True; + Message.Result := 0; + end + else if IsInHeader then + begin + HitIndex := TVirtualTreeColumnsCracker(FColumns).AdjustDownColumn(P); + //in design-time header columns are always draggable + if ((csDesigning in Tree.ComponentState) and (HitIndex > NoColumn)) or ((hoDrag in FOptions) and (HitIndex > NoColumn) and (coDraggable in FColumns[HitIndex].Options)) + then + begin + //Show potential drag operation. + //Disabled columns do not start a drag operation because they can't be clicked. + Include(FStates, hsDragPending); + SetCapture(Tree.Handle); + Result := True; + Message.Result := 0; + end; + end; + + //This is a good opportunity to notify the application. + if not (csDesigning in Tree.ComponentState) and IsInHeader then + TBaseVirtualTreeCracker(FOwner).DoHeaderMouseDown(mbLeft, GetShiftState, P.X, P.Y + Integer(FHeight)); + end; + WM_NCRBUTTONDOWN : + begin + with TWMNCRButtonDown(Message) do + P := FOwner.ScreenToClient(Point(XCursor, YCursor)); + if InHeader(P) then + TBaseVirtualTreeCracker(FOwner).DoHeaderMouseDown(mbRight, GetShiftState, P.X, P.Y + Integer(FHeight)); + end; + WM_NCRBUTTONUP : + if not (csDesigning in FOwner.ComponentState) then + with TWMNCRButtonUp(Message) do + begin + Application.CancelHint; + P := FOwner.ScreenToClient(Point(XCursor, YCursor)); + if InHeader(P) then + begin + HandleMessage := TVirtualTreeColumnsCracker(FColumns).HandleClick(P, mbRight, True, False); + TBaseVirtualTreeCracker(FOwner).DoHeaderMouseUp(mbRight, GetShiftState, P.X, P.Y + Integer(FHeight)); + end; + end; + //When the tree window has an active mouse capture then we only get "client-area" messages. + WM_LBUTTONUP, WM_NCLBUTTONUP : + begin + Application.CancelHint; + + if FStates <> [] then + begin + ReleaseCapture; + if hsDragging in FStates then + begin + //successfull dragging moves columns + with TWMLButtonUp(Message) do + P := Tree.ClientToScreen(Point(XPos, YPos)); + GetWindowRect(Tree.Handle, R); + with FColumns do + begin + FDragImage.EndDrag; + + //Problem fixed: + //Column Header does not paint correctly after a drop in certain conditions + // ** The conditions are, drag is across header, mouse is not moved after + //the drop and the graphics hardware is slow in certain operations (encountered + //on Windows 10). + //Fix for the problem on certain systems where the dropped column header + //does not appear in the new position if the mouse is not moved after + //the drop. The reason is that the restore backup image operation (BitBlt) + //in the above EndDrag is slower than the header repaint in the code below + //and overlaps the new changed header with the older image. + //This happens because BitBlt seems to operate in its own thread in the + //graphics hardware and finishes later than the following code. + // + //To solve this problem, we introduce a small delay here so that the + //changed header in the following code is correctly repainted after + //the delayed BitBlt above has finished operation to restore the old + //backup image. + sleep(50); + + if (DropTarget > - 1) and (DropTarget <> DragIndex) and PtInRect(R, P) then + begin + OldPosition := FColumns[DragIndex].Position; + if FColumns.DropBefore then + begin + if FColumns[DragIndex].Position < FColumns[DropTarget].Position then + FColumns[DragIndex].Position := Max(0, FColumns[DropTarget].Position - 1) + else + FColumns[DragIndex].Position := FColumns[DropTarget].Position; + end + else + begin + if FColumns[DragIndex].Position < FColumns[DropTarget].Position then + FColumns[DragIndex].Position := FColumns[DropTarget].Position + else + FColumns[DragIndex].Position := FColumns[DropTarget].Position + 1; + end; + Tree.DoHeaderDragged(DragIndex, OldPosition); + end + else + Tree.DoHeaderDraggedOut(DragIndex, P); + DropTarget := NoColumn; + end; + Invalidate(nil); + end; + Result := True; + Message.Result := 0; + end; + + case Message.Msg of + WM_LBUTTONUP : + with TWMLButtonUp(Message) do + begin + with TVirtualTreeColumnsCracker(FColumns) do + begin + if DownIndex > NoColumn then + HandleClick(Point(XPos, YPos), mbLeft, False, False); + end; + if FStates <> [] then + TBaseVirtualTreeCracker(FOwner).DoHeaderMouseUp(mbLeft, KeysToShiftState(Keys), XPos, YPos); + end; + WM_NCLBUTTONUP : + with TWMNCLButtonUp(Message) do + begin + P := FOwner.ScreenToClient(Point(XCursor, YCursor)); + TVirtualTreeColumnsCracker(FColumns).HandleClick(P, mbLeft, False, False); + TBaseVirtualTreeCracker(FOwner).DoHeaderMouseUp(mbLeft, GetShiftState, P.X, P.Y + Integer(FHeight)); + end; + end; + + if FColumns.TrackIndex > NoColumn then + begin + if hsColumnWidthTracking in FStates then + DoAfterColumnWidthTracking(FColumns.TrackIndex); + Invalidate(Columns[FColumns.TrackIndex]); + FColumns.TrackIndex := NoColumn; + end; + with TVirtualTreeColumnsCracker(FColumns) do + begin + if DownIndex > NoColumn then + begin + Invalidate(FColumns[DownIndex]); + DownIndex := NoColumn; + end; + end; + if hsHeightTracking in FStates then + DoAfterHeightTracking; + + FStates := FStates - [hsDragging, hsDragPending, hsColumnWidthTracking, hsColumnWidthTrackPending, hsHeightTracking, hsHeightTrackPending]; + end; //WM_NCLBUTTONUP + //hovering, mouse leave detection + WM_NCMOUSEMOVE : + with TWMNCMouseMove(Message), TVirtualTreeColumnsCracker(FColumns) do + begin + P := Tree.ScreenToClient(Point(XCursor, YCursor)); + Tree.DoHeaderMouseMove(GetShiftState, P.X, P.Y + Integer(FHeight)); + if InHeader(P) and ((AdjustHoverColumn(P)) or ((DownIndex >= 0) and (HoverIndex <> DownIndex))) then + begin + //We need a mouse leave detection from here for the non client area. + //TODO: The best solution available would be the TrackMouseEvent API. + //With the drop of the support of Win95 totally and WinNT4 we should replace the timer. + Tree.StopTimer(HeaderTimer); + SetTimer(Tree.Handle, HeaderTimer, 50, nil); + //use Delphi's internal hint handling for header hints too + if hoShowHint in FOptions then + begin + //client coordinates! + XCursor := P.X; + YCursor := P.Y + Integer(FHeight); + Application.HintMouseMessage(FOwner, Message); + end; + end; + end; + WM_TIMER : + if TWMTimer(Message).TimerID = HeaderTimer then + begin + //determine current mouse position to check if it left the window + GetCursorPos(P); + P := Tree.ScreenToClient(P); + with TVirtualTreeColumnsCracker(FColumns) do + begin + if not InHeader(P) or ((DownIndex > NoColumn) and (HoverIndex <> DownIndex)) then + begin + Tree.StopTimer(HeaderTimer); + HoverIndex := NoColumn; + ClickIndex := NoColumn; + DownIndex := NoColumn; + CheckBoxHit := False; + Result := True; + Message.Result := 0; + Invalidate(nil); + end; + end; + end; + WM_MOUSEMOVE : //mouse capture and general message redirection + Result := HandleHeaderMouseMove(TWMMouseMove(Message)); + WM_SETCURSOR : + //Feature: design-time header + if (FStates = []) then + begin + //Retrieve last cursor position (GetMessagePos does not work here, I don't know why). + GetCursorPos(P); + + //Is the mouse in the header rectangle and near the splitters? + P := Tree.ScreenToClient(P); + IsHSplitterHit := HSplitterHit; + //in design-time header columns are always resizable + if (csDesigning in Tree.ComponentState) then + IsVSplitterHit := InHeaderSplitterArea(P) + else + IsVSplitterHit := InHeaderSplitterArea(P) and CanSplitterResize(P); + + if IsVSplitterHit or IsHSplitterHit then + begin + NewCursor := Screen.Cursors[Tree.Cursor]; + if IsVSplitterHit and ((hoHeightResize in FOptions) or (csDesigning in Tree.ComponentState)) then + NewCursor := Screen.Cursors[crVertSplit] + else if IsHSplitterHit then + NewCursor := Screen.Cursors[crHeaderSplit]; + + if not (csDesigning in Tree.ComponentState) then + Tree.DoGetHeaderCursor(NewCursor); + Result := NewCursor <> Screen.Cursors[crDefault]; + if Result then + begin + WinApi.Windows.SetCursor(NewCursor); + Message.Result := 1; + end; + end; + end + else + begin + Message.Result := 1; + Result := True; + end; + WM_KEYDOWN, WM_KILLFOCUS : + if (Message.Msg = WM_KILLFOCUS) or (TWMKeyDown(Message).CharCode = VK_ESCAPE) then + begin + if hsDragging in FStates then + begin + ReleaseCapture; + FDragImage.EndDrag; + Exclude(FStates, hsDragging); + FColumns.DropTarget := NoColumn; + Invalidate(nil); + Result := True; + Message.Result := 0; + end + else + begin + if [hsColumnWidthTracking, hsHeightTracking] * FStates <> [] then + begin + ReleaseCapture; + if hsColumnWidthTracking in FStates then + DoAfterColumnWidthTracking(FColumns.TrackIndex); + if hsHeightTracking in FStates then + DoAfterHeightTracking; + Result := True; + Message.Result := 0; + end; + + FStates := FStates - [hsColumnWidthTracking, hsColumnWidthTrackPending, hsHeightTracking, hsHeightTrackPending]; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.ImageListChange(Sender : TObject); + +begin + if not (csDestroying in Tree.ComponentState) then + Invalidate(nil); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.PrepareDrag(P, Start : TPoint); + +//Initializes dragging of the header, P is the current mouse postion and Start the initial mouse position. + +var + Image : TBitmap; + ImagePos : TPoint; + DragColumn : TVirtualTreeColumn; + RTLOffset : Integer; + +begin + //Determine initial position of drag image (screen coordinates). + FColumns.DropTarget := NoColumn; + Start := Tree.ScreenToClient(Start); + Inc(Start.Y, FHeight); + FColumns.DragIndex := FColumns.ColumnFromPosition(Start); + DragColumn := FColumns[FColumns.DragIndex]; + + Image := TBitmap.Create; + with Image do + try + PixelFormat := pf32Bit; + SetSize(DragColumn.Width, FHeight); + + //Erase the entire image with the color key value, for the case not everything + //in the image is covered by the header image. + Canvas.Brush.Color := clBtnFace; + Canvas.FillRect(Rect(0, 0, Width, Height)); + + if Tree.UseRightToLeftAlignment then + RTLOffset := Tree.ComputeRTLOffset + else + RTLOffset := 0; + with DragColumn do + FColumns.PaintHeader(Canvas, Rect(Left, 0, Left + Width, Height), Point( - RTLOffset, 0), RTLOffset); + + if Tree.UseRightToLeftAlignment then + ImagePos := Tree.ClientToScreen(Point(DragColumn.Left + Tree.ComputeRTLOffset(True), 0)) + else + ImagePos := Tree.ClientToScreen(Point(DragColumn.Left, 0)); + //Column rectangles are given in local window coordinates not client coordinates. + Dec(ImagePos.Y, FHeight); + + if hoRestrictDrag in FOptions then + FDragImage.MoveRestriction := dmrHorizontalOnly + else + FDragImage.MoveRestriction := dmrNone; + FDragImage.PrepareDrag(Image, ImagePos, P, nil); + FDragImage.ShowDragImage; + finally + Image.Free; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.ReadColumns(Reader : TReader); + +begin + Include(FStates, hsLoading); + Columns.Clear; + Reader.ReadValue; + Reader.ReadCollection(Columns); + Exclude(FStates, hsLoading); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.RecalculateHeader; + +//Initiate a recalculation of the non-client area of the owner tree. + +begin + if Tree.HandleAllocated then + begin + Tree.UpdateHeaderRect; + SetWindowPos(Tree.Handle, 0, 0, 0, 0, 0, SWP_FRAMECHANGED or SWP_NOMOVE or SWP_NOACTIVATE or SWP_NOOWNERZORDER or SWP_NOSENDCHANGING or SWP_NOSIZE or SWP_NOZORDER); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.RescaleHeader; + +//Rescale the fixed elements (fixed columns, header itself) to FixedAreaConstraints. + +var + FixedWidth, MaxFixedWidth, MinFixedWidth : Integer; + + //--------------- local function -------------------------------------------- + + procedure ComputeConstraints; + + var + I : TColumnIndex; + + begin + with FColumns do + begin + I := GetFirstVisibleColumn; + while I > NoColumn do + begin + if (coFixed in FColumns[I].Options) and (FColumns[I].Width < FColumns[I].MinWidth) then + TVirtualTreeColumnCracker(FColumns[I]).InternalSetWidth(FColumns[I].MinWidth); //SetWidth has side effects and this bypasses them + I := GetNextVisibleColumn(I); + end; + FixedWidth := GetVisibleFixedWidth; + end; + + with FFixedAreaConstraints do + begin + MinFixedWidth := (Tree.ClientWidth * FMinWidthPercent) div 100; + MaxFixedWidth := (Tree.ClientWidth * FMaxWidthPercent) div 100; + end; + end; + +//----------- end local function -------------------------------------------- + +begin + if ([csLoading, csReading, csWriting, csDestroying] * Tree.ComponentState = []) and not (hsLoading in FStates) and Tree.HandleAllocated then + begin + Include(FStates, hsScaling); + + SetHeight(FHeight); + RecalculateHeader; + + with FFixedAreaConstraints do + if (FMaxWidthPercent > 0) or (FMinWidthPercent > 0) or (FMinHeightPercent > 0) or (FMaxHeightPercent > 0) then + begin + ComputeConstraints; + + with FColumns do + if (FMaxWidthPercent > 0) and (FixedWidth > MaxFixedWidth) then + ResizeColumns(MaxFixedWidth - FixedWidth, 0, Count - 1, [coVisible, coFixed]) + else if (FMinWidthPercent > 0) and (FixedWidth < MinFixedWidth) then + ResizeColumns(MinFixedWidth - FixedWidth, 0, Count - 1, [coVisible, coFixed]); + + TVirtualTreeColumnsCracker(FColumns).UpdatePositions; + end; + + Exclude(FStates, hsScaling); + Exclude(FStates, hsNeedScaling); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.UpdateMainColumn(); + +//Called once the load process of the owner tree is done. + +begin + if FMainColumn < 0 then + MainColumn := 0; + if FMainColumn > FColumns.Count - 1 then + MainColumn := FColumns.Count - 1; + if (FMainColumn >= 0) and not (coVisible in Self.Columns[FMainColumn].Options) then + begin + //Issue #946: Choose new MainColumn if current one ist not visible + MainColumn := Self.Columns.GetFirstVisibleColumn(); + end +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.UpdateSpringColumns; + +var + I : TColumnIndex; + SpringCount : Integer; + Sign : Integer; + ChangeBy : Single; + Difference : Single; + NewAccumulator : Single; + +begin + with Tree do + ChangeBy := HeaderRect.Right - HeaderRect.Left - FLastWidth; + if (hoAutoSpring in FOptions) and (FLastWidth <> 0) and (ChangeBy <> 0) then + begin + //Stay positive if downsizing the control. + if ChangeBy < 0 then + Sign := - 1 + else + Sign := 1; + ChangeBy := Abs(ChangeBy); + //Count how many columns have spring enabled. + SpringCount := 0; + for I := 0 to FColumns.Count - 1 do + if [coVisible, coAutoSpring] * FColumns[I].Options = [coVisible, coAutoSpring] then + Inc(SpringCount); + if SpringCount > 0 then + begin + //Calculate the size to add/sub to each columns. + Difference := ChangeBy / SpringCount; + //Adjust the column's size accumulators and resize if the result is >= 1. + for I := 0 to FColumns.Count - 1 do + if [coVisible, coAutoSpring] * FColumns[I].Options = [coVisible, coAutoSpring] then + begin + //Sum up rest changes from previous runs and the amount from this one and store it in the + //column. If there is at least one pixel difference then do a resize and reset the accumulator. + NewAccumulator := FColumns[I].SpringRest + Difference; + //Set new width if at least one pixel size difference is reached. + if NewAccumulator >= 1 then + TVirtualTreeColumnCracker(FColumns[I]).SetWidth(FColumns[I].Width + (Trunc(NewAccumulator) * Sign)); + FColumns[I].SpringRest := Frac(NewAccumulator); + + //Keep track of the size count. + ChangeBy := ChangeBy - Difference; + //Exit loop if resize count drops below freezing point. + if ChangeBy < 0 then + Break; + end; + end; + end; + with Tree do + FLastWidth := HeaderRect.Right - HeaderRect.Left; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +type + //--- HACK WARNING! + //This type cast is a partial rewrite of the private section of TWriter. The purpose is to have access to + //the FPropPath member, which is otherwise not accessible. The reason why this access is needed is that + //with nested components this member contains unneeded property path information. These information prevent + //successful load of the stored properties later. + //In System.Classes.pas you can see that FPropPath is reset several times to '' to prevent this case for certain properies. + //Unfortunately, there is no clean way for us here to do the same. +{$HINTS off} + TWriterHack = class(TFiler) + private + FRootAncestor : TComponent; + FPropPath : string; + end; +{$HINTS on} + + +procedure TVTHeader.WriteColumns(Writer : TWriter); + +//Write out the columns but take care for the case VT is a nested component. + +var + LastPropPath : string; + +begin + //Save last property path for restoration. + LastPropPath := TWriterHack(Writer).FPropPath; + try + //If VT is a nested component then this path contains the name of the parent component at this time + //(otherwise it is already empty). This path is then combined with the property name under which the tree + //is defined in the parent component. Unfortunately, the load code in System.Classes.pas does not consider this case + //is then unable to load this property. + TWriterHack(Writer).FPropPath := ''; + Writer.WriteCollection(Columns); + finally + TWriterHack(Writer).FPropPath := LastPropPath; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.AllowFocus(ColumnIndex : TColumnIndex) : Boolean; +begin + Result := False; + if not FColumns.IsValidColumn(ColumnIndex) then + Exit; //Just in case. + + Result := (coAllowFocus in FColumns[ColumnIndex].Options); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.Assign(Source : TPersistent); + +begin + if Source is TVTHeader then + begin + AutoSizeIndex := TVTHeader(Source).AutoSizeIndex; + Background := TVTHeader(Source).Background; + Columns := TVTHeader(Source).Columns; + Font := TVTHeader(Source).Font; + FixedAreaConstraints.Assign(TVTHeader(Source).FixedAreaConstraints); + Height := TVTHeader(Source).Height; + Images := TVTHeader(Source).Images; + MainColumn := TVTHeader(Source).MainColumn; + Options := TVTHeader(Source).Options; + ParentFont := TVTHeader(Source).ParentFont; + PopupMenu := TVTHeader(Source).PopupMenu; + SortColumn := TVTHeader(Source).SortColumn; + SortDirection := TVTHeader(Source).SortDirection; + Style := TVTHeader(Source).Style; + + RescaleHeader; + end + else + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.AutoFitColumns(Animated : Boolean = True; SmartAutoFitType : TSmartAutoFitType = smaUseColumnOption; RangeStartCol : Integer = NoColumn; + RangeEndCol : Integer = NoColumn); + +//--------------- local functions ------------------------------------------- + + function GetUseSmartColumnWidth(ColumnIndex : TColumnIndex) : Boolean; + + begin + case SmartAutoFitType of + smaAllColumns : + Result := True; + smaUseColumnOption : + Result := coSmartResize in FColumns.Items[ColumnIndex].Options; + else + Result := False; + end; + end; + +//---------------------------------------------------------------------------- + + procedure DoAutoFitColumn(Column : TColumnIndex); + + begin + with TVirtualTreeColumnsCracker(FColumns) do + if ([coResizable, coVisible] * Items[PositionToIndex[Column]].Options = [coResizable, coVisible]) and DoBeforeAutoFitColumn(PositionToIndex[Column], SmartAutoFitType) and + not Tree.OperationCanceled then + begin + if Animated then + AnimatedResize(PositionToIndex[Column], Tree.GetMaxColumnWidth(PositionToIndex[Column], GetUseSmartColumnWidth(PositionToIndex[Column]))) + else + FColumns[PositionToIndex[Column]].Width := Tree.GetMaxColumnWidth(PositionToIndex[Column], GetUseSmartColumnWidth(PositionToIndex[Column])); + + DoAfterAutoFitColumn(PositionToIndex[Column]); + end; + end; + +//--------------- end local functions ---------------------------------------- + +var + I : Integer; + StartCol, EndCol : Integer; + +begin + StartCol := Max(NoColumn + 1, RangeStartCol); + + if RangeEndCol <= NoColumn then + EndCol := FColumns.Count - 1 + else + EndCol := Min(RangeEndCol, FColumns.Count - 1); + + if StartCol > EndCol then + Exit; //nothing to do + + Tree.StartOperation(okAutoFitColumns); + FDoingAutoFitColumns := True; + try + if Assigned(Tree.OnBeforeAutoFitColumns) then + Tree.OnBeforeAutoFitColumns(Self, SmartAutoFitType); + + for I := StartCol to EndCol do + DoAutoFitColumn(I); + + if Assigned(Tree.OnAfterAutoFitColumns) then + Tree.OnAfterAutoFitColumns(Self); + + finally + Tree.EndOperation(okAutoFitColumns); + Tree.Invalidate(); + FDoingAutoFitColumns := False; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.InHeader(P : TPoint) : Boolean; + +//Determines whether the given point (client coordinates!) is within the header rectangle (non-client coordinates). + +var + R, RW : TRect; + +begin + R := Tree.HeaderRect; + + //Current position of the owner in screen coordinates. + GetWindowRect(Tree.Handle, RW); + + //Convert to client coordinates. + MapWindowPoints(0, Tree.Handle, RW, 2); + + //Consider the header within this rectangle. + OffsetRect(R, RW.Left, RW.Top); + Result := PtInRect(R, P); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.InHeaderSplitterArea(P : TPoint) : Boolean; + +//Determines whether the given point (client coordinates!) hits the horizontal splitter area of the header. + +var + R, RW : TRect; + +begin + if (P.Y > 2) or (P.Y < - 2) or not (hoVisible in FOptions) then + Result := False + else + begin + R := Tree.HeaderRect; + Inc(R.Bottom, 2); + + //Current position of the owner in screen coordinates. + GetWindowRect(Tree.Handle, RW); + + //Convert to client coordinates. + MapWindowPoints(0, Tree.Handle, RW, 2); + + //Consider the header within this rectangle. + OffsetRect(R, RW.Left, RW.Top); + Result := PtInRect(R, P); + end; +end; + +procedure TVTHeader.InternalSetAutoSizeIndex(const Index : TColumnIndex); +begin + FAutoSizeIndex := index; +end; + +procedure TVTHeader.InternalSetMainColumn(const Index : TColumnIndex); +begin + FMainColumn := index; +end; + +procedure TVTHeader.InternalSetSortColumn(const Index : TColumnIndex); +begin + FSortColumn := index; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.Invalidate(Column : TVirtualTreeColumn; ExpandToBorder : Boolean = False; UpdateNowFlag : Boolean = False); + +//Because the header is in the non-client area of the tree it needs some special handling in order to initiate its +//repainting. +//If ExpandToBorder is True then not only the given column but everything or (depending on hoFullRepaintOnResize) just +//everything to its right (or left, in RTL mode) will be invalidated (useful for resizing). This makes only sense when +//a column is given. + +var + R, RW : TRect; + Flags : Cardinal; + +begin + if (hoVisible in FOptions) and Tree.HandleAllocated then + with Tree do + begin + if Column = nil then + R := HeaderRect + else + begin + R := Column.GetRect; + if not (coFixed in Column.Options) then + OffsetRect(R, - EffectiveOffsetX, 0); + if UseRightToLeftAlignment then + OffsetRect(R, ComputeRTLOffset, 0); + if ExpandToBorder then + begin + if (hoFullRepaintOnResize in Header.Options) then + begin + R.Left := HeaderRect.Left; + R.Right := HeaderRect.Right; + end + else + begin + if UseRightToLeftAlignment then + R.Left := HeaderRect.Left + else + R.Right := HeaderRect.Right; + end; + end; + end; + R.Bottom := Tree.ClientHeight; //We want to repaint the entire column to bottom, not just the header + + //Current position of the owner in screen coordinates. + GetWindowRect(Handle, RW); + + //Consider the header within this rectangle. + OffsetRect(R, RW.Left, RW.Top); + + //Expressed in client coordinates (because RedrawWindow wants them so, they will actually become negative). + MapWindowPoints(0, Handle, R, 2); + Flags := RDW_FRAME or RDW_INVALIDATE or RDW_VALIDATE or RDW_NOINTERNALPAINT or RDW_NOERASE or RDW_NOCHILDREN; + if UpdateNowFlag then + Flags := Flags or RDW_UPDATENOW; + RedrawWindow(Handle, @R, 0, Flags); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.LoadFromStream(const Stream : TStream); + +//restore the state of the header from the given stream + +var + Dummy, Version : Integer; + S : AnsiString; + OldOptions : TVTHeaderOptions; + +begin + Include(FStates, hsLoading); + with Stream do + try + //Switch off all options which could influence loading the columns (they will be later set again). + OldOptions := FOptions; + FOptions := []; + + //Determine whether the stream contains data without a version number. + ReadBuffer(Dummy, SizeOf(Dummy)); + if Dummy > - 1 then + begin + //Seek back to undo the read operation if this is an old stream format. + Seek( - SizeOf(Dummy), soFromCurrent); + Version := - 1; + end + else //Read version number if this is a "versionized" format. + ReadBuffer(Version, SizeOf(Version)); + Columns.LoadFromStream(Stream, Version); + + ReadBuffer(Dummy, SizeOf(Dummy)); + AutoSizeIndex := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + Background := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + Height := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + FOptions := OldOptions; + Options := TVTHeaderOptions(Dummy); + //PopupMenu is neither saved nor restored + ReadBuffer(Dummy, SizeOf(Dummy)); + Style := TVTHeaderStyle(Dummy); + //TFont has no own save routine so we do it manually + with Font do + begin + ReadBuffer(Dummy, SizeOf(Dummy)); + Color := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + Height := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + SetLength(S, Dummy); + ReadBuffer(PAnsiChar(S)^, Dummy); + Name := UTF8ToString(S); + ReadBuffer(Dummy, SizeOf(Dummy)); + Pitch := TFontPitch(Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + Style := TFontStyles(Byte(Dummy)); + end; + + //Read data introduced by stream version 1+. + if Version > 0 then + begin + ReadBuffer(Dummy, SizeOf(Dummy)); + MainColumn := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + SortColumn := Dummy; + ReadBuffer(Dummy, SizeOf(Dummy)); + SortDirection := TSortDirection(Byte(Dummy)); + end; + + //Read data introduced by stream version 5+. + if Version > 4 then + begin + ReadBuffer(Dummy, SizeOf(Dummy)); + ParentFont := Boolean(Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + FMaxHeight := Integer(Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + FMinHeight := Integer(Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + FDefaultHeight := Integer(Dummy); + with FFixedAreaConstraints do + begin + ReadBuffer(Dummy, SizeOf(Dummy)); + FMaxHeightPercent := TVTConstraintPercent(Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + FMaxWidthPercent := TVTConstraintPercent(Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + FMinHeightPercent := TVTConstraintPercent(Dummy); + ReadBuffer(Dummy, SizeOf(Dummy)); + FMinWidthPercent := TVTConstraintPercent(Dummy); + end; + end; + finally + Exclude(FStates, hsLoading); + RecalculateHeader(); + Tree.DoColumnResize(NoColumn); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TVTHeader.ResizeColumns(ChangeBy : Integer; RangeStartCol : TColumnIndex; RangeEndCol : TColumnIndex; Options : TVTColumnOptions = [coVisible]) : Integer; + +//Distribute the given width change to a range of columns. A 'fair' way is used to distribute ChangeBy to the columns, +//while ensuring that everything that can be distributed will be distributed. + +var + Start, I : TColumnIndex; + ColCount, ToGo, Sign, Rest, MaxDelta, Difference : Integer; + Constraints, Widths : array of Integer; + BonusPixel : Boolean; + + //--------------- local functions ------------------------------------------- + + function IsResizable(Column : TColumnIndex) : Boolean; + + begin + if BonusPixel then + Result := Widths[Column - RangeStartCol] < Constraints[Column - RangeStartCol] + else + Result := Widths[Column - RangeStartCol] > Constraints[Column - RangeStartCol]; + end; + +//--------------------------------------------------------------------------- + + procedure IncDelta(Column : TColumnIndex); + + begin + if BonusPixel then + Inc(MaxDelta, FColumns[Column].MaxWidth - Widths[Column - RangeStartCol]) + else + Inc(MaxDelta, Widths[Column - RangeStartCol] - Constraints[Column - RangeStartCol]); + end; + +//--------------------------------------------------------------------------- + + function ChangeWidth(Column : TColumnIndex; Delta : Integer) : Integer; + + begin + if Delta > 0 then + Delta := Min(Delta, Constraints[Column - RangeStartCol] - Widths[Column - RangeStartCol]) + else + Delta := Max(Delta, Constraints[Column - RangeStartCol] - Widths[Column - RangeStartCol]); + + Inc(Widths[Column - RangeStartCol], Delta); + Dec(ToGo, Abs(Delta)); + Result := Abs(Delta); + end; + +//--------------------------------------------------------------------------- + + function ReduceConstraints : Boolean; + + var + MaxWidth, MaxReserveCol, Column : TColumnIndex; + + begin + Result := True; + if not (hsScaling in FStates) or BonusPixel then + Exit; + + MaxWidth := 0; + MaxReserveCol := NoColumn; + for Column := RangeStartCol to RangeEndCol do + if (Options * FColumns[Column].Options = Options) and (FColumns[Column].Width > MaxWidth) then + begin + MaxWidth := Widths[Column - RangeStartCol]; + MaxReserveCol := Column; + end; + + if (MaxReserveCol <= NoColumn) or (Constraints[MaxReserveCol - RangeStartCol] <= 10) then + Result := False + else + Dec(Constraints[MaxReserveCol - RangeStartCol], Constraints[MaxReserveCol - RangeStartCol] div 10); + end; + +//----------- end local functions ------------------------------------------- + +begin + Result := 0; + if ChangeBy <> 0 then + begin + //Do some initialization here + BonusPixel := ChangeBy > 0; + Sign := IfThen(BonusPixel, 1, - 1); + Start := IfThen(BonusPixel, RangeStartCol, RangeEndCol); + ToGo := Abs(ChangeBy); + SetLength(Widths, RangeEndCol - RangeStartCol + 1); + SetLength(Constraints, RangeEndCol - RangeStartCol + 1); + for I := RangeStartCol to RangeEndCol do + begin + Widths[I - RangeStartCol] := FColumns[I].Width; + Constraints[I - RangeStartCol] := IfThen(BonusPixel, FColumns[I].MaxWidth, FColumns[I].MinWidth); + end; + + repeat + repeat + MaxDelta := 0; + ColCount := 0; + for I := RangeStartCol to RangeEndCol do + if (Options * FColumns[I].Options = Options) and IsResizable(I) then + begin + Inc(ColCount); + IncDelta(I); + end; + if MaxDelta < Abs(ChangeBy) then + if not ReduceConstraints then + Break; + until (MaxDelta >= Abs(ChangeBy)) or not (hsScaling in FStates); + + if ColCount = 0 then + Break; + + ToGo := Min(ToGo, MaxDelta); + Difference := ToGo div ColCount; + Rest := ToGo mod ColCount; + + if Difference > 0 then + for I := RangeStartCol to RangeEndCol do + if (Options * FColumns[I].Options = Options) and IsResizable(I) then + ChangeWidth(I, Difference * Sign); + + //Now distribute Rest. + I := Start; + while Rest > 0 do + begin + if (Options * FColumns[I].Options = Options) and IsResizable(I) then + if FColumns[I].BonusPixel <> BonusPixel then + begin + Dec(Rest, ChangeWidth(I, Sign)); + FColumns[I].BonusPixel := BonusPixel; + end; + Inc(I, Sign); + if (BonusPixel and (I > RangeEndCol)) or (not BonusPixel and (I < RangeStartCol)) then + begin + for I := RangeStartCol to RangeEndCol do + if Options * FColumns[I].Options = Options then + FColumns[I].BonusPixel := not FColumns[I].BonusPixel; + I := Start; + end; + end; + until ToGo <= 0; + + //Now set the computed widths. We also compute the result here. + Include(FStates, hsResizing); + for I := RangeStartCol to RangeEndCol do + if (Options * FColumns[I].Options = Options) then + begin + Inc(Result, Widths[I - RangeStartCol] - FColumns[I].Width); + TVirtualTreeColumnCracker(FColumns[I]).SetWidth(Widths[I - RangeStartCol]); + end; + Exclude(FStates, hsResizing); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.RestoreColumns; + +//Restores all columns to their width which they had before they have been auto fitted. + +var + I : TColumnIndex; + +begin + with TVirtualTreeColumnsCracker(FColumns) do + for I := Count - 1 downto 0 do + if [coResizable, coVisible] * Items[PositionToIndex[I]].Options = [coResizable, coVisible] then + Items[I].RestoreLastWidth; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TVTHeader.SaveToStream(const Stream : TStream); + +//Saves the complete state of the header into the provided stream. + +var + Dummy : Integer; + Tmp : AnsiString; + +begin + with Stream do + begin + //In previous version of VT was no header stream version defined. + //For feature enhancements it is necessary, however, to know which stream + //format we are trying to load. + //In order to distict from non-version streams an indicator is inserted. + Dummy := - 1; + WriteBuffer(Dummy, SizeOf(Dummy)); + //Write current stream version number, nothing more is required at the time being. + Dummy := VTHeaderStreamVersion; + WriteBuffer(Dummy, SizeOf(Dummy)); + + //Save columns in case they depend on certain options (like auto size). + Columns.SaveToStream(Stream); + + Dummy := FAutoSizeIndex; + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := FBackgroundColor; + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := FHeight; + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Integer(FOptions); + WriteBuffer(Dummy, SizeOf(Dummy)); + //PopupMenu is neither saved nor restored + Dummy := Ord(FStyle); + WriteBuffer(Dummy, SizeOf(Dummy)); + //TFont has no own save routine so we do it manually + with Font do + begin + Dummy := Color; + WriteBuffer(Dummy, SizeOf(Dummy)); + + //Need only to write one: size or height, I decided to write height. + Dummy := Height; + WriteBuffer(Dummy, SizeOf(Dummy)); + Tmp := UTF8Encode(Name); + Dummy := Length(Tmp); + WriteBuffer(Dummy, SizeOf(Dummy)); + WriteBuffer(PAnsiChar(Tmp)^, Dummy); + Dummy := Ord(Pitch); + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Byte(Style); + WriteBuffer(Dummy, SizeOf(Dummy)); + end; + + //Data introduced by stream version 1. + Dummy := FMainColumn; + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := FSortColumn; + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Byte(FSortDirection); + WriteBuffer(Dummy, SizeOf(Dummy)); + + //Data introduced by stream version 5. + Dummy := Integer(ParentFont); + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Integer(FMaxHeight); + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Integer(FMinHeight); + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Integer(FDefaultHeight); + WriteBuffer(Dummy, SizeOf(Dummy)); + with FFixedAreaConstraints do + begin + Dummy := Integer(FMaxHeightPercent); + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Integer(FMaxWidthPercent); + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Integer(FMinHeightPercent); + WriteBuffer(Dummy, SizeOf(Dummy)); + Dummy := Integer(FMinWidthPercent); + WriteBuffer(Dummy, SizeOf(Dummy)); + end; + end; +end; + +{ TVTHeaderHelper } + +function TVTHeaderHelper.Tree : TBaseVirtualTreeCracker; +begin + Result := TBaseVirtualTreeCracker(Self.FOwner); +end; + +end. diff --git a/Source/VirtualTrees.HeaderPopup.pas b/Source/VirtualTrees.HeaderPopup.pas index 6d003bca1..1573d9fef 100644 --- a/Source/VirtualTrees.HeaderPopup.pas +++ b/Source/VirtualTrees.HeaderPopup.pas @@ -68,7 +68,8 @@ interface uses System.Classes, Vcl.Menus, - VirtualTrees; + VirtualTrees, + VirtualTrees.Types; type TVTHeaderPopupOption = ( @@ -78,7 +79,7 @@ interface ); TVTHeaderPopupOptions = set of TVTHeaderPopupOption; - TColumnChangeEvent = procedure(const Sender: TBaseVirtualTree; const Column: TColumnIndex; Visible: Boolean) of object; + TColumnChangeEvent = procedure(const Sender: TObject; const Column: TColumnIndex; Visible: Boolean) of object; TVTHeaderPopupMenu = class(TPopupMenu) strict private @@ -107,7 +108,10 @@ TVTHeaderPopupMenu = class(TPopupMenu) implementation uses - Winapi.Windows, System.Types; + Winapi.Windows, System.Types, + VirtualTrees.Constants, + VirtualTrees.Columns, + VirtualTrees.Header; resourcestring sResizeColumnToFit = 'Size &Column to Fit'; diff --git a/Source/VirtualTrees.Options.pas b/Source/VirtualTrees.Options.pas new file mode 100644 index 000000000..99ad12728 --- /dev/null +++ b/Source/VirtualTrees.Options.pas @@ -0,0 +1,620 @@ +unit VirtualTrees.Options; + +interface + +uses + System.Classes, + Vcl.Controls, + Vcl.Themes, + System.UITypes, + VirtualTrees.Types; + +type + // There is a heap of switchable behavior in the tree. Since published properties may never exceed 4 bytes, + // which limits sets to at most 32 members, and because for better overview tree options are splitted + // in various sub-options and are held in a commom options class. + // + // Options to customize tree appearance: + TVTPaintOption = ( + toHideFocusRect, // Avoid drawing the dotted rectangle around the currently focused node. + toHideSelection, // Selected nodes are drawn as unselected nodes if the tree is unfocused. + toHotTrack, // Track which node is under the mouse cursor. + toPopupMode, // Paint tree as would it always have the focus (useful for tree combo boxes etc.) + toShowBackground, // Use the background image if there's one. + toShowButtons, // Display collapse/expand buttons left to a node. + toShowDropmark, // Show the dropmark during drag'n drop operations. + toShowHorzGridLines, // Display horizontal lines to simulate a grid. + toShowRoot, // Show lines also at top level (does not show the hidden/internal root node). + toShowTreeLines, // Display tree lines to show hierarchy of nodes. + toShowVertGridLines, // Display vertical lines (depending on columns) to simulate a grid. + toThemeAware, // Draw UI elements (header, tree buttons etc.) according to the current theme if enabled (Windows XP+ only, application must be themed). + toUseBlendedImages, // Enable alpha blending for ghosted nodes or those which are being cut/copied. + toGhostedIfUnfocused, // Ghosted images are still shown as ghosted if unfocused (otherwise the become non-ghosted images). + toFullVertGridLines, // Display vertical lines over the full client area, not only the space occupied by nodes. + // This option only has an effect if toShowVertGridLines is enabled too. + toAlwaysHideSelection, // Do not draw node selection, regardless of focused state. + toUseBlendedSelection, // Enable alpha blending for node selections. + toStaticBackground, // Show simple static background instead of a tiled one. + toChildrenAbove, // Display child nodes above their parent. + toFixedIndent, // Draw the tree with a fixed indent. + toUseExplorerTheme, // Use the explorer theme if run under Windows Vista (or above). + toHideTreeLinesIfThemed, // Do not show tree lines if theming is used. + toShowFilteredNodes // Draw nodes even if they are filtered out. + ); + TVTPaintOptions = set of TVTPaintOption; + + { Options to toggle animation support: + **Do not use toAnimatedToggle when a background image is used for the tree. + The animation does not look good as the image splits and moves with it. + } + TVTAnimationOption = (toAnimatedToggle, // Expanding and collapsing a node is animated (quick window scroll). + // **See note above. + toAdvancedAnimatedToggle // Do some advanced animation effects when toggling a node. + ); + TVTAnimationOptions = set of TVTAnimationOption; + + // Options which toggle automatic handling of certain situations: + TVTAutoOption = (toAutoDropExpand, // Expand node if it is the drop target for more than a certain time. + toAutoExpand, // Nodes are expanded (collapsed) when getting (losing) the focus. + toAutoScroll, // Scroll if mouse is near the border while dragging or selecting. + toAutoScrollOnExpand, // Scroll as many child nodes in view as possible after expanding a node. + toAutoSort, // Sort tree when Header.SortColumn or Header.SortDirection change or sort node if + // child nodes are added. Sorting will take place also if SortColum is NoColumn (-1). + + toAutoSpanColumns, // Large entries continue into next column(s) if there's no text in them (no clipping). + toAutoTristateTracking, // Checkstates are automatically propagated for tri state check boxes. + toAutoHideButtons, // Node buttons are hidden when there are child nodes, but all are invisible. + toAutoDeleteMovedNodes, // Delete nodes which where moved in a drag operation (if not directed otherwise). + toDisableAutoscrollOnFocus, // Disable scrolling a node or column into view if it gets focused. + toAutoChangeScale, // Change default node height automatically if the system's font scale is set to big fonts. + toAutoFreeOnCollapse, // Frees any child node after a node has been collapsed (HasChildren flag stays there). + toDisableAutoscrollOnEdit, // Do not center a node horizontally when it is edited. + toAutoBidiColumnOrdering // When set then columns (if any exist) will be reordered from lowest index to highest index + // and vice versa when the tree's bidi mode is changed. + ); + TVTAutoOptions = set of TVTAutoOption; + + // Options which determine the tree's behavior when selecting nodes: + TVTSelectionOption = (toDisableDrawSelection, // Prevent user from selecting with the selection rectangle in multiselect mode. + toExtendedFocus, // Entries other than in the main column can be selected, edited etc. + toFullRowSelect, // Hit test as well as selection highlight are not constrained to the text of a node. + toLevelSelectConstraint, // Constrain selection to the same level as the selection anchor. + toMiddleClickSelect, // Allow selection, dragging etc. with the middle mouse button. This and toWheelPanning + // are mutual exclusive. + toMultiSelect, // Allow more than one node to be selected. + toRightClickSelect, // Allow selection, dragging etc. with the right mouse button. + toSiblingSelectConstraint, // Constrain selection to nodes with same parent. + toCenterScrollIntoView, // Center nodes vertically in the client area when scrolling into view. + toSimpleDrawSelection, // Simplifies draw selection, so a node's caption does not need to intersect with the + // selection rectangle. + toAlwaysSelectNode, // If this flag is set to true, the tree view tries to always have a node selected. + // This behavior is closer to the Windows TreeView and useful in Windows Explorer style applications. + toRestoreSelection, // Set to true if upon refill the previously selected nodes should be selected again. + // The nodes will be identified by its caption (text in MainColumn) + // You may use TVTHeader.RestoreSelectiuonColumnIndex to define an other column that should be used for indentification. + toSyncCheckboxesWithSelection // If checkboxes are shown, they follow the change in selections. When checkboxes are + // changed, the selections follow them and vice-versa. + // **Only supported for ctCheckBox type checkboxes. + ); + TVTSelectionOptions = set of TVTSelectionOption; + + TVTEditOptions = (toDefaultEdit, // Standard behaviour for end of editing (after VK_RETURN stay on edited cell). + toVerticalEdit, // After VK_RETURN switch to next column. + toHorizontalEdit // After VK_RETURN switch to next row. + ); + + // Options which do not fit into any of the other groups: + TVTMiscOption = (toAcceptOLEDrop, // Register tree as OLE accepting drop target + toCheckSupport, // Show checkboxes/radio buttons. + toEditable, // Node captions can be edited. + toFullRepaintOnResize, // Fully invalidate the tree when its window is resized (CS_HREDRAW/CS_VREDRAW). + toGridExtensions, // Use some special enhancements to simulate and support grid behavior. + toInitOnSave, // Initialize nodes when saving a tree to a stream. + toReportMode, // Tree behaves like TListView in report mode. + toToggleOnDblClick, // Toggle node expansion state when it is double clicked. + toWheelPanning, // Support for mouse panning (wheel mice only). This option and toMiddleClickSelect are + // mutal exclusive, where panning has precedence. + toReadOnly, // The tree does not allow to be modified in any way. No action is executed and + // node editing is not possible. + toVariableNodeHeight, // When set then GetNodeHeight will trigger OnMeasureItem to allow variable node heights. + toFullRowDrag, // Start node dragging by clicking anywhere in it instead only on the caption or image. + // Must be used together with toDisableDrawSelection. + toNodeHeightResize, // Allows changing a node's height via mouse. + toNodeHeightDblClickResize, // Allows to reset a node's height to FDefaultNodeHeight via a double click. + toEditOnClick, // Editing mode can be entered with a single click + toEditOnDblClick, // Editing mode can be entered with a double click + toReverseFullExpandHotKey // Used to define Ctrl+'+' instead of Ctrl+Shift+'+' for full expand (and similar for collapsing) + ); + TVTMiscOptions = set of TVTMiscOption; + + // Options to control data export + TVTExportMode = (emAll, // export all records (regardless checked state) + emChecked, // export checked records only + emUnchecked, // export unchecked records only + emVisibleDueToExpansion, // Do not export nodes that are not visible because their parent is not expanded + emSelected // export selected nodes only + ); + + // Options regarding strings (useful only for the string tree and descendants): + TVTStringOption = (toSaveCaptions, // If set then the caption is automatically saved with the tree node, regardless of what is + // saved in the user data. + toShowStaticText, // Show static text in a caption which can be differently formatted than the caption + // but cannot be edited. + toAutoAcceptEditChange // Automatically accept changes during edit if the user finishes editing other then + // VK_RETURN or ESC. If not set then changes are cancelled. + ); + TVTStringOptions = set of TVTStringOption; + +const + DefaultPaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toShowRoot, toThemeAware, toUseBlendedImages]; + DefaultAnimationOptions = []; + DefaultAutoOptions = [toAutoDropExpand, toAutoTristateTracking, toAutoScrollOnExpand, toAutoDeleteMovedNodes, toAutoChangeScale, toAutoSort]; + DefaultSelectionOptions = []; + DefaultMiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, toEditOnClick]; + + DefaultStringOptions = [toSaveCaptions, toAutoAcceptEditChange]; + +type + TCustomVirtualTreeOptions = class(TPersistent) + private + FOwner : TCustomControl; + FPaintOptions : TVTPaintOptions; + FAnimationOptions : TVTAnimationOptions; + FAutoOptions : TVTAutoOptions; + FSelectionOptions : TVTSelectionOptions; + FMiscOptions : TVTMiscOptions; + FExportMode : TVTExportMode; + FEditOptions : TVTEditOptions; + procedure SetAnimationOptions(const Value : TVTAnimationOptions); + procedure SetAutoOptions(const Value : TVTAutoOptions); + procedure SetMiscOptions(const Value : TVTMiscOptions); + procedure SetPaintOptions(const Value : TVTPaintOptions); + procedure SetSelectionOptions(const Value : TVTSelectionOptions); + protected + // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) + function StyleServices(AControl : TControl = nil) : TCustomStyleServices; + public + constructor Create(AOwner : TCustomControl); virtual; + //these bypass the side effects in the regular setters. + procedure InternalSetMiscOptions(const Value : TVTMiscOptions); + + procedure AssignTo(Dest : TPersistent); override; + property AnimationOptions : TVTAnimationOptions read FAnimationOptions write SetAnimationOptions default DefaultAnimationOptions; + property AutoOptions : TVTAutoOptions read FAutoOptions write SetAutoOptions default DefaultAutoOptions; + property ExportMode : TVTExportMode read FExportMode write FExportMode default emAll; + property MiscOptions : TVTMiscOptions read FMiscOptions write SetMiscOptions default DefaultMiscOptions; + property PaintOptions : TVTPaintOptions read FPaintOptions write SetPaintOptions default DefaultPaintOptions; + property SelectionOptions : TVTSelectionOptions read FSelectionOptions write SetSelectionOptions default DefaultSelectionOptions; + property EditOptions : TVTEditOptions read FEditOptions write FEditOptions default toDefaultEdit; + + //property Owner: TBaseVirtualTree read FOwner; + end; + + TTreeOptionsClass = class of TCustomVirtualTreeOptions; + + TVirtualTreeOptions = class(TCustomVirtualTreeOptions) + published + property AnimationOptions; + property AutoOptions; + property ExportMode; + property MiscOptions; + property PaintOptions; + property SelectionOptions; + end; + + TCustomStringTreeOptions = class(TCustomVirtualTreeOptions) + private + FStringOptions : TVTStringOptions; + procedure SetStringOptions(const Value : TVTStringOptions); + protected + public + constructor Create(AOwner : TCustomControl); override; + procedure AssignTo(Dest : TPersistent); override; + property StringOptions : TVTStringOptions read FStringOptions write SetStringOptions default DefaultStringOptions; + end; + + TStringTreeOptions = class(TCustomStringTreeOptions) + published + property AnimationOptions; + property AutoOptions; + property ExportMode; + property MiscOptions; + property PaintOptions; + property SelectionOptions; + property StringOptions; + property EditOptions; + end; + + TScrollBarStyle = (sbmRegular, sbm3D); + + // A class to manage scroll bar aspects. + TScrollBarOptions = class(TPersistent) + private + FAlwaysVisible : Boolean; + FOwner : TCustomControl; + FScrollBars : TScrollStyle; // used to hide or show vertical and/or horizontal scrollbar + FScrollBarStyle : TScrollBarStyle; // kind of scrollbars to use + FIncrementX, FIncrementY : TVTScrollIncrement; // number of pixels to scroll in one step (when auto scrolling) + procedure SetAlwaysVisible(Value : Boolean); + procedure SetScrollBars(Value : TScrollStyle); + procedure SetScrollBarStyle(Value : TScrollBarStyle); + protected + function GetOwner : TPersistent; override; + public + constructor Create(AOwner : TCustomControl); + + procedure Assign(Source : TPersistent); override; + published + property AlwaysVisible : Boolean read FAlwaysVisible write SetAlwaysVisible default False; + property HorizontalIncrement : TVTScrollIncrement read FIncrementX write FIncrementX default 20; + property ScrollBars : TScrollStyle read FScrollBars write SetScrollBars default TScrollStyle.ssBoth; + property ScrollBarStyle : TScrollBarStyle read FScrollBarStyle write SetScrollBarStyle default sbmRegular; + property VerticalIncrement : TVTScrollIncrement read FIncrementY write FIncrementY default 20; + end; + +implementation + +uses + VirtualTrees, + VirtualTrees.StyleHooks, + WinApi.Windows, + WinApi.ActiveX; + +type + TVTCracker = class(TBaseVirtualTree); + + //----------------- TCustomVirtualTreeOptions -------------------------------------------------------------------------- + +constructor TCustomVirtualTreeOptions.Create(AOwner : TCustomControl); +begin + FOwner := AOwner; + + FPaintOptions := DefaultPaintOptions; + FAnimationOptions := DefaultAnimationOptions; + FAutoOptions := DefaultAutoOptions; + FSelectionOptions := DefaultSelectionOptions; + FMiscOptions := DefaultMiscOptions; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TCustomVirtualTreeOptions.SetAnimationOptions(const Value : TVTAnimationOptions); +begin + FAnimationOptions := Value; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TCustomVirtualTreeOptions.SetAutoOptions(const Value : TVTAutoOptions); +var + ChangedOptions : TVTAutoOptions; +begin + if FAutoOptions <> Value then + begin + // Exclusive ORing to get all entries wich are in either set but not in both. + ChangedOptions := FAutoOptions + Value - (FAutoOptions * Value); + FAutoOptions := Value; + with FOwner do + if (toAutoSpanColumns in ChangedOptions) and not (csLoading in ComponentState) and HandleAllocated then + Invalidate; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TCustomVirtualTreeOptions.InternalSetMiscOptions(const Value : TVTMiscOptions); +begin + FMiscOptions := Value; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TCustomVirtualTreeOptions.SetMiscOptions(const Value : TVTMiscOptions); +var + ToBeSet, ToBeCleared : TVTMiscOptions; +begin + if FMiscOptions <> Value then + begin + ToBeSet := Value - FMiscOptions; + ToBeCleared := FMiscOptions - Value; + FMiscOptions := Value; + + with TVTCracker(FOwner) do + if not (csLoading in ComponentState) and HandleAllocated then + begin + if toCheckSupport in ToBeSet + ToBeCleared then + Invalidate; + if toEditOnDblClick in ToBeSet then + FMiscOptions := FMiscOptions - [toToggleOnDblClick]; + // In order for toEditOnDblClick to take effect, we need to remove toToggleOnDblClick which is handled with priority. See issue #747 + + if not (csDesigning in ComponentState) then + begin + if toAcceptOLEDrop in ToBeCleared then + RevokeDragDrop(Handle); + if toFullRepaintOnResize in ToBeSet + ToBeCleared then + RecreateWnd; + if toAcceptOLEDrop in ToBeSet then + RegisterDragDrop(Handle, DragManager as IDropTarget); + if toVariableNodeHeight in ToBeSet then + begin + BeginUpdate(); + try + ReInitNode(nil, True); + finally + EndUpdate(); + end; //try..finally + end; //if toVariableNodeHeight + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TCustomVirtualTreeOptions.SetPaintOptions(const Value : TVTPaintOptions); +var + ToBeSet, ToBeCleared : TVTPaintOptions; + Run : PVirtualNode; + HandleWasAllocated : Boolean; +begin + if FPaintOptions <> Value then + begin + ToBeSet := Value - FPaintOptions; + ToBeCleared := FPaintOptions - Value; + FPaintOptions := Value; + if (toFixedIndent in ToBeSet) then + begin + // Fixes issue #388 + Include(FPaintOptions, toShowRoot); + Include(ToBeSet, toShowRoot); + end; //if + with TVTCracker(FOwner) do + begin + HandleWasAllocated := HandleAllocated; + + if not (csLoading in ComponentState) and (toShowFilteredNodes in ToBeSet + ToBeCleared) then + begin + if HandleWasAllocated then + BeginUpdate; + InterruptValidation; + Run := GetFirstNoInit; + while Assigned(Run) do + begin + if (vsFiltered in Run.States) then + begin + if FullyVisible[Run] then + begin + if toShowFilteredNodes in ToBeSet then + IncVisibleCount + else + DecVisibleCount; + end; + if toShowFilteredNodes in ToBeSet then + AdjustTotalHeight(Run, Run.NodeHeight, True) + else + AdjustTotalHeight(Run, - Run.NodeHeight, True); + end; + Run := GetNextNoInit(Run); + end; + if HandleWasAllocated then + EndUpdate; + end; + + if HandleAllocated then + begin + if IsWinVistaOrAbove and ((tsUseThemes in TreeStates) or ((toThemeAware in ToBeSet) and StyleServices.Enabled)) and (toUseExplorerTheme in (ToBeSet + ToBeCleared)) and + not VclStyleEnabled then + begin + if (toUseExplorerTheme in ToBeSet) then + begin + SetWindowTheme('explorer'); + DoStateChange([tsUseExplorerTheme]); + end + else if toUseExplorerTheme in ToBeCleared then + begin + SetWindowTheme(''); + DoStateChange([], [tsUseExplorerTheme]); + end; + end; + + if not (csLoading in ComponentState) then + begin + if ((toThemeAware in ToBeSet + ToBeCleared) or (toUseExplorerTheme in ToBeSet + ToBeCleared) or VclStyleEnabled) then + begin + if ((toThemeAware in ToBeSet) and StyleServices.Enabled) then + DoStateChange([tsUseThemes]) + else if (toThemeAware in ToBeCleared) then + DoStateChange([], [tsUseThemes]); + + PrepareBitmaps(True, False); + RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_VALIDATE or RDW_FRAME); + end; + + if toChildrenAbove in ToBeSet + ToBeCleared then + begin + InvalidateCache; + if UpdateCount = 0 then + begin + ValidateCache; + Invalidate; + end; + end; + + Invalidate; + end; + end; + end; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TCustomVirtualTreeOptions.SetSelectionOptions(const Value : TVTSelectionOptions); +var + ToBeSet, ToBeCleared : TVTSelectionOptions; +begin + if FSelectionOptions <> Value then + begin + ToBeSet := Value - FSelectionOptions; + ToBeCleared := FSelectionOptions - Value; + FSelectionOptions := Value; + + with TVTCracker(FOwner) do + begin + if (toMultiSelect in (ToBeCleared + ToBeSet)) or ([toLevelSelectConstraint, toSiblingSelectConstraint] * ToBeSet <> []) then + ClearSelection; + + if (toExtendedFocus in ToBeCleared) and (FocusedColumn > 0) and HandleAllocated then + begin + FocusedColumn := Header.MainColumn; + Invalidate; + end; + + if not (toExtendedFocus in FSelectionOptions) then + FocusedColumn := Header.MainColumn; + end; + end; +end; + +function TCustomVirtualTreeOptions.StyleServices(AControl : TControl) : TCustomStyleServices; +begin + Result := VTStyleServices(FOwner); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TCustomVirtualTreeOptions.AssignTo(Dest : TPersistent); +begin + if Dest is TCustomVirtualTreeOptions then + begin + with Dest as TCustomVirtualTreeOptions do + begin + PaintOptions := Self.PaintOptions; + AnimationOptions := Self.AnimationOptions; + AutoOptions := Self.AutoOptions; + SelectionOptions := Self.SelectionOptions; + MiscOptions := Self.MiscOptions; + end; + end + else + inherited; +end; + +//----------------- TCustomStringTreeOptions --------------------------------------------------------------------------- + +constructor TCustomStringTreeOptions.Create(AOwner : TCustomControl); +begin + inherited; + FStringOptions := DefaultStringOptions; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TCustomStringTreeOptions.SetStringOptions(const Value : TVTStringOptions); +var + ChangedOptions : TVTStringOptions; +begin + if FStringOptions <> Value then + begin + // Exclusive ORing to get all entries wich are in either set but not in both. + ChangedOptions := FStringOptions + Value - (FStringOptions * Value); + FStringOptions := Value; + with FOwner do + if (toShowStaticText in ChangedOptions) and not (csLoading in ComponentState) and HandleAllocated then + Invalidate; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TCustomStringTreeOptions.AssignTo(Dest : TPersistent); +begin + if Dest is TCustomStringTreeOptions then + begin + with Dest as TCustomStringTreeOptions do + begin + StringOptions := Self.StringOptions; + EditOptions := Self.EditOptions; + end; + end; + + // Let ancestors assign their options to the destination class. + inherited; +end; + +//----------------- TScrollBarOptions ---------------------------------------------------------------------------------- + +constructor TScrollBarOptions.Create(AOwner : TCustomControl); +begin + inherited Create; + + FOwner := AOwner; + FAlwaysVisible := False; + FScrollBarStyle := sbmRegular; + FScrollBars := TScrollStyle.ssBoth; + FIncrementX := 20; + FIncrementY := 20; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TScrollBarOptions.SetAlwaysVisible(Value : Boolean); +begin + if FAlwaysVisible <> Value then + begin + FAlwaysVisible := Value; + if not (csLoading in FOwner.ComponentState) and FOwner.HandleAllocated then + TVTCracker(FOwner).RecreateWnd; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TScrollBarOptions.SetScrollBars(Value : TScrollStyle); +begin + if FScrollBars <> Value then + begin + FScrollBars := Value; + if not (csLoading in FOwner.ComponentState) and FOwner.HandleAllocated then + TVTCracker(FOwner).RecreateWnd; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TScrollBarOptions.SetScrollBarStyle(Value : TScrollBarStyle); + +begin + if FScrollBarStyle <> Value then + begin + FScrollBarStyle := Value; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TScrollBarOptions.GetOwner : TPersistent; + +begin + Result := FOwner; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TScrollBarOptions.Assign(Source : TPersistent); + +begin + if Source is TScrollBarOptions then + begin + AlwaysVisible := TScrollBarOptions(Source).AlwaysVisible; + HorizontalIncrement := TScrollBarOptions(Source).HorizontalIncrement; + ScrollBars := TScrollBarOptions(Source).ScrollBars; + ScrollBarStyle := TScrollBarOptions(Source).ScrollBarStyle; + VerticalIncrement := TScrollBarOptions(Source).VerticalIncrement; + end + else + inherited; +end; + +end. diff --git a/Source/VirtualTrees.StyleHooks.pas b/Source/VirtualTrees.StyleHooks.pas index e62b1d3ce..b7f37a25d 100644 --- a/Source/VirtualTrees.StyleHooks.pas +++ b/Source/VirtualTrees.StyleHooks.pas @@ -118,13 +118,30 @@ TScrollWindow = class(TWinControl) VTStyleServicesFunc: TVTStyleServicesFunc = nil; +/// Wrapper function for styles services that handles differences between RAD Studio 10.4 and older versions, +/// as well as the case if these controls are used inside the IDE. +function VTStyleServices(AControl: TControl = nil): TCustomStyleServices; + + implementation uses System.SysUtils, System.Math, System.Types, - VirtualTrees; + VirtualTrees, + VirtualTrees.Header; + +function VTStyleServices(AControl: TControl = nil): TCustomStyleServices; +begin + if Assigned(VTStyleServicesFunc) then + Result := VTStyleServicesFunc(AControl) + else + Result := Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(AControl){$ifend}; +end; + +//---------------------------------------------------------------------------------------------------------------------- + type TBaseVirtualTreeCracker = class(TBaseVirtualTree) diff --git a/Source/VirtualTrees.Types.pas b/Source/VirtualTrees.Types.pas new file mode 100644 index 000000000..5adde39e6 --- /dev/null +++ b/Source/VirtualTrees.Types.pas @@ -0,0 +1,45 @@ +unit VirtualTrees.Types; + +interface + +uses + WinApi.ActiveX, + System.Types, + System.SysUtils; + +type +{$IFDEF VT_FMX} + TDimension = Single; +{$ELSE} + TDimension = Integer; // For Firemonkey support, see #841 +{$ENDIF} + TColumnIndex = type Integer; + TColumnPosition = type Cardinal; + PCardinal = ^Cardinal; + + // The exception used by the trees. + EVirtualTreeError = class(Exception); + + // Limits the speed interval which can be used for auto scrolling (milliseconds). + TAutoScrollInterval = 1 .. 1000; + + TVTScrollIncrement = 1 .. 10000; + + // OLE drag'n drop support + TFormatEtcArray = array of TFormatEtc; + TFormatArray = array of Word; + + // protection against TRect record method that cause problems with with-statements + TWithSafeRect = record + case Integer of + 0 : + (Left, Top, Right, Bottom : Integer); + 1 : + (TopLeft, BottomRight : TPoint); + end; + + TAddPopupItemType = (apNormal, apDisabled, apHidden); + +implementation + +end. diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 8fe360854..e93f8a07b 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -30,7 +30,7 @@ // Anthony Mills, Alexander Egorushkin (BCB), Mathias Torell (BCB), Frank van den Bergh, Vadim Sedulin, Peter Evans, // Milan Vandrovec (BCB), Steve Moss, Joe White, David Clark, Anders Thomsen, Igor Afanasyev, Eugene Programmer, // Corbin Dunn, Richard Pringle, Uli Gerhardt, Azza, Igor Savkic, Daniel Bauten, Timo Tegtmeier, Dmitry Zegebart, -// Andreas Hausladen, Joachim Marder, Roman Kassebaum, Vincent Parret, Dietmar Roesler, Sanjay Kanade, +// Andreas Hausladen, Joachim Marder, Roman Kassebaum, Vincent Parrett, Dietmar Roesler, Sanjay Kanade, // and everyone that sent pull requests: https://github.com/Virtual-TreeView/Virtual-TreeView/pulls?q= // Beta testers: // Freddy Ertl, Hans-Juergen Schnorrenberg, Werner Lehmann, Jim Kueneman, Vadim Sedulin, Moritz Franckenstein, @@ -78,141 +78,88 @@ interface Winapi.Windows, Winapi.oleacc, Winapi.Messages, System.SysUtils, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.ImgList, Winapi.ActiveX, Vcl.StdCtrls, System.Classes, Vcl.Menus, Vcl.Printers, System.Types, Winapi.CommCtrl, Vcl.Themes, Winapi.UxTheme, - Winapi.ShlObj, System.UITypes, System.Generics.Collections; -type -{$IFDEF VT_FMX} - TDimension = Single; -{$ELSE} - TDimension = Integer; // For Firemonkey support, see #841 -{$ENDIF} - -const - VTVersion = '7.5.0' deprecated 'This const is going to be removed in a future version'; - -const - VTTreeStreamVersion = 3; - VTHeaderStreamVersion = 6; // The header needs an own stream version to indicate changes only relevant to the header. - - CacheThreshold = 2000; // Number of nodes a tree must at least have to start caching and at the same - // time the maximum number of nodes between two cache entries. - FadeAnimationStepCount = 255; // Number of animation steps for hint fading (0..255). - ShadowSize = 5; // Size in pixels of the hint shadow. This value has no influence on Win2K and XP systems - // as those OSes have native shadow support. - - // Special identifiers for columns. - NoColumn = -1; - InvalidColumn = -2; - - // Indices for check state images used for checking. - ckEmpty = 0; // an empty image used as place holder - // radio buttons - ckRadioUncheckedNormal = 1; - ckRadioUncheckedHot = 2; - ckRadioUncheckedPressed = 3; - ckRadioUncheckedDisabled = 4; - ckRadioCheckedNormal = 5; - ckRadioCheckedHot = 6; - ckRadioCheckedPressed = 7; - ckRadioCheckedDisabled = 8; - // check boxes - ckCheckUncheckedNormal = 9; - ckCheckUncheckedHot = 10; - ckCheckUncheckedPressed = 11; - ckCheckUncheckedDisabled = 12; - ckCheckCheckedNormal = 13; - ckCheckCheckedHot = 14; - ckCheckCheckedPressed = 15; - ckCheckCheckedDisabled = 16; - ckCheckMixedNormal = 17; - ckCheckMixedHot = 18; - ckCheckMixedPressed = 19; - ckCheckMixedDisabled = 20; - // simple button - ckButtonNormal = 21; - ckButtonHot = 22; - ckButtonPressed = 23; - ckButtonDisabled = 24; - - // Instead using a TTimer class for each of the various events I use Windows timers with messages - // as this is more economical. - ExpandTimer = 1; - EditTimer = 2; - HeaderTimer = 3; - ScrollTimer = 4; - ChangeTimer = 5; - StructureChangeTimer = 6; - SearchTimer = 7; - ThemeChangedTimer = 8; - - ThemeChangedTimerDelay = 500; - - // Virtual Treeview does not need to be subclassed by an eventual Theme Manager instance as it handles - // Windows XP theme painting itself. Hence the special message is used to prevent subclassing. - CM_DENYSUBCLASSING = CM_BASE + 2000; - - // Decoupling message for auto-adjusting the internal edit window. - CM_AUTOADJUST = CM_BASE + 2005; - - // VT's own clipboard formats, - // Note: The reference format is used internally to allow to link to a tree reference - // to implement optimized moves and other back references. - CFSTR_VIRTUALTREE = 'Virtual Tree Data'; - CFSTR_VTREFERENCE = 'Virtual Tree Reference'; - CFSTR_HTML = 'HTML Format'; - CFSTR_RTF = 'Rich Text Format'; - CFSTR_RTFNOOBJS = 'Rich Text Format Without Objects'; - CFSTR_CSV = 'CSV'; - - // Drag image helpers for Windows 2000 and up. - IID_IDropTargetHelper: TGUID = (D1: $4657278B; D2: $411B; D3: $11D2; D4: ($83, $9A, $00, $C0, $4F, $D9, $18, $D0)); - IID_IDragSourceHelper: TGUID = (D1: $DE5BF786; D2: $477A; D3: $11D2; D4: ($83, $9D, $00, $C0, $4F, $D9, $18, $D0)); - IID_IDropTarget: TGUID = (D1: $00000122; D2: $0000; D3: $0000; D4: ($C0, $00, $00, $00, $00, $00, $00, $46)); - - // Help identifiers for exceptions. Application developers are responsible to link them with actual help topics. - hcTFEditLinkIsNil = 2000; - hcTFWrongMoveError = 2001; - hcTFWrongStreamFormat = 2002; - hcTFWrongStreamVersion = 2003; - hcTFStreamTooSmall = 2004; - hcTFCorruptStream1 = 2005; - hcTFCorruptStream2 = 2006; - hcTFClipboardFailed = 2007; - hcTFCannotSetUserData = 2008; - - // Header standard split cursor. - crHeaderSplit = TCursor(63); - - // Height changing cursor. - crVertSplit = TCursor(62); - -var // Clipboard format IDs used in OLE drag'n drop and clipboard transfers. - CF_VIRTUALTREE, - CF_VTREFERENCE, - CF_VRTF, - CF_VRTFNOOBJS, // Unfortunately CF_RTF* is already defined as being - // registration strings so I have to use different identifiers. - CF_HTML, - CF_CSV: Word; + Winapi.ShlObj, System.UITypes, System.Generics.Collections, + VirtualTrees.Types, + VirtualTrees.Constants, + VirtualTrees.Colors, + VirtualTrees.Options, + VirtualTrees.DragImage, + VirtualTrees.Columns, + VirtualTrees.Header; +var IsWinVistaOrAbove: Boolean; {$MinEnumSize 1, make enumerations as small as possible} - - type // Alias defintions for convenience TImageIndex = System.UITypes.TImageIndex; TCanvas = Vcl.Graphics.TCanvas; - - // The exception used by the trees. - EVirtualTreeError = class(Exception); - - PCardinal = ^Cardinal; - - // Limits the speed interval which can be used for auto scrolling (milliseconds). - TAutoScrollInterval = 1..1000; + //these were moved, aliases are for backwards compatibility. + //some may be removed once we sort out excactly what is needed. + TDimension = VirtualTrees.Types.TDimension; + TColumnIndex = VirtualTrees.Types.TColumnIndex; + TColumnPosition = VirtualTrees.Types.TColumnPosition; + EVirtualTreeError = VirtualTrees.Types.EVirtualTreeError; + TAutoScrollInterval = VirtualTrees.Types.TAutoScrollInterval; + TVTScrollIncrement = VirtualTrees.Types.TVTScrollIncrement; + TFormatArray = VirtualTrees.Types.TFormatArray; + TFormatEtcArray = VirtualTrees.Types.TFormatEtcArray; + + TVTPaintOption = VirtualTrees.Options.TVTPaintOption; + TVTPaintOptions = VirtualTrees.Options.TVTPaintOptions; + TVTAnimateOption = VirtualTrees.Options.TVTAnimationOption; + TVTAnimateOptions = VirtualTrees.Options.TVTAnimationOptions; + TVTAutoOption = VirtualTrees.Options.TVTAutoOption; + TVTAutoOptions = VirtualTrees.Options.TVTAutoOptions; + TVTSelectionOption = VirtualTrees.Options.TVTSelectionOption; + TVTSelectionOptions = VirtualTrees.Options.TVTSelectionOptions; + TVTEditOptions = VirtualTrees.Options.TVTEditOptions; + TVTMiscOption = VirtualTrees.Options.TVTMiscOption; + TVTMiscOptions = VirtualTrees.Options.TVTMiscOptions; + TVTExportMode = VirtualTrees.Options.TVTExportMode; + TVTStringOption = VirtualTrees.Options.TVTStringOption; + TVTStringOptions = VirtualTrees.Options.TVTStringOptions; + TCustomVirtualTreeOptions = VirtualTrees.Options.TCustomVirtualTreeOptions; + TVirtualTreeOptions = VirtualTrees.Options.TVirtualTreeOptions; + TTreeOptionsClass = VirtualTrees.Options.TTreeOptionsClass; + TCustomStringTreeOptions = VirtualTrees.Options.TCustomStringTreeOptions; + TStringTreeOptions = VirtualTrees.Options.TStringTreeOptions; + + TScrollBarStyle = VirtualTrees.Options.TScrollBarStyle; + TScrollBarOptions = VirtualTrees.Options.TScrollBarOptions; + + TVTColumnOption = VirtualTrees.Columns.TVTColumnOption; + TVTColumnOptions = VirtualTrees.Columns.TVTColumnOptions; + TVirtualTreeColumnStyle = VirtualTrees.Columns.TVirtualTreeColumnStyle; + TSortDirection = VirtualTrees.Columns.TSortDirection; + TCheckType = VirtualTrees.Columns.TCheckType; + TCheckState = VirtualTrees.Columns.TCheckState; + TVTDropMarkMode = VirtualTrees.Columns.TVTDropMarkMode; + TScrollDirections = VirtualTrees.Columns.TScrollDirections; + TVirtualTreeColumn = VirtualTrees.Columns.TVirtualTreeColumn; + TVirtualTreeColumns = VirtualTrees.Columns.TVirtualTreeColumns; + TVirtualTreeColumnClass = VirtualTrees.Columns.TVirtualTreeColumnClass; + TColumnsArray = VirtualTrees.Columns.TColumnsArray; + TCardinalArray = VirtualTrees.Columns.TCardinalArray; + TIndexArray = VirtualTrees.Columns.TIndexArray; + + + TVTHeader = VirtualTrees.Header.TVTHeader; + TVTHeaderClass = VirtualTrees.Header.TVTHeaderClass; + TVTHeaderOption = VirtualTrees.Header.TVTHeaderOption; + TVTHeaderOptions = VirtualTrees.Header.TVTHeaderOptions; + TVTConstraintPercent = VirtualTrees.Header.TVTConstraintPercent; + TSmartAutoFitType = VirtualTrees.Header.TSmartAutoFitType; + TVTFixedAreaConstraints = VirtualTrees.Header.TVTFixedAreaConstraints; + TVTHeaderStyle = VirtualTrees.Header.TVTHeaderStyle; + THeaderState = VirtualTrees.Header.THeaderState; + THeaderStates = VirtualTrees.Header.THeaderStates; + + TVTColors = VirtualTrees.Colors.TVTColors; + // // Be careful when adding new states as this might change the size of the type which in turn // changes the alignment in the node record as well as the stream chunks. @@ -251,33 +198,7 @@ EVirtualTreeError = class(Exception); ); TVirtualNodeInitStates = set of TVirtualNodeInitState; - TScrollBarStyle = ( - sbmRegular, - sbm3D - ); - // Options per column. - TVTColumnOption = ( - coAllowClick, // Column can be clicked (must be enabled too). - coDraggable, // Column can be dragged. - coEnabled, // Column is enabled. - coParentBidiMode, // Column uses the parent's bidi mode. - coParentColor, // Column uses the parent's background color. - coResizable, // Column can be resized. - coShowDropMark, // Column shows the drop mark if it is currently the drop target. - coVisible, // Column is shown. - coAutoSpring, // Column takes part in the auto spring feature of the header (must be resizable too). - coFixed, // Column is fixed and can not be selected or scrolled etc. - coSmartResize, // Column is resized to its largest entry which is in view (instead of its largest - // visible entry). - coAllowFocus, // Column can be focused. - coDisableAnimatedResize, // Column resizing is not animated. - coWrapCaption, // Caption could be wrapped across several header lines to fit columns width. - coUseCaptionAlignment, // Column's caption has its own aligment. - coEditable, // Column can be edited - coStyleColor // Prefer background color of VCL style over TVirtualTreeColumn.Color - ); - TVTColumnOptions = set of TVTColumnOption; // These flags are used to indicate where a click in the header happened. TVTHeaderHitPosition = ( @@ -310,51 +231,6 @@ EVirtualTreeError = class(Exception); ); THitPositions = set of THitPosition; - TCheckType = ( - ctNone, - ctTriStateCheckBox, - ctCheckBox, - ctRadioButton, - ctButton - ); - - // The check states include both, transient and fluent (temporary) states. The only temporary state defined so - // far is the pressed state. - TCheckState = ( - csUncheckedNormal, // unchecked and not pressed - csUncheckedPressed, // unchecked and pressed - csCheckedNormal, // checked and not pressed - csCheckedPressed, // checked and pressed - csMixedNormal, // 3-state check box and not pressed - csMixedPressed, // 3-state check box and pressed - csUncheckedDisabled,// disabled checkbox, not checkable - csCheckedDisabled, // disabled checkbox, not uncheckable - csMixedDisabled // disabled 3-state checkbox - ); - - /// Adds some convenience methods to type TCheckState - TCheckStateHelper = record helper for TCheckState - strict private - const - // Lookup to quickly convert a specific check state into its pressed counterpart and vice versa. - cPressedState: array[TCheckState] of TCheckState = ( - csUncheckedPressed, csUncheckedPressed, csCheckedPressed, csCheckedPressed, csMixedPressed, csMixedPressed, csUncheckedDisabled, csCheckedDisabled, csMixedDisabled); - cUnpressedState: array[TCheckState] of TCheckState = ( - csUncheckedNormal, csUncheckedNormal, csCheckedNormal, csCheckedNormal, csMixedNormal, csMixedNormal, csUncheckedDisabled, csCheckedDisabled, csMixedDisabled); - cEnabledState: array[TCheckState] of TCheckState = ( - csUncheckedNormal, csUncheckedPressed, csCheckedNormal, csCheckedPressed, csMixedNormal, csMixedPressed, csUncheckedNormal, csCheckedNormal, csMixedNormal); - cToggledState: array[TCheckState] of TCheckState = ( - csCheckedNormal, csCheckedPressed, csUnCheckedNormal, csUnCheckedPressed, csCheckedNormal, csCheckedPressed, csUncheckedDisabled, csCheckedDisabled, csMixedDisabled); - public - function GetPressed(): TCheckState; inline; - function GetUnpressed(): TCheckState; inline; - function GetEnabled(): TCheckState; inline; - function GetToggled(): TCheckState; inline; - function IsDisabled(): Boolean; inline; - function IsChecked(): Boolean; inline; - function IsUnChecked(): Boolean; inline; - function IsMixed(): Boolean; inline; - end; TCheckImageKind = ( ckCustom, // application defined check images @@ -428,138 +304,6 @@ TCheckStateHelper = record helper for TCheckState ); - // There is a heap of switchable behavior in the tree. Since published properties may never exceed 4 bytes, - // which limits sets to at most 32 members, and because for better overview tree options are splitted - // in various sub-options and are held in a commom options class. - // - // Options to customize tree appearance: - TVTPaintOption = ( - toHideFocusRect, // Avoid drawing the dotted rectangle around the currently focused node. - toHideSelection, // Selected nodes are drawn as unselected nodes if the tree is unfocused. - toHotTrack, // Track which node is under the mouse cursor. - toPopupMode, // Paint tree as would it always have the focus (useful for tree combo boxes etc.) - toShowBackground, // Use the background image if there's one. - toShowButtons, // Display collapse/expand buttons left to a node. - toShowDropmark, // Show the dropmark during drag'n drop operations. - toShowHorzGridLines, // Display horizontal lines to simulate a grid. - toShowRoot, // Show lines also at top level (does not show the hidden/internal root node). - toShowTreeLines, // Display tree lines to show hierarchy of nodes. - toShowVertGridLines, // Display vertical lines (depending on columns) to simulate a grid. - toThemeAware, // Draw UI elements (header, tree buttons etc.) according to the current theme if - // enabled (Windows XP+ only, application must be themed). - toUseBlendedImages, // Enable alpha blending for ghosted nodes or those which are being cut/copied. - toGhostedIfUnfocused, // Ghosted images are still shown as ghosted if unfocused (otherwise the become non-ghosted - // images). - toFullVertGridLines, // Display vertical lines over the full client area, not only the space occupied by nodes. - // This option only has an effect if toShowVertGridLines is enabled too. - toAlwaysHideSelection, // Do not draw node selection, regardless of focused state. - toUseBlendedSelection, // Enable alpha blending for node selections. - toStaticBackground, // Show simple static background instead of a tiled one. - toChildrenAbove, // Display child nodes above their parent. - toFixedIndent, // Draw the tree with a fixed indent. - toUseExplorerTheme, // Use the explorer theme if run under Windows Vista (or above). - toHideTreeLinesIfThemed, // Do not show tree lines if theming is used. - toShowFilteredNodes // Draw nodes even if they are filtered out. - ); - TVTPaintOptions = set of TVTPaintOption; - - { Options to toggle animation support: - **Do not use toAnimatedToggle when a background image is used for the tree. - The animation does not look good as the image splits and moves with it. - } - TVTAnimationOption = ( - toAnimatedToggle, // Expanding and collapsing a node is animated (quick window scroll). - // **See note above. - toAdvancedAnimatedToggle // Do some advanced animation effects when toggling a node. - ); - TVTAnimationOptions = set of TVTAnimationOption; - - // Options which toggle automatic handling of certain situations: - TVTAutoOption = ( - toAutoDropExpand, // Expand node if it is the drop target for more than a certain time. - toAutoExpand, // Nodes are expanded (collapsed) when getting (losing) the focus. - toAutoScroll, // Scroll if mouse is near the border while dragging or selecting. - toAutoScrollOnExpand, // Scroll as many child nodes in view as possible after expanding a node. - toAutoSort, // Sort tree when Header.SortColumn or Header.SortDirection change or sort node if - // child nodes are added. Sorting will take place also if SortColum is NoColumn (-1). - toAutoSpanColumns, // Large entries continue into next column(s) if there's no text in them (no clipping). - toAutoTristateTracking, // Checkstates are automatically propagated for tri state check boxes. - toAutoHideButtons, // Node buttons are hidden when there are child nodes, but all are invisible. - toAutoDeleteMovedNodes, // Delete nodes which where moved in a drag operation (if not directed otherwise). - toDisableAutoscrollOnFocus, // Disable scrolling a node or column into view if it gets focused. - toAutoChangeScale, // Change default node height automatically if the system's font scale is set to big fonts. - toAutoFreeOnCollapse, // Frees any child node after a node has been collapsed (HasChildren flag stays there). - toDisableAutoscrollOnEdit, // Do not center a node horizontally when it is edited. - toAutoBidiColumnOrdering // When set then columns (if any exist) will be reordered from lowest index to highest index - // and vice versa when the tree's bidi mode is changed. - ); - TVTAutoOptions = set of TVTAutoOption; - - // Options which determine the tree's behavior when selecting nodes: - TVTSelectionOption = ( - toDisableDrawSelection, // Prevent user from selecting with the selection rectangle in multiselect mode. - toExtendedFocus, // Entries other than in the main column can be selected, edited etc. - toFullRowSelect, // Hit test as well as selection highlight are not constrained to the text of a node. - toLevelSelectConstraint, // Constrain selection to the same level as the selection anchor. - toMiddleClickSelect, // Allow selection, dragging etc. with the middle mouse button. This and toWheelPanning - // are mutual exclusive. - toMultiSelect, // Allow more than one node to be selected. - toRightClickSelect, // Allow selection, dragging etc. with the right mouse button. - toSiblingSelectConstraint, // Constrain selection to nodes with same parent. - toCenterScrollIntoView, // Center nodes vertically in the client area when scrolling into view. - toSimpleDrawSelection, // Simplifies draw selection, so a node's caption does not need to intersect with the - // selection rectangle. - toAlwaysSelectNode, // If this flag is set to true, the tree view tries to always have a node selected. - // This behavior is closer to the Windows TreeView and useful in Windows Explorer style applications. - toRestoreSelection, // Set to true if upon refill the previously selected nodes should be selected again. - // The nodes will be identified by its caption (text in MainColumn) - // You may use TVTHeader.RestoreSelectiuonColumnIndex to define an other column that should be used for indentification. - toSyncCheckboxesWithSelection // If checkboxes are shown, they follow the change in selections. When checkboxes are - // changed, the selections follow them and vice-versa. - // **Only supported for ctCheckBox type checkboxes. - ); - TVTSelectionOptions = set of TVTSelectionOption; - - TVTEditOptions = ( - toDefaultEdit, // Standard behaviour for end of editing (after VK_RETURN stay on edited cell). - toVerticalEdit, // After VK_RETURN switch to next column. - toHorizontalEdit // After VK_RETURN switch to next row. - ); - - // Options which do not fit into any of the other groups: - TVTMiscOption = ( - toAcceptOLEDrop, // Register tree as OLE accepting drop target - toCheckSupport, // Show checkboxes/radio buttons. - toEditable, // Node captions can be edited. - toFullRepaintOnResize, // Fully invalidate the tree when its window is resized (CS_HREDRAW/CS_VREDRAW). - toGridExtensions, // Use some special enhancements to simulate and support grid behavior. - toInitOnSave, // Initialize nodes when saving a tree to a stream. - toReportMode, // Tree behaves like TListView in report mode. - toToggleOnDblClick, // Toggle node expansion state when it is double clicked. - toWheelPanning, // Support for mouse panning (wheel mice only). This option and toMiddleClickSelect are - // mutal exclusive, where panning has precedence. - toReadOnly, // The tree does not allow to be modified in any way. No action is executed and - // node editing is not possible. - toVariableNodeHeight, // When set then GetNodeHeight will trigger OnMeasureItem to allow variable node heights. - toFullRowDrag, // Start node dragging by clicking anywhere in it instead only on the caption or image. - // Must be used together with toDisableDrawSelection. - toNodeHeightResize, // Allows changing a node's height via mouse. - toNodeHeightDblClickResize, // Allows to reset a node's height to FDefaultNodeHeight via a double click. - toEditOnClick, // Editing mode can be entered with a single click - toEditOnDblClick, // Editing mode can be entered with a double click - toReverseFullExpandHotKey // Used to define Ctrl+'+' instead of Ctrl+Shift+'+' for full expand (and similar for collapsing) - ); - TVTMiscOptions = set of TVTMiscOption; - - // Options to control data export - TVTExportMode = ( - emAll, // export all records (regardless checked state) - emChecked, // export checked records only - emUnchecked, // export unchecked records only - emVisibleDueToExpansion, //Do not export nodes that are not visible because their parent is not expanded - emSelected // export selected nodes only - ); - // Kinds of operations TVTOperationKind = ( okAutoFitColumns, @@ -587,31 +331,12 @@ TCheckStateHelper = record helper for TCheckState /// An array that can be used to calculate the offsets ofthe elements in the tree. TVTOffsets = array [TVTElement] of TDimension; - TAddPopupItemType = ( - apNormal, - apDisabled, - apHidden - ); - -const - DefaultPaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toShowRoot, toThemeAware, toUseBlendedImages]; - DefaultAnimationOptions = []; - DefaultAutoOptions = [toAutoDropExpand, toAutoTristateTracking, toAutoScrollOnExpand, toAutoDeleteMovedNodes, toAutoChangeScale, toAutoSort]; - DefaultSelectionOptions = []; - DefaultMiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning, - toEditOnClick]; - DefaultColumnOptions = [coAllowClick, coDraggable, coEnabled, coParentColor, coParentBidiMode, coResizable, - coShowDropmark, coVisible, coAllowFocus, coEditable, coStyleColor]; - type TBaseVirtualTree = class; TVirtualTreeClass = class of TBaseVirtualTree; PVirtualNode = ^TVirtualNode; - TColumnIndex = type Integer; - TColumnPosition = type Cardinal; - // This record must already be defined here and not later because otherwise BCB users will not be able // to compile (conversion done by BCB is wrong). TCacheEntry = record @@ -622,51 +347,6 @@ TCacheEntry = record TCache = array of TCacheEntry; TNodeArray = array of PVirtualNode; - TCustomVirtualTreeOptions = class(TPersistent) - private - FOwner: TBaseVirtualTree; - FPaintOptions: TVTPaintOptions; - FAnimationOptions: TVTAnimationOptions; - FAutoOptions: TVTAutoOptions; - FSelectionOptions: TVTSelectionOptions; - FMiscOptions: TVTMiscOptions; - FExportMode: TVTExportMode; - FEditOptions: TVTEditOptions; - procedure SetAnimationOptions(const Value: TVTAnimationOptions); - procedure SetAutoOptions(const Value: TVTAutoOptions); - procedure SetMiscOptions(const Value: TVTMiscOptions); - procedure SetPaintOptions(const Value: TVTPaintOptions); - procedure SetSelectionOptions(const Value: TVTSelectionOptions); - protected - // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) - function StyleServices(AControl: TControl = nil): TCustomStyleServices; - //these bypass the side effects in the regular setters. - procedure InternalSetMiscOptions(const Value: TVTMiscOptions); - public - constructor Create(AOwner: TBaseVirtualTree); virtual; - procedure AssignTo(Dest: TPersistent); override; - property AnimationOptions: TVTAnimationOptions read FAnimationOptions write SetAnimationOptions default DefaultAnimationOptions; - property AutoOptions: TVTAutoOptions read FAutoOptions write SetAutoOptions default DefaultAutoOptions; - property ExportMode: TVTExportMode read FExportMode write FExportMode default emAll; - property MiscOptions: TVTMiscOptions read FMiscOptions write SetMiscOptions default DefaultMiscOptions; - property PaintOptions: TVTPaintOptions read FPaintOptions write SetPaintOptions default DefaultPaintOptions; - property SelectionOptions: TVTSelectionOptions read FSelectionOptions write SetSelectionOptions default DefaultSelectionOptions; - property EditOptions: TVTEditOptions read FEditOptions write FEditOptions default toDefaultEdit; - - property Owner: TBaseVirtualTree read FOwner; - end; - - TTreeOptionsClass = class of TCustomVirtualTreeOptions; - - TVirtualTreeOptions = class(TCustomVirtualTreeOptions) - published - property AnimationOptions; - property AutoOptions; - property ExportMode; - property MiscOptions; - property PaintOptions; - property SelectionOptions; - end; // Used in the CF_VTREFERENCE clipboard format. PVTReference = ^TVTReference; @@ -725,38 +405,7 @@ THitInfo = record HitPoint: TPoint; end; - // auto scroll directions - TScrollDirections = set of ( - sdLeft, - sdUp, - sdRight, - sdDown - ); - - // OLE drag'n drop support - TFormatEtcArray = array of TFormatEtc; - TFormatArray = array of Word; - - // IDataObject.SetData support - TInternalStgMedium = packed record - Format: TClipFormat; - Medium: TStgMedium; - end; - TInternalStgMediumArray = array of TInternalStgMedium; - - TEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc) - private - FTree: TBaseVirtualTree; - FFormatEtcArray: TFormatEtcArray; - FCurrentIndex: Integer; - public - constructor Create(Tree: TBaseVirtualTree; const AFormatEtcArray: TFormatEtcArray); - function Clone(out Enum: IEnumFormatEtc): HResult; stdcall; - function Next(celt: Integer; out elt; pceltFetched: PLongint): HResult; stdcall; - function Reset: HResult; stdcall; - function Skip(celt: Integer): HResult; stdcall; - end; // ----- OLE drag'n drop handling @@ -774,73 +423,7 @@ TEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc) property IsDropTarget: Boolean read GetIsDropTarget; end; - // This data object is used in two different places. One is for clipboard operations and the other while dragging. - TVTDataObject = class(TInterfacedObject, IDataObject) - private - FOwner: TBaseVirtualTree; // The tree which provides clipboard or drag data. - FForClipboard: Boolean; // Determines which data to render with GetData. - FFormatEtcArray: TFormatEtcArray; - FInternalStgMediumArray: TInternalStgMediumArray; // The available formats in the DataObject - FAdviseHolder: IDataAdviseHolder; // Reference to an OLE supplied implementation for advising. - protected - function CanonicalIUnknown(const TestUnknown: IUnknown): IUnknown; - function EqualFormatEtc(FormatEtc1, FormatEtc2: TFormatEtc): Boolean; - function FindFormatEtc(TestFormatEtc: TFormatEtc; const FormatEtcArray: TFormatEtcArray): integer; - function FindInternalStgMedium(Format: TClipFormat): PStgMedium; - function HGlobalClone(HGlobal: THandle): THandle; - function RenderInternalOLEData(const FormatEtcIn: TFormatEtc; var Medium: TStgMedium; var OLEResult: HResult): Boolean; - function StgMediumIncRef(const InStgMedium: TStgMedium; var OutStgMedium: TStgMedium; - CopyInMedium: Boolean; const DataObject: IDataObject): HRESULT; - - property ForClipboard: Boolean read FForClipboard; - property FormatEtcArray: TFormatEtcArray read FFormatEtcArray write FFormatEtcArray; - property InternalStgMediumArray: TInternalStgMediumArray read FInternalStgMediumArray write FInternalStgMediumArray; - property Owner: TBaseVirtualTree read FOwner; - public - constructor Create(AOwner: TBaseVirtualTree; ForClipboard: Boolean); virtual; - destructor Destroy; override; - - function DAdvise(const FormatEtc: TFormatEtc; advf: Integer; const advSink: IAdviseSink; out dwConnection: Integer): - HResult; virtual; stdcall; - function DUnadvise(dwConnection: Integer): HResult; virtual; stdcall; - function EnumDAdvise(out enumAdvise: IEnumStatData): HResult; virtual; stdcall; - function EnumFormatEtc(Direction: Integer; out EnumFormatEtc: IEnumFormatEtc): HResult; virtual; stdcall; - function GetCanonicalFormatEtc(const FormatEtc: TFormatEtc; out FormatEtcOut: TFormatEtc): HResult; virtual; stdcall; - function GetData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium): HResult; virtual; stdcall; - function GetDataHere(const FormatEtc: TFormatEtc; out Medium: TStgMedium): HResult; virtual; stdcall; - function QueryGetData(const FormatEtc: TFormatEtc): HResult; virtual; stdcall; - function SetData(const FormatEtc: TFormatEtc; var Medium: TStgMedium; DoRelease: BOOL): HResult; virtual; stdcall; - end; - - // TVTDragManager is a class to manage drag and drop in a Virtual Treeview. - TVTDragManager = class(TInterfacedObject, IVTDragManager, IDropSource, IDropTarget) - private - FOwner, // The tree which is responsible for drag management. - FDragSource: TBaseVirtualTree; // Reference to the source tree if the source was a VT, might be different than - // the owner tree. - FIsDropTarget: Boolean; // True if the owner is currently the drop target. - FDataObject: IDataObject; // A reference to the data object passed in by DragEnter (only used when the owner - // tree is the current drop target). - FDropTargetHelper: IDropTargetHelper; // Win2k > Drag image support - FFullDragging: BOOL; // True, if full dragging is currently enabled in the system. - - function GetDataObject: IDataObject; stdcall; - function GetDragSource: TBaseVirtualTree; stdcall; - function GetDropTargetHelperSupported: Boolean; stdcall; - function GetIsDropTarget: Boolean; stdcall; - public - constructor Create(AOwner: TBaseVirtualTree); virtual; - destructor Destroy; override; - function DragEnter(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint; - var Effect: Longint): HResult; stdcall; - function DragLeave: HResult; stdcall; - function DragOver(KeyState: Integer; Pt: TPoint; var Effect: LongInt): HResult; stdcall; - function Drop(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint; var Effect: Integer): HResult; stdcall; - procedure ForceDragLeave; stdcall; - function GiveFeedback(Effect: Integer): HResult; stdcall; - function QueryContinueDrag(EscapePressed: BOOL; KeyState: Integer): HResult; stdcall; - end; PVTHintData = ^TVTHintData; TVTHintData = record @@ -871,431 +454,6 @@ TVirtualTreeHintWindow = class(THintWindow) function IsHintMsg(var Msg: TMsg): Boolean; override; end; - // Drag image support for the tree. - TVTTransparency = 0..255; - TVTBias = -128..127; - - // Simple move limitation for the drag image. - TVTDragMoveRestriction = ( - dmrNone, - dmrHorizontalOnly, - dmrVerticalOnly - ); - - TVTDragImageStates = set of ( - disHidden, // Internal drag image is currently hidden (always hidden if drag image helper interfaces are used). - disInDrag, // Drag image class is currently being used. - disPrepared, // Drag image class is prepared. - disSystemSupport // Running on Windows 2000 or higher. System supports drag images natively. - ); - - // Class to manage header and tree drag image during a drag'n drop operation. - TVTDragImage = class - private - FOwner: TBaseVirtualTree; - FBackImage, // backup of overwritten screen area - FAlphaImage, // target for alpha blending - FDragImage: TBitmap; // the actual drag image to blend to screen - FImagePosition, // position of image (upper left corner) in screen coordinates - FLastPosition: TPoint; // last mouse position in screen coordinates - FTransparency: TVTTransparency; // alpha value of the drag image (0 - fully transparent, 255 - fully opaque) - FPreBlendBias, // value to darken or lighten the drag image before it is blended - FPostBlendBias: TVTBias; // value to darken or lighten the alpha blend result - FFade: Boolean; // determines whether to fade the drag image from center to borders or not - FRestriction: TVTDragMoveRestriction; // determines in which directions the drag image can be moved - FColorKey: TColor; // color to make fully transparent regardless of any other setting - FStates: TVTDragImageStates; // Determines the states of the drag image class. - function GetVisible: Boolean; // True if the drag image is currently hidden (used only when dragging) - procedure InternalShowDragImage(ScreenDC: HDC); - procedure MakeAlphaChannel(Source, Target: TBitmap); - procedure RecaptureBackground(Tree: TBaseVirtualTree; R: TRect; VisibleRegion: HRGN; CaptureNCArea, - ReshowDragImage: Boolean); - function WillMove(P: TPoint): Boolean; - property Visible: Boolean read GetVisible; - property PreBlendBias: TVTBias read FPreBlendBias write FPreBlendBias default 0; - property Transparency: TVTTransparency read FTransparency write FTransparency default 128; - property ColorKey: TColor read FColorKey write FColorKey default clWindow; - property Fade: Boolean read FFade write FFade default False; - public - constructor Create(AOwner: TBaseVirtualTree); - destructor Destroy; override; - - function DragTo(P: TPoint; ForceRepaint: Boolean): Boolean; - procedure EndDrag; - function GetDragImageRect: TRect; - procedure HideDragImage; - procedure PrepareDrag(DragImage: TBitmap; ImagePosition, HotSpot: TPoint; const DataObject: IDataObject); - procedure ShowDragImage; - property ImagePosition : TPoint read FImagePosition; - property LastPosition : TPoint read FLastPosition; - property MoveRestriction: TVTDragMoveRestriction read FRestriction write FRestriction default dmrNone; - end; - - // tree columns implementation - TVirtualTreeColumns = class; - TVTHeader = class; - - TVirtualTreeColumnStyle = ( - vsText, - vsOwnerDraw - ); - - TVTHeaderColumnLayout = ( - blGlyphLeft, - blGlyphRight, - blGlyphTop, - blGlyphBottom - ); - - TSortDirection = ( - sdAscending, - sdDescending - ); - - TSortDirectionHelper = record helper for VirtualTrees.TSortDirection - strict private - const cSortDirectionToInt: Array [TSortDirection] of Integer = (1, -1); - public - /// Returns +1 for ascending and -1 for descending sort order. - function ToInt(): Integer; inline; - end; - - // Used during owner draw of the header to indicate which drop mark for the column must be drawn. - TVTDropMarkMode = ( - dmmNone, - dmmLeft, - dmmRight - ); - - TVirtualTreeColumn = class; - - // This structure carries all important information about header painting and is used in the advanced header painting. - THeaderPaintInfo = record - TargetCanvas: TCanvas; - Column: TVirtualTreeColumn; - PaintRectangle: TRect; - TextRectangle: TRect; - IsHoverIndex, - IsDownIndex, - IsEnabled, - ShowHeaderGlyph, - ShowSortGlyph, - ShowRightBorder: Boolean; - DropMark: TVTDropMarkMode; - GlyphPos, - SortGlyphPos: TPoint; - SortGlyphSize: TSize; - procedure DrawSortArrow(pDirection: TSortDirection); - procedure DrawDropMark(); - end; - - - TVirtualTreeColumn = class(TCollectionItem) - private - const cDefaultColumnSpacing = 3; - private - FText, - FHint: string; - FWidth: TDimension; - FPosition: TColumnPosition; - FMinWidth: TDimension; - FMaxWidth: TDimension; - FStyle: TVirtualTreeColumnStyle; - FImageIndex: TImageIndex; - FBiDiMode: TBiDiMode; - FLayout: TVTHeaderColumnLayout; - FMargin, - FSpacing: TDimension; - FOptions: TVTColumnOptions; - FEditOptions: TVTEditOptions; - FEditNextColumn: TDimension; - FTag: NativeInt; - FAlignment: TAlignment; - FCaptionAlignment: TAlignment; // Alignment of the caption. - FLastWidth: TDimension; - FColor: TColor; - FBonusPixel: Boolean; - FSpringRest: Single; // Accumulator for width adjustment when auto spring option is enabled. - FCaptionText: string; - FCheckBox: Boolean; - FCheckType: TCheckType; - FCheckState: TCheckState; - FImageRect: TRect; - FHasImage: Boolean; - FDefaultSortDirection: TSortDirection; - function GetCaptionAlignment: TAlignment; - function GetCaptionWidth: TDimension; - function GetLeft: TDimension; - function IsBiDiModeStored: Boolean; - function IsCaptionAlignmentStored: Boolean; - function IsColorStored: Boolean; - procedure SetAlignment(const Value: TAlignment); - procedure SetBiDiMode(Value: TBiDiMode); - procedure SetCaptionAlignment(const Value: TAlignment); - procedure SetCheckBox(Value: Boolean); - procedure SetCheckState(Value: TCheckState); - procedure SetCheckType(Value: TCheckType); - procedure SetColor(const Value: TColor); - procedure SetImageIndex(Value: TImageIndex); - procedure SetLayout(Value: TVTHeaderColumnLayout); - procedure SetMargin(Value: TDimension); - procedure SetMaxWidth(Value: TDimension); - procedure SetMinWidth(Value: TDimension); - procedure SetOptions(Value: TVTColumnOptions); - procedure SetPosition(Value: TColumnPosition); - procedure SetSpacing(Value: TDimension); - procedure SetStyle(Value: TVirtualTreeColumnStyle); - procedure SetWidth(Value: TDimension); - protected - FLeft: TDimension; - procedure ChangeScale(M, D: TDimension; isDpiChange: Boolean); virtual; - procedure ComputeHeaderLayout(var PaintInfo: THeaderPaintInfo; DrawFormat: Cardinal; CalculateTextRect: Boolean = False); - procedure DefineProperties(Filer: TFiler); override; - procedure GetAbsoluteBounds(var Left, Right: TDimension); - function GetDisplayName: string; override; - function GetText: string; virtual; // [IPK] - procedure SetText(const Value: string); virtual; // [IPK] private to protected & virtual - function GetOwner: TVirtualTreeColumns; reintroduce; - procedure InternalSetWidth(const value : TDimension); //bypass side effects in SetWidth - procedure ReadHint(Reader: TReader); - procedure ReadText(Reader: TReader); - procedure SetCollection(Value: TCollection); override; - public - constructor Create(Collection: TCollection); override; - destructor Destroy; override; - - procedure Assign(Source: TPersistent); override; - function Equals(OtherColumnObj: TObject): Boolean; override; - function GetRect: TRect; virtual; - property HasImage: Boolean read FHasImage; - property ImageRect: TRect read FImageRect; - procedure LoadFromStream(const Stream: TStream; Version: Integer); - procedure ParentBiDiModeChanged; - procedure ParentColorChanged; - procedure RestoreLastWidth; - function GetEffectiveColor(): TColor; - procedure SaveToStream(const Stream: TStream); - function UseRightToLeftReading: Boolean; - - property BonusPixel: Boolean read FBonusPixel write FBonusPixel; - property CaptionText: string read FCaptionText; - property Left: TDimension read GetLeft; - property Owner: TVirtualTreeColumns read GetOwner; - property SpringRest: Single read FSpringRest write FSpringRest; - published - property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify; - property BiDiMode: TBiDiMode read FBiDiMode write SetBiDiMode stored IsBiDiModeStored; - property CaptionAlignment: TAlignment read GetCaptionAlignment write SetCaptionAlignment - stored IsCaptionAlignmentStored default taLeftJustify; - property CaptionWidth: TDimension read GetCaptionWidth; - property CheckType: TCheckType read FCheckType write SetCheckType default ctCheckBox; - property CheckState: TCheckState read FCheckState write SetCheckState default csUncheckedNormal; - property CheckBox: Boolean read FCheckBox write SetCheckBox default False; - property Color: TColor read FColor write SetColor stored IsColorStored; - property DefaultSortDirection: TSortDirection read FDefaultSortDirection write FDefaultSortDirection default sdAscending; - property Hint: string read FHint write FHint; - property ImageIndex: TImageIndex read FImageIndex write SetImageIndex default -1; - property Layout: TVTHeaderColumnLayout read FLayout write SetLayout default blGlyphLeft; - property Margin: TDimension read FMargin write SetMargin default 4; - property MaxWidth: TDimension read FMaxWidth write SetMaxWidth default 10000; - property MinWidth: TDimension read FMinWidth write SetMinWidth default 10; - property Options: TVTColumnOptions read FOptions write SetOptions default DefaultColumnOptions; - property EditOptions: TVTEditOptions read FEditOptions write FEditOptions default toDefaultEdit; - property EditNextColumn: TDimension read FEditNextColumn write FEditNextColumn default -1; - property Position: TColumnPosition read FPosition write SetPosition; - property Spacing: TDimension read FSpacing write SetSpacing default cDefaultColumnSpacing; - property Style: TVirtualTreeColumnStyle read FStyle write SetStyle default vsText; - property Tag: NativeInt read FTag write FTag default 0; - property Text: string read GetText write SetText; - property Width: TDimension read FWidth write SetWidth default 50; - end; - - TVirtualTreeColumnClass = class of TVirtualTreeColumn; - - TColumnsArray = array of TVirtualTreeColumn; - TCardinalArray = array of Cardinal; - TIndexArray = array of TColumnIndex; - - TVirtualTreeColumns = class(TCollection) - private - FHeader: TVTHeader; - FHeaderBitmap: TBitmap; // backbuffer for drawing - FHoverIndex, // currently "hot" column - FDownIndex, // Column on which a mouse button is held down. - FTrackIndex: TColumnIndex; // Index of column which is currently being resized. - FClickIndex: TColumnIndex; // Index of the last clicked column. - FCheckBoxHit: Boolean; // True if the last click was on a header checkbox. - FPositionToIndex: TIndexArray; - FDefaultWidth: TDimension; // the width columns are created with - FNeedPositionsFix: Boolean; // True if FixPositions must still be called after DFM loading or Bidi mode change. - FClearing: Boolean; // True if columns are being deleted entirely. - FColumnPopupMenu: TPopupMenu; // Member for storing the TVTHeaderPopupMenu - - function GetCount: TDimension; - function GetItem(Index: TColumnIndex): TVirtualTreeColumn; - function GetNewIndex(P: TPoint; var OldIndex: TColumnIndex): Boolean; - procedure SetDefaultWidth(Value: TDimension); - procedure SetItem(Index: TColumnIndex; Value: TVirtualTreeColumn); - protected - // drag support - FDragIndex: TColumnIndex; // index of column currently being dragged - FDropTarget: TColumnIndex; // current target column (index) while dragging - FDropBefore: Boolean; // True if drop position is in the left half of a column, False for the right - // side to drop the dragged column to - - procedure AdjustAutoSize(CurrentIndex: TColumnIndex; Force: Boolean = False); - function AdjustDownColumn(P: TPoint): TColumnIndex; - function AdjustHoverColumn(P: TPoint): Boolean; - procedure AdjustPosition(Column: TVirtualTreeColumn; Position: Cardinal); - function CanSplitterResize(P: TPoint; Column: TColumnIndex): Boolean; - procedure DoCanSplitterResize(P: TPoint; Column: TColumnIndex; var Allowed: Boolean); virtual; - procedure DrawButtonText(DC: HDC; Caption: string; Bounds: TRect; Enabled, Hot: Boolean; DrawFormat: Cardinal; - WrapCaption: Boolean); - procedure FixPositions; - function GetColumnAndBounds(P: TPoint; var ColumnLeft, ColumnRight: TDimension; Relative: Boolean = True): Integer; - function GetOwner: TPersistent; override; - function HandleClick(P: TPoint; Button: TMouseButton; Force, DblClick: Boolean): Boolean; virtual; - procedure HeaderPopupMenuAddHeaderPopupItem(const Sender: TBaseVirtualTree; const Column: TColumnIndex; - var Cmd: TAddPopupItemType); - procedure HeaderPopupMenuColumnChange(const Sender: TBaseVirtualTree; const Column: TColumnIndex; Visible: Boolean); - procedure IndexChanged(OldIndex, NewIndex: Integer); - procedure InitializePositionArray; - procedure Notify(Item: TCollectionItem; Action: System.Classes.TCollectionNotification); override; - procedure ReorderColumns(RTL: Boolean); - procedure SetHoverIndex(index : TColumnIndex); - procedure Update(Item: TCollectionItem); override; - procedure UpdatePositions(Force: Boolean = False); - - property HeaderBitmap: TBitmap read FHeaderBitmap; - property PositionToIndex: TIndexArray read FPositionToIndex; - property HoverIndex: TColumnIndex read FHoverIndex write FHoverIndex; - property DownIndex: TColumnIndex read FDownIndex write FDownIndex; - property CheckBoxHit: Boolean read FCheckBoxHit write FCheckBoxHit; - // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) - function StyleServices(AControl: TControl = nil): TCustomStyleServices; - public - constructor Create(AOwner: TVTHeader); virtual; - destructor Destroy; override; - - function Add: TVirtualTreeColumn; virtual; - procedure AnimatedResize(Column: TColumnIndex; NewWidth: TDimension); - procedure Assign(Source: TPersistent); override; - procedure Clear; virtual; - function ColumnFromPosition(P: TPoint; Relative: Boolean = True): TColumnIndex; overload; virtual; - function ColumnFromPosition(PositionIndex: TColumnPosition): TColumnIndex; overload; virtual; - function Equals(OtherColumnsObj: TObject): Boolean; override; - procedure GetColumnBounds(Column: TColumnIndex; var Left, Right: TDimension); - function GetFirstVisibleColumn(ConsiderAllowFocus: Boolean = False): TColumnIndex; - function GetLastVisibleColumn(ConsiderAllowFocus: Boolean = False): TColumnIndex; - function GetFirstColumn: TColumnIndex; - function GetNextColumn(Column: TColumnIndex): TColumnIndex; - function GetNextVisibleColumn(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex; - function GetPreviousColumn(Column: TColumnIndex): TColumnIndex; - function GetPreviousVisibleColumn(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex; - function GetScrollWidth: TDimension; - function GetVisibleColumns: TColumnsArray; - function GetVisibleFixedWidth: TDimension; - function IsValidColumn(Column: TColumnIndex): Boolean; - procedure LoadFromStream(const Stream: TStream; Version: Integer); - procedure PaintHeader(DC: HDC; R: TRect; HOffset: TDimension); overload; virtual; - procedure PaintHeader(TargetCanvas: TCanvas; R: TRect; const Target: TPoint; - RTLOffset: TDimension = 0); overload; virtual; - procedure SaveToStream(const Stream: TStream); - procedure EndUpdate(); override; - function TotalWidth: TDimension; - - property Count: Integer read GetCount; - property ClickIndex: TColumnIndex read FClickIndex; - property DefaultWidth: TDimension read FDefaultWidth write SetDefaultWidth; - property DragIndex : TColumnIndex read FDragIndex write FDragIndex; - property DropBefore : boolean read FDropBefore write FDropBefore; - property DropTarget : TColumnIndex read FDropTarget write FDropTarget; - property Items[Index: TColumnIndex]: TVirtualTreeColumn read GetItem write SetItem; default; - property Header: TVTHeader read FHeader; - property TrackIndex: TColumnIndex read FTrackIndex write FTrackIndex; - end; - - TVirtualTreeColumnsClass = class of TVirtualTreeColumns; - - TVTConstraintPercent = 0..100; - TVTFixedAreaConstraints = class(TPersistent) - private - FHeader: TVTHeader; - FMaxHeightPercent, - FMaxWidthPercent, - FMinHeightPercent, - FMinWidthPercent: TVTConstraintPercent; - FOnChange: TNotifyEvent; - procedure SetConstraints(Index: Integer; Value: TVTConstraintPercent); - protected - procedure Change; - property Header: TVTHeader read FHeader; - public - constructor Create(AOwner: TVTHeader); - - procedure Assign(Source: TPersistent); override; - property OnChange: TNotifyEvent read FOnChange write FOnChange; - published - property MaxHeightPercent: TVTConstraintPercent index 0 read FMaxHeightPercent write SetConstraints default 0; - property MaxWidthPercent: TVTConstraintPercent index 1 read FMaxWidthPercent write SetConstraints default 95; - property MinHeightPercent: TVTConstraintPercent index 2 read FMinHeightPercent write SetConstraints default 0; - property MinWidthPercent: TVTConstraintPercent index 3 read FMinWidthPercent write SetConstraints default 0; - end; - - TVTHeaderStyle = ( - hsThickButtons, // TButton look and feel - hsFlatButtons, // flatter look than hsThickButton, like an always raised flat TToolButton - hsPlates // flat TToolButton look and feel (raise on hover etc.) - ); - - TVTHeaderOption = ( - hoAutoResize, // Adjust a column so that the header never exceeds the client width of the owner control. - hoColumnResize, // Resizing columns with the mouse is allowed. - hoDblClickResize, // Allows a column to resize itself to its largest entry. - hoDrag, // Dragging columns is allowed. - hoHotTrack, // Header captions are highlighted when mouse is over a particular column. - hoOwnerDraw, // Header items with the owner draw style can be drawn by the application via event. - hoRestrictDrag, // Header can only be dragged horizontally. - hoShowHint, // Show application defined header hint. - hoShowImages, // Show header images. - hoShowSortGlyphs, // Allow visible sort glyphs. - hoVisible, // Header is visible. - hoAutoSpring, // Distribute size changes of the header to all columns, which are sizable and have the - // coAutoSpring option enabled. - hoFullRepaintOnResize, // Fully invalidate the header (instead of subsequent columns only) when a column is resized. - hoDisableAnimatedResize, // Disable animated resize for all columns. - hoHeightResize, // Allow resizing header height via mouse. - hoHeightDblClickResize, // Allow the header to resize itself to its default height. - hoHeaderClickAutoSort, // Clicks on the header will make the clicked column the SortColumn or toggle sort direction if - // it already was the sort column - hoAutoColumnPopupMenu, // Show a context menu for activating and deactivating columns on right click - hoAutoResizeInclCaption // Includes the header caption for the auto resizing - ); - TVTHeaderOptions = set of TVTHeaderOption; - - THeaderState = ( - hsAutoSizing, // auto size chain is in progess, do not trigger again on WM_SIZE - hsDragging, // header dragging is in progress (only if enabled) - hsDragPending, // left button is down, user might want to start dragging a column - hsLoading, // The header currently loads from stream, so updates are not necessary. - hsColumnWidthTracking, // column resizing is in progress - hsColumnWidthTrackPending, // left button is down, user might want to start resize a column - hsHeightTracking, // height resizing is in progress - hsHeightTrackPending, // left button is down, user might want to start changing height - hsResizing, // multi column resizing in progress - hsScaling, // the header is scaled after a change of FixedAreaConstraints or client size - hsNeedScaling // the header needs to be scaled - ); - THeaderStates = set of THeaderState; - - - TSmartAutoFitType = ( - smaAllColumns, // consider nodes in view only for all columns - smaNoColumn, // consider nodes in view only for no column - smaUseColumnOption // use coSmartResize of the corresponding column - ); // describes the used column resize behaviour for AutoFitColumns - - TChangeReason = ( crIgnore, // used as placeholder crAccumulated, // used for delayed changes @@ -1306,138 +464,6 @@ TVTFixedAreaConstraints = class(TPersistent) crNodeMoved // a node has been moved to a new place ); // desribes what made a structure change event happen - TVTHeader = class(TPersistent) - private - FOwner: TBaseVirtualTree; - FColumns: TVirtualTreeColumns; - FHeight: TDimension; - FFont: TFont; - FParentFont: Boolean; - FOptions: TVTHeaderOptions; - FStyle: TVTHeaderStyle; // button style - FBackgroundColor: TColor; - FAutoSizeIndex: TColumnIndex; - FPopupMenu: TPopupMenu; - FMainColumn: TColumnIndex; // the column which holds the tree - FMaxHeight: TDimension; - FMinHeight: TDimension; - FDefaultHeight: TDimension; - FFixedAreaConstraints: TVTFixedAreaConstraints; // Percentages for the fixed area (header, fixed columns). - FImages: TCustomImageList; - FImageChangeLink: TChangeLink; // connections to the image list to get notified about changes - fSplitterHitTolerance: TDimension; // For property SplitterHitTolerance - FSortColumn: TColumnIndex; - FSortDirection: TSortDirection; - FDragImage: TVTDragImage; // drag image management during header drag - FLastWidth: TDimension; // Used to adjust spring columns. This is the width of all visible columns, not the header rectangle. - FRestoreSelectionColumnIndex: Integer; // The column that is used to implement the coRestoreSelection option - function GetMainColumn: TColumnIndex; - function GetUseColumns: Boolean; - function IsFontStored: Boolean; - procedure SetAutoSizeIndex(Value: TColumnIndex); - procedure SetBackground(Value: TColor); - procedure SetColumns(Value: TVirtualTreeColumns); - procedure SetDefaultHeight(Value: TDimension); - procedure SetFont(const Value: TFont); - procedure SetHeight(Value: TDimension); - procedure SetImages(const Value: TCustomImageList); - procedure SetMainColumn(Value: TColumnIndex); - procedure SetMaxHeight(Value: TDimension); - procedure SetMinHeight(Value: TDimension); - procedure SetOptions(Value: TVTHeaderOptions); - procedure SetParentFont(Value: Boolean); - procedure SetSortColumn(Value: TColumnIndex); - procedure SetSortDirection(const Value: TSortDirection); - procedure SetStyle(Value: TVTHeaderStyle); - function GetRestoreSelectionColumnIndex: Integer; - protected - FStates: THeaderStates; // Used to keep track of internal states the header can enter. - FDragStart: TPoint; // initial mouse drag position - FTrackStart: TPoint; // client coordinates of the tracking start point - FTrackPoint: TPoint; // Client coordinate where the tracking started. - FDoingAutoFitColumns: boolean; // Flag to avoid using the stored width for Main column - - procedure FontChanged(Sender: TObject); virtual; - procedure AutoScale(); virtual; - function CanSplitterResize(P: TPoint): Boolean; - function CanWriteColumns: Boolean; virtual; - procedure ChangeScale(M, D: TDimension; isDpiChange: Boolean); virtual; - function DetermineSplitterIndex(P: TPoint): Boolean; virtual; - procedure DoAfterAutoFitColumn(Column: TColumnIndex); virtual; - procedure DoAfterColumnWidthTracking(Column: TColumnIndex); virtual; - procedure DoAfterHeightTracking; virtual; - function DoBeforeAutoFitColumn(Column: TColumnIndex; SmartAutoFitType: TSmartAutoFitType): Boolean; virtual; - procedure DoBeforeColumnWidthTracking(Column: TColumnIndex; Shift: TShiftState); virtual; - procedure DoBeforeHeightTracking(Shift: TShiftState); virtual; - procedure DoCanSplitterResize(P: TPoint; var Allowed: Boolean); virtual; - function DoColumnWidthDblClickResize(Column: TColumnIndex; P: TPoint; Shift: TShiftState): Boolean; virtual; - function DoColumnWidthTracking(Column: TColumnIndex; Shift: TShiftState; var TrackPoint: TPoint; P: TPoint): Boolean; virtual; - function DoGetPopupMenu(Column: TColumnIndex; Position: TPoint): TPopupMenu; virtual; - function DoHeightTracking(var P: TPoint; Shift: TShiftState): Boolean; virtual; - function DoHeightDblClickResize(var P: TPoint; Shift: TShiftState): Boolean; virtual; - procedure DoSetSortColumn(Value: TColumnIndex; pSortDirection: TSortDirection); virtual; - procedure DragTo(P: TPoint); virtual; - procedure FixedAreaConstraintsChanged(Sender: TObject); - function GetColumnsClass: TVirtualTreeColumnsClass; virtual; - function GetOwner: TPersistent; override; - function GetShiftState: TShiftState; - function HandleHeaderMouseMove(var Message: TWMMouseMove): Boolean; - function HandleMessage(var Message: TMessage): Boolean; virtual; - procedure ImageListChange(Sender: TObject); - procedure PrepareDrag(P, Start: TPoint); - procedure ReadColumns(Reader: TReader); - procedure RecalculateHeader; virtual; - procedure RescaleHeader; - procedure UpdateMainColumn; - procedure UpdateSpringColumns; - procedure WriteColumns(Writer: TWriter); - public - constructor Create(AOwner: TBaseVirtualTree); virtual; - destructor Destroy; override; - - function AllowFocus(ColumnIndex: TColumnIndex): Boolean; - procedure Assign(Source: TPersistent); override; - procedure AutoFitColumns(Animated: Boolean = True; SmartAutoFitType: TSmartAutoFitType = smaUseColumnOption; - RangeStartCol: Integer = NoColumn; RangeEndCol: Integer = NoColumn); virtual; - function InHeader(P: TPoint): Boolean; virtual; - function InHeaderSplitterArea(P: TPoint): Boolean; virtual; - procedure Invalidate(Column: TVirtualTreeColumn; ExpandToBorder: Boolean = False; UpdateNowFlag : Boolean = False); - procedure LoadFromStream(const Stream: TStream); virtual; - function ResizeColumns(ChangeBy: TDimension; RangeStartCol: TColumnIndex; RangeEndCol: TColumnIndex; - Options: TVTColumnOptions = [coVisible]): TDimension; - procedure RestoreColumns; - procedure SaveToStream(const Stream: TStream); virtual; - procedure StyleChanged(); virtual; - - property DragImage: TVTDragImage read FDragImage; - property RestoreSelectionColumnIndex: Integer read GetRestoreSelectionColumnIndex write fRestoreSelectionColumnIndex default NoColumn; - property States: THeaderStates read FStates; - property Treeview: TBaseVirtualTree read FOwner; - property UseColumns: Boolean read GetUseColumns; - property doingAutoFitColumns: boolean read FDoingAutoFitColumns; - published - property AutoSizeIndex: TColumnIndex read FAutoSizeIndex write SetAutoSizeIndex; - property Background: TColor read FBackgroundColor write SetBackground default clBtnFace; - property Columns: TVirtualTreeColumns read FColumns write SetColumns stored False; // Stored by the owner tree to support VFI. - property DefaultHeight: Integer read FDefaultHeight write SetDefaultHeight default 19; - property Font: TFont read FFont write SetFont stored IsFontStored; - property FixedAreaConstraints: TVTFixedAreaConstraints read FFixedAreaConstraints write FFixedAreaConstraints; - property Height: Integer read FHeight write SetHeight default 19; - property Images: TCustomImageList read FImages write SetImages; - property MainColumn: TColumnIndex read GetMainColumn write SetMainColumn default 0; - property MaxHeight: Integer read FMaxHeight write SetMaxHeight default 10000; - property MinHeight: Integer read FMinHeight write SetMinHeight default 10; - property Options: TVTHeaderOptions read FOptions write SetOptions default [hoColumnResize, hoDrag, hoShowSortGlyphs]; - property ParentFont: Boolean read FParentFont write SetParentFont default True; - property PopupMenu: TPopupMenu read FPopupMenu write FPopupMenu; - property SortColumn: TColumnIndex read FSortColumn write SetSortColumn default NoColumn; - property SortDirection: TSortDirection read FSortDirection write SetSortDirection default sdAscending; - property SplitterHitTolerance: Integer read fSplitterHitTolerance write fSplitterHitTolerance default 8; // The area in pixels around a spliter which is sensitive for resizing - property Style: TVTHeaderStyle read FStyle write SetStyle default hsThickButtons; - end; - - TVTHeaderClass = class of TVTHeader; - // Communication interface between a tree editor and the tree itself (declared as using stdcall in case it // is implemented in a (C/C++) DLL). The GUID is not nessecary in Delphi but important for BCB users // to allow QueryInterface and _uuidof calls. @@ -1594,7 +620,6 @@ TVTHeaderClass = class of TVTHeader; // A collection of line type IDs which is used while painting a node. TLineImage = array of TVTLineType; - TVTScrollIncrement = 1..10000; // Export type TVTExportType = ( @@ -1614,106 +639,6 @@ TVTHeaderClass = class of TVTHeader; TVTColumnExportEvent = procedure (Sender: TBaseVirtualTree; aExportType: TVTExportType; Column: TVirtualTreeColumn) of object; TVTTreeExportEvent = procedure(Sender: TBaseVirtualTree; aExportType: TVTExportType) of object; - // A class to manage scroll bar aspects. - TScrollBarOptions = class(TPersistent) - private - FAlwaysVisible: Boolean; - FOwner: TBaseVirtualTree; - FScrollBars: TScrollStyle; // used to hide or show vertical and/or horizontal scrollbar - FScrollBarStyle: TScrollBarStyle; // kind of scrollbars to use - FIncrementX, - FIncrementY: TVTScrollIncrement; // number of pixels to scroll in one step (when auto scrolling) - procedure SetAlwaysVisible(Value: Boolean); - procedure SetScrollBars(Value: TScrollStyle); - procedure SetScrollBarStyle(Value: TScrollBarStyle); - protected - function GetOwner: TPersistent; override; - public - constructor Create(AOwner: TBaseVirtualTree); - - procedure Assign(Source: TPersistent); override; - published - property AlwaysVisible: Boolean read FAlwaysVisible write SetAlwaysVisible default False; - property HorizontalIncrement: TVTScrollIncrement read FIncrementX write FIncrementX default 20; - property ScrollBars: TScrollStyle read FScrollBars write SetScrollBars default ssBoth; - property ScrollBarStyle: TScrollBarStyle read FScrollBarStyle write SetScrollBarStyle default sbmRegular; - property VerticalIncrement: TVTScrollIncrement read FIncrementY write FIncrementY default 20; - end; - - // class to collect all switchable colors into one place - TVTColors = class(TPersistent) - private - type - TVTColorEnum =(cDisabledColor, cDropMarkColor, cDropTargetColor, cFocusedSelectionColor, - cGridLineColor, cTreeLineColor, cUnfocusedSelectionColor, cBorderColor, cHotColor, - cFocusedSelectionBorderColor, cUnfocusedSelectionBorderColor, cDropTargetBorderColor, - cSelectionRectangleBlendColor, cSelectionRectangleBorderColor, cHeaderHotColor, - cSelectionTextColor, cUnfocusedColor); - - // Please make sure that the published Color properties at the corresponding index - // have the same color if you change anything here! - const cDefaultColors : array[TVTColorEnum] of TColor = ( - clBtnShadow, // DisabledColor - clHighlight, // DropMarkColor - clHighLight, // DropTargetColor - clHighLight, // FocusedSelectionColor - clBtnFace, // GridLineColor - clBtnShadow, // TreeLineColor - clInactiveCaption, // UnfocusedSelectionColor - clBtnFace, // BorderColor - clWindowText, // HotColor - clHighLight, // FocusedSelectionBorderColor - clInactiveCaption, // UnfocusedSelectionBorderColor - clHighlight, // DropTargetBorderColor - clHighlight, // SelectionRectangleBlendColor - clHighlight, // SelectionRectangleBorderColor - clBtnShadow, // HeaderHotColor - clHighlightText, // SelectionTextColor - clInactiveCaptionText); // UnfocusedColor [IPK] - - private - FOwner: TBaseVirtualTree; - FColors: array[TVTColorEnum] of TColor; // [IPK] 15 -> 16 - function GetColor(const Index: TVTColorEnum): TColor; - procedure SetColor(const Index: TVTColorEnum; const Value: TColor); - function GetBackgroundColor: TColor; - function GetHeaderFontColor: TColor; - function GetNodeFontColor: TColor; - function GetSelectedNodeFontColor(Focused:boolean): TColor; - public - constructor Create(AOwner: TBaseVirtualTree); - - procedure Assign(Source: TPersistent); override; - property BackGroundColor: TColor read GetBackgroundColor; - property HeaderFontColor: TColor read GetHeaderFontColor; - property NodeFontColor: TColor read GetNodeFontColor; - // Mitigator function to use the correct style service for this context (either the style assigned to the control for Delphi > 10.4 or the application style) - function StyleServices(AControl: TControl = nil): TCustomStyleServices; - published - property BorderColor: TColor index cBorderColor read GetColor write SetColor default clBtnFace; - property DisabledColor: TColor index cDisabledColor read GetColor write SetColor default clBtnShadow; - property DropMarkColor: TColor index cDropMarkColor read GetColor write SetColor default clHighlight; - property DropTargetColor: TColor index cDropTargetColor read GetColor write SetColor default clHighLight; - property DropTargetBorderColor: TColor index cDropTargetBorderColor read GetColor write SetColor default clHighLight; - /// The background color of selected nodes in case the tree has the focus, or the toPopupMode flag is set. - property FocusedSelectionColor: TColor index cFocusedSelectionColor read GetColor write SetColor default clHighLight; - /// The border color of selected nodes when the tree has the focus. - property FocusedSelectionBorderColor: TColor index cFocusedSelectionBorderColor read GetColor write SetColor default clHighLight; - property GridLineColor: TColor index cGridLineColor read GetColor write SetColor default clBtnFace; - property HeaderHotColor: TColor index cHeaderHotColor read GetColor write SetColor default clBtnShadow; - property HotColor: TColor index cHotColor read GetColor write SetColor default clWindowText; - property SelectionRectangleBlendColor: TColor index cSelectionRectangleBlendColor read GetColor write SetColor default clHighlight; - property SelectionRectangleBorderColor: TColor index cSelectionRectangleBorderColor read GetColor write SetColor default clHighlight; - /// The text color of selected nodes - property SelectionTextColor: TColor index cSelectionTextColor read GetColor write SetColor default clHighlightText; - property TreeLineColor: TColor index cTreeLineColor read GetColor write SetColor default clBtnShadow; - property UnfocusedColor: TColor index cUnfocusedColor read GetColor write SetColor default clInactiveCaptionText; // [IPK] Added - /// The background color of selected nodes in case the tree does not have the focus and the toPopupMode flag is not set. - property UnfocusedSelectionColor: TColor index cUnfocusedSelectionColor read GetColor write SetColor default clInactiveCaption; - /// The border color of selected nodes in case the tree does not have the focus and the toPopupMode flag is not set. - property UnfocusedSelectionBorderColor: TColor index cUnfocusedSelectionBorderColor read GetColor write SetColor default clInactiveCaption; - end; - // For painting a node and its columns/cells a lot of information must be passed frequently around. TVTImageInfo = record Index: TImageIndex; // Index in the associated image list. @@ -1873,8 +798,7 @@ TClipboardFormats = class(TStringList) TVTBeforeGetCheckStateEvent = procedure(Sender: TBaseVirtualTree; Node: PVirtualNode) of object; // header/column events - TVTHeaderAddPopupItemEvent = procedure(const Sender: TBaseVirtualTree; const Column: TColumnIndex; - var Cmd: TAddPopupItemType) of object; + TVTHeaderAddPopupItemEvent = procedure(const Sender: TObject; const Column: TColumnIndex; var Cmd: TAddPopupItemType) of object; TVTHeaderClickEvent = procedure(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo) of object; TVTHeaderMouseEvent = procedure(Sender: TVTHeader; Button: TMouseButton; Shift: TShiftState; X, Y: Integer) of object; TVTHeaderMouseMoveEvent = procedure(Sender: TVTHeader; Shift: TShiftState; X, Y: Integer) of object; @@ -2371,7 +1295,6 @@ TBaseVirtualTree = class(TCustomControl) procedure CMParentDoubleBufferedChange(var Message: TMessage); message CM_PARENTDOUBLEBUFFEREDCHANGED; procedure AdjustTotalCount(Node: PVirtualNode; Value: Integer; relative: Boolean = False); - procedure AdjustTotalHeight(Node: PVirtualNode; Value: Integer; relative: Boolean = False); function CalculateCacheEntryCount: Integer; procedure CalculateVerticalAlignments(var PaintInfo: TVTPaintInfo; var VButtonAlign: Integer); function ChangeCheckState(Node: PVirtualNode; Value: TCheckState): Boolean; @@ -2477,7 +1400,6 @@ TBaseVirtualTree = class(TCustomControl) procedure SetVisiblePath(Node: PVirtualNode; Value: Boolean); procedure PrepareBackGroundPicture(Source: TPicture; DrawBitmap: TBitmap; DrawBitmapWidth: Integer; DrawBitMapHeight: Integer; ABkgcolor: TColor); procedure StaticBackground(Source: TPicture; Target: TCanvas; OffsetPosition: TPoint; R: TRect; aBkgColor: TColor); - procedure StopTimer(ID: Integer); procedure TileBackground(Source: TPicture; Target: TCanvas; Offset: TPoint; R: TRect; aBkgColor: TColor); function ToggleCallback(Step, StepSize: Integer; Data: Pointer): Boolean; @@ -2547,6 +1469,7 @@ TBaseVirtualTree = class(TCustomControl) procedure AddToSelection(const NewItems: TNodeArray; NewLength: Integer; ForceInsert: Boolean = False); overload; virtual; procedure AdjustPaintCellRect(var PaintInfo: TVTPaintInfo; var NextNonEmpty: TColumnIndex); virtual; procedure AdjustPanningCursor(X, Y: Integer); virtual; + procedure AdjustTotalHeight(Node: PVirtualNode; Value: Integer; relative: Boolean = False); procedure AdviseChangeEvent(StructureChange: Boolean; Node: PVirtualNode; Reason: TChangeReason); virtual; function AllocateInternalDataArea(Size: Cardinal): Cardinal; virtual; procedure Animate(Steps, Duration: Cardinal; Callback: TVTAnimationCallback; Data: Pointer); virtual; @@ -2567,6 +1490,7 @@ TBaseVirtualTree = class(TCustomControl) function CountVisibleChildren(Node: PVirtualNode): Cardinal; virtual; procedure CreateParams(var Params: TCreateParams); override; procedure CreateWnd; override; + procedure DecVisibleCount; procedure DefineProperties(Filer: TFiler); override; procedure DeleteNode(Node: PVirtualNode; Reindex: Boolean; ParentClearing: Boolean); overload; function DetermineDropMode(const P: TPoint; var HitInfo: THitInfo; var NodeRect: TRect): TDropMode; virtual; @@ -2736,6 +1660,7 @@ TBaseVirtualTree = class(TCustomControl) procedure HandleClickSelection(LastFocused, NewNode: PVirtualNode; Shift: TShiftState; DragPending: Boolean); function HasImage(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex): Boolean; virtual; deprecated 'Use GetImageSize instead'; function HasPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Pos: TPoint): Boolean; virtual; + procedure IncVisibleCount; procedure InitChildren(Node: PVirtualNode); virtual; procedure InitNode(Node: PVirtualNode); virtual; procedure InternalAddFromStream(Stream: TStream; Version: Integer; Node: PVirtualNode); virtual; @@ -2747,6 +1672,7 @@ TBaseVirtualTree = class(TCustomControl) procedure InternalConnectNode(Node, Destination: PVirtualNode; Target: TBaseVirtualTree; Mode: TVTNodeAttachMode); virtual; function InternalData(Node: PVirtualNode): Pointer; procedure InternalDisconnectNode(Node: PVirtualNode; KeepFocus: Boolean; Reindex: Boolean = True; ParentClearing: Boolean = False); virtual; + procedure InternalSetFocusedColumn(const index : TColumnIndex); procedure InternalRemoveFromSelection(Node: PVirtualNode); virtual; procedure InterruptValidation(pWaitForValidationTermination: Boolean = True); procedure InvalidateCache; @@ -2785,6 +1711,7 @@ TBaseVirtualTree = class(TCustomControl) procedure SkipNode(Stream: TStream); virtual; procedure StartOperation(OperationKind: TVTOperationKind); procedure StartWheelPanning(Position: TPoint); virtual; + procedure StopTimer(ID: Integer); procedure StopWheelPanning; virtual; procedure StructureChange(Node: PVirtualNode; Reason: TChangeReason); virtual; function SuggestDropEffect(Source: TObject; Shift: TShiftState; Pt: TPoint; AllowedEffects: Integer): Integer; virtual; @@ -3259,116 +2186,8 @@ TBaseVirtualTree = class(TCustomControl) property DoubleBuffered: Boolean read GetDoubleBuffered write SetDoubleBuffered default True; end; - - // --------- TCustomVirtualStringTree - - // Options regarding strings (useful only for the string tree and descendants): - TVTStringOption = ( - toSaveCaptions, // If set then the caption is automatically saved with the tree node, regardless of what is - // saved in the user data. - toShowStaticText, // Show static text in a caption which can be differently formatted than the caption - // but cannot be edited. - toAutoAcceptEditChange // Automatically accept changes during edit if the user finishes editing other then - // VK_RETURN or ESC. If not set then changes are cancelled. - ); - TVTStringOptions = set of TVTStringOption; - -const - DefaultStringOptions = [toSaveCaptions, toAutoAcceptEditChange]; - -type - TCustomStringTreeOptions = class(TCustomVirtualTreeOptions) - private - FStringOptions: TVTStringOptions; - procedure SetStringOptions(const Value: TVTStringOptions); - protected - property StringOptions: TVTStringOptions read FStringOptions write SetStringOptions default DefaultStringOptions; - public - constructor Create(AOwner: TBaseVirtualTree); override; - - procedure AssignTo(Dest: TPersistent); override; - end; - - TStringTreeOptions = class(TCustomStringTreeOptions) - published - property AnimationOptions; - property AutoOptions; - property ExportMode; - property MiscOptions; - property PaintOptions; - property SelectionOptions; - property StringOptions; - property EditOptions; - end; - TCustomVirtualStringTree = class; - // Edit support Classes. - TStringEditLink = class; - - TVTEdit = class(TCustomEdit) - private - procedure CMAutoAdjust(var Message: TMessage); message CM_AUTOADJUST; - procedure CMExit(var Message: TMessage); message CM_EXIT; - procedure CMRelease(var Message: TMessage); message CM_RELEASE; - procedure CNCommand(var Message: TWMCommand); message CN_COMMAND; - procedure WMChar(var Message: TWMChar); message WM_CHAR; - procedure WMDestroy(var Message: TWMDestroy); message WM_DESTROY; - procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE; - procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN; - protected - FRefLink: IVTEditLink; - FLink: TStringEditLink; - procedure AutoAdjustSize; virtual; - function CalcMinHeight: Integer; virtual; - procedure CreateParams(var Params: TCreateParams); override; - function GetTextSize: TSize; virtual; - procedure KeyPress(var Key: Char); override; - public - constructor Create(Link: TStringEditLink); reintroduce; - procedure ClearLink; - procedure ClearRefLink; - procedure Release; virtual; - - property AutoSelect; - property AutoSize; - property BorderStyle; - property CharCase; - property HideSelection; - property MaxLength; - property OEMConvert; - property PasswordChar; - end; - - TStringEditLink = class(TInterfacedObject, IVTEditLink) - private - FEdit: TVTEdit; // A normal custom edit control. - protected - FTree: TCustomVirtualStringTree; // A back reference to the tree calling. - FNode: PVirtualNode; // The node to be edited. - FColumn: TColumnIndex; // The column of the node. - FAlignment: TAlignment; - FTextBounds: TRect; // Smallest rectangle around the text. - FStopping: Boolean; // Set to True when the edit link requests stopping the edit action. - procedure SetEdit(const Value: TVTEdit); // Setter for the FEdit member; - public - constructor Create; virtual; - destructor Destroy; override; - property Alignment : TAlignment read FAlignment; - property Node : PVirtualNode read FNode; // [IPK] Make FNode accessible - property Column: TColumnIndex read FColumn; // [IPK] Make Column(Index) accessible - - function BeginEdit: Boolean; virtual; stdcall; - function CancelEdit: Boolean; virtual; stdcall; - property Edit: TVTEdit read FEdit write SetEdit; - function EndEdit: Boolean; virtual; stdcall; - function GetBounds: TRect; virtual; stdcall; - function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; virtual; stdcall; - procedure ProcessMessage(var Message: TMessage); virtual; stdcall; - procedure SetBounds(R: TRect); virtual; stdcall; - property Stopping : boolean read FStopping; - property Tree : TCustomVirtualStringTree read FTree; - end; // Describes the type of text to return in the text and draw info retrival events. TVSTTextType = ( @@ -4054,11 +2873,14 @@ implementation VirtualTrees.AccessibilityFactory, VirtualTrees.StyleHooks, VirtualTrees.Classes, + VirtualTrees.DataObject, VirtualTrees.WorkerThread, VirtualTrees.ClipBoard, - VirtualTrees.Utils, + VirtualTrees.Utils, VirtualTrees.Export, - VirtualTrees.HeaderPopup; + VirtualTrees.HeaderPopup, + VirtualTrees.DragnDrop, + VirtualTrees.EditLink; resourcestring // Localizable strings. @@ -4083,27 +2905,7 @@ implementation // in the compiled binary file. Copyright: string = 'Virtual Treeview © 1999-2021 Mike Lischke, Joachim Marder'; -var - StandardOLEFormat: TFormatEtc = ( - // Format must later be set. - cfFormat: 0; - // No specific target device to render on. - ptd: nil; - // Normal content to render. - dwAspect: DVASPECT_CONTENT; - // No specific page of multipage data (we don't use multipage data by default). - lindex: -1; - // Acceptable storage formats are IStream and global memory. The first is preferred. - tymed: TYMED_ISTREAM or TYMED_HGLOBAL; - ); -type - // protection against TRect record method that cause problems with with-statements - TWithSafeRect = record - case Integer of - 0: (Left, Top, Right, Bottom: Integer); - 1: (TopLeft, BottomRight: TPoint); - end; type // streaming support TMagicID = array[0..5] of WideChar; @@ -4151,6 +2953,12 @@ TToggleAnimationData = record TCanvasEx = class(TCanvas); + //These allow us access to protected members in the classes + TVirtualTreeColumnsCracker = class(TVirtualTreeColumns); + TVTHeaderCracker = class(TVTHeader); + TVirtualTreeColumnCracker = class(TVirtualTreeColumn); + + const MagicID: TMagicID = (#$2045, 'V', 'T', WideChar(VTTreeStreamVersion), ' ', #$2046); @@ -4201,19 +3009,6 @@ function TreeFromNode(Node: PVirtualNode): TBaseVirtualTree; //---------------------------------------------------------------------------------------------------------------------- -/// Wrapper function for styles services that handles differences between RAD Studio 10.4 and older versions, -/// as well as the case if these controls are used inside the IDE. -function VTStyleServices(AControl: TControl = nil): TCustomStyleServices; -begin - if Assigned(VTStyleServicesFunc) then - Result := VTStyleServicesFunc(AControl) - else - Result := Vcl.Themes.StyleServices{$if CompilerVersion >= 34}(AControl){$ifend}; -end; - -//---------------------------------------------------------------------------------------------------------------------- - - procedure QuickSort(const TheArray: TNodeArray; L, R: Integer); var @@ -4509,14746 +3304,5801 @@ procedure FinalizeGlobalStructures(); gWatcher := nil; end; +//----------------- TVirtualTreeHintWindow ----------------------------------------------------------------------------- - - -//----------------- TCustomVirtualTreeOptions -------------------------------------------------------------------------- - -constructor TCustomVirtualTreeOptions.Create(AOwner: TBaseVirtualTree); +procedure TVirtualTreeHintWindow.CMTextChanged(var Message: TMessage); begin - FOwner := AOwner; - - FPaintOptions := DefaultPaintOptions; - FAnimationOptions := DefaultAnimationOptions; - FAutoOptions := DefaultAutoOptions; - FSelectionOptions := DefaultSelectionOptions; - FMiscOptions := DefaultMiscOptions; + // swallow this message to prevent the ancestor from resizing the window (we don't use the caption anyway) end; //---------------------------------------------------------------------------------------------------------------------- -procedure TCustomVirtualTreeOptions.SetAnimationOptions(const Value: TVTAnimationOptions); +procedure TVirtualTreeHintWindow.WMEraseBkgnd(var Message: TWMEraseBkgnd); + +// The control is fully painted by own code so don't erase its background as this causes flickering. begin - FAnimationOptions := Value; + Message.Result := 1; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TCustomVirtualTreeOptions.SetAutoOptions(const Value: TVTAutoOptions); - -var - ChangedOptions: TVTAutoOptions; +procedure TVirtualTreeHintWindow.CreateParams(var Params: TCreateParams); begin - if FAutoOptions <> Value then + inherited CreateParams(Params); + + with Params do begin - // Exclusive ORing to get all entries wich are in either set but not in both. - ChangedOptions := FAutoOptions + Value - (FAutoOptions * Value); - FAutoOptions := Value; - with FOwner do - if (toAutoSpanColumns in ChangedOptions) and not (csLoading in ComponentState) and HandleAllocated then - Invalidate; + Style := WS_POPUP; + ExStyle := ExStyle and not WS_EX_CLIENTEDGE; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TCustomVirtualTreeOptions.InternalSetMiscOptions(const Value: TVTMiscOptions); -begin - FMiscOptions := value; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TCustomVirtualTreeOptions.SetMiscOptions(const Value: TVTMiscOptions); - +procedure TVirtualTreeHintWindow.Paint(); var - ToBeSet, - ToBeCleared: TVTMiscOptions; + R: TRect; + Y: Integer; + S: string; + DrawFormat: Cardinal; + HintKind: TVTHintKind; + LClipRect: TRect; + + LColor: TColor; + LDetails: TThemedElementDetails; + LGradientStart: TColor; + LGradientEnd: TColor; begin - if FMiscOptions <> Value then + with FHintData do begin - ToBeSet := Value - FMiscOptions; - ToBeCleared := FMiscOptions - Value; - FMiscOptions := Value; + // Do actual painting only in the very first run. + // If the given node is nil then we have to display a header hint. + if (Node = nil) or (Tree.FHintMode <> hmToolTip) then + begin + Canvas.Font := Screen.HintFont; + Canvas.Font.Height := MulDiv(Canvas.Font.Height, Tree.ScaledPixels(96), Screen.PixelsPerInch); // See issue #992 + Y := 2; + end + else + begin + Tree.GetTextInfo(Node, Column, Canvas.Font, R, S); + if LineBreakStyle = hlbForceMultiLine then + Y := 1 + else + Y := (R.Top - R.Bottom + Self.Height) div 2; + end; - with FOwner do - if not (csLoading in ComponentState) and HandleAllocated then - begin - if toCheckSupport in ToBeSet + ToBeCleared then - Invalidate; - if toEditOnDblClick in ToBeSet then - FMiscOptions := FMiscOptions - [toToggleOnDblClick]; // In order for toEditOnDblClick to take effect, we need to remove toToggleOnDblClick which is handled with priority. See issue #747 + R := Rect(0, 0, Width, Height); - if not (csDesigning in ComponentState) then + HintKind := vhkText; + if Assigned(Node) then + Tree.DoGetHintKind(Node, Column, HintKind); + + if HintKind = vhkOwnerDraw then + begin + Tree.DoDrawHint(Canvas, Node, R, Column); + end + else + with Canvas do + begin + if Tree.VclStyleEnabled then begin - if toAcceptOLEDrop in ToBeCleared then - RevokeDragDrop(Handle); - if toFullRepaintOnResize in ToBeSet + ToBeCleared then - RecreateWnd; - if toAcceptOLEDrop in ToBeSet then - RegisterDragDrop(Handle, DragManager as IDropTarget); - if toVariableNodeHeight in ToBeSet then begin - BeginUpdate(); - try - ReInitNode(nil, True); - finally - EndUpdate(); - end;//try..finally - end;//if toVariableNodeHeight + InflateRect(R, -1, -1); // Fixes missing border when VCL styles are used + LDetails := StyleServices(Tree).GetElementDetails(thHintNormal); + if StyleServices(Tree).GetElementColor(LDetails, ecGradientColor1, LColor) and (LColor <> clNone) then + LGradientStart := LColor + else + LGradientStart := clInfoBk; + if StyleServices(Tree).GetElementColor(LDetails, ecGradientColor2, LColor) and (LColor <> clNone) then + LGradientEnd := LColor + else + LGradientEnd := clInfoBk; + if StyleServices(Tree).GetElementColor(LDetails, ecTextColor, LColor) and (LColor <> clNone) then + Font.Color := LColor + else + Font.Color := Screen.HintFont.Color; + GradientFillCanvas(Canvas, LGradientStart, LGradientEnd, R, gdVertical); + end + else + begin + // Still force tooltip back and text color. + Font.Color := clInfoText; + Pen.Color := clBlack; + Brush.Color := clInfoBk; + if IsWinVistaOrAbove and StyleServices(Tree).Enabled and ((toThemeAware in Tree.TreeOptions.PaintOptions) or + (toUseExplorerTheme in Tree.TreeOptions.PaintOptions)) then + begin + if toUseExplorerTheme in Tree.TreeOptions.PaintOptions then // ToolTip style + StyleServices(Tree).DrawElement(Canvas.Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) + else + begin // Hint style + LClipRect := R; + InflateRect(R, 4, 4); + StyleServices(Tree).DrawElement(Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R, @LClipRect{$IF CompilerVersion >= 34}, FCurrentPPI{$IFEND}); + R := LClipRect; + StyleServices(Tree).DrawEdge(Handle, StyleServices(Tree).GetElementDetails(twWindowRoot), R, [eeRaisedOuter], [efRect]); + end; + end + else + if Tree.VclStyleEnabled then + StyleServices(Tree).DrawElement(Canvas.Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) + else + Rectangle(R); end; + // Determine text position and don't forget the border. + InflateRect(R, -1, -1); + DrawFormat := DT_TOP or DT_NOPREFIX; + SetBkMode(Handle, Winapi.Windows.TRANSPARENT); + R.Top := Y; + R.Left := R.Left + 3; // Make the text more centered + if Assigned(Node) and (LineBreakStyle = hlbForceMultiLine) then + DrawFormat := DrawFormat or DT_WORDBREAK; + Winapi.Windows.DrawTextW(Handle, PWideChar(HintText), Length(HintText), R, DrawFormat); end; end; end; +function TVirtualTreeHintWindow.StyleServices(AControl: TControl): TCustomStyleServices; +begin + Result := VTStyleServices(AControl); +end; + //---------------------------------------------------------------------------------------------------------------------- -procedure TCustomVirtualTreeOptions.SetPaintOptions(const Value: TVTPaintOptions); +function TVirtualTreeHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect; var - ToBeSet, - ToBeCleared: TVTPaintOptions; - Run: PVirtualNode; - HandleWasAllocated: Boolean; + TM: TTextMetric; + R: TRect; begin - if FPaintOptions <> Value then - begin - ToBeSet := Value - FPaintOptions; - ToBeCleared := FPaintOptions - Value; - FPaintOptions := Value; - if (toFixedIndent in ToBeSet) then - begin - // Fixes issue #388 - Include(FPaintOptions, toShowRoot); - Include(ToBeSet, toShowRoot); - end;//if - with FOwner do + try + if AData = nil then + // Defensive approach, it *can* happen that AData is nil. Maybe when several user defined hint classes are used. + Result := Rect(0, 0, 0, 0) + else begin - HandleWasAllocated := HandleAllocated; + // The hint window does not need any bidi mode setting but the caller of this method (TApplication.ActivateHint) + // does some unneccessary actions if the hint window is not left-to-right. + // The text alignment is based on the bidi mode passed in the hint data, hence we can + // simply set the window's mode to left-to-right (it might have been modified by the caller, if the + // tree window is right-to-left aligned). + BidiMode := bdLeftToRight; + + FHintData := PVTHintData(AData)^; - if not (csLoading in ComponentState) and (toShowFilteredNodes in ToBeSet + ToBeCleared) then + with FHintData do begin - if HandleWasAllocated then - BeginUpdate; - InterruptValidation; - Run := GetFirstNoInit; - while Assigned(Run) do + // The draw tree gets its hint size by the application (but only if not a header hint is about to show). + // If the user will be drawing the hint, it gets its hint size by the application + // (but only if not a header hint is about to show). + // This size has already been determined in CMHintShow. + if Assigned(Node) and (not IsRectEmpty(HintRect)) then + Result := HintRect + else begin - if (vsFiltered in Run.States) then + if Column <= NoColumn then begin - if FullyVisible[Run] then - begin - if toShowFilteredNodes in ToBeSet then - Inc(FVisibleCount) - else - Dec(FVisibleCount); - end; - if toShowFilteredNodes in ToBeSet then - AdjustTotalHeight(Run, Run.NodeHeight, True) - else - AdjustTotalHeight(Run, -Run.NodeHeight, True); + BidiMode := Tree.BidiMode; + Alignment := Tree.Alignment; + end + else + begin + BidiMode := Tree.Header.Columns[Column].BidiMode; + Alignment := Tree.Header.Columns[Column].Alignment; end; - Run := GetNextNoInit(Run); - end; - if HandleWasAllocated then - EndUpdate; - end; - if HandleAllocated then - begin - if IsWinVistaOrAbove and ((tsUseThemes in FStates) or - ((toThemeAware in ToBeSet) and StyleServices.Enabled)) and - (toUseExplorerTheme in (ToBeSet + ToBeCleared)) and not VclStyleEnabled then - begin - if (toUseExplorerTheme in ToBeSet) then + if BidiMode <> bdLeftToRight then + ChangeBidiModeAlignment(Alignment); + + if (Node = nil) or (Tree.FHintMode <> hmToolTip) then begin - SetWindowTheme('explorer'); - DoStateChange([tsUseExplorerTheme]); + Canvas.Font := Screen.HintFont; + Canvas.Font.Height := MulDiv(Canvas.Font.Height, Tree.ScaledPixels(96), Screen.PixelsPerInch); // See issue #992 end else - if toUseExplorerTheme in ToBeCleared then - begin - SetWindowTheme(''); - DoStateChange([], [tsUseExplorerTheme]); - end; - end; - - if not (csLoading in ComponentState) then - begin - if ((toThemeAware in ToBeSet + ToBeCleared) or (toUseExplorerTheme in ToBeSet + ToBeCleared) or VclStyleEnabled) then begin - if ((toThemeAware in ToBeSet) and StyleServices.Enabled) then - DoStateChange([tsUseThemes]) - else - if (toThemeAware in ToBeCleared) then - DoStateChange([], [tsUseThemes]); - - PrepareBitmaps(True, False); - RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_VALIDATE or RDW_FRAME); + Canvas.Font := Tree.Font; + if Tree is TCustomVirtualStringTree then + with TCustomVirtualStringTree(Tree) do + DoPaintText(Node, Self.Canvas, Column, ttNormal); end; - if toChildrenAbove in ToBeSet + ToBeCleared then + GetTextMetrics(Canvas.Handle, TM); + FTextHeight := TM.tmHeight; + + if Length(HintText) = 0 then + Result := Rect(0, 0, 0, 0) + else begin - InvalidateCache; - if FUpdateCount = 0 then + if Assigned(Node) and (Tree.FHintMode = hmToolTip) then begin - ValidateCache; - Invalidate; - end; - end; - - Invalidate; - end; - end; - end; - end; -end; + // Determine actual line break style depending on what was returned by the methods and what's in the node. + if LineBreakStyle = hlbDefault then + if vsMultiline in Node.States then + LineBreakStyle := hlbForceMultiLine + else + LineBreakStyle := hlbForceSingleLine; -//---------------------------------------------------------------------------------------------------------------------- + // Hint for a node. + if LineBreakStyle = hlbForceMultiLine then + begin + // Multiline tooltips use the columns width but extend the bottom border to fit the whole caption. + Result := Tree.GetDisplayRect(Node, Column, True, False); + R := Result; -procedure TCustomVirtualTreeOptions.SetSelectionOptions(const Value: TVTSelectionOptions); + // On Windows NT/2K/XP the behavior of the tooltip is slightly different to that on Windows 9x/Me. + // We don't have Unicode word wrap on the latter so the tooltip gets as wide as the largest line + // in the caption (limited by carriage return), which results in unoptimal overlay of the tooltip. + Winapi.Windows.DrawTextW(Canvas.Handle, PWideChar(HintText), Length(HintText), R, DT_CALCRECT or DT_WORDBREAK); + if BidiMode = bdLeftToRight then + Result.Right := R.Right + Tree.FTextMargin + else + Result.Left := R.Left - Tree.FTextMargin + 1; + Result.Bottom := R.Bottom; -var - ToBeSet, - ToBeCleared: TVTSelectionOptions; + Inc(Result.Right); -begin - if FSelectionOptions <> Value then - begin - ToBeSet := Value - FSelectionOptions; - ToBeCleared := FSelectionOptions - Value; - FSelectionOptions := Value; + // If the node height and the column width are both already large enough to cover the entire text, + // then we don't need the hint, though. + // However if the text is partially scrolled out of the client area then a hint is useful as well. + if (Tree.Header.Columns.Count > 0) and ((Integer(Tree.NodeHeight[Node]) + 2) >= (Result.Bottom - Result.Top)) and + ((Tree.Header.Columns[Column].Width + 2) >= (Result.Right - Result.Left)) and not + ((Result.Left < 0) or (Result.Right > Tree.ClientWidth + 3) or + (Result.Top < 0) or (Result.Bottom > Tree.ClientHeight + 3)) then + begin + Result := Rect(0, 0, 0, 0); + Exit; + end; + end + else + begin + Result := Tree.FLastHintRect; // = Tree.GetDisplayRect(Node, Column, True, True, True); see TBaseVirtualTree.CMHintShow - with FOwner do - begin - if (toMultiSelect in (ToBeCleared + ToBeSet)) or - ([toLevelSelectConstraint, toSiblingSelectConstraint] * ToBeSet <> []) then - ClearSelection; + { Fixes issue #623 - if (toExtendedFocus in ToBeCleared) and (FFocusedColumn > 0) and HandleAllocated then - begin - FFocusedColumn := FHeader.MainColumn; - Invalidate; - end; + Measure the rectangle to draw the text. The width of the result + is always adjusted according to the hint text because it may + be a custom hint coming in which can be larger or smaller than + the node text. + Earlier logic was using the current width of the node that was + either cutting off the hint text or producing undesired space + on the right. + } + R := Rect(0, 0, MaxWidth, FTextHeight); + Winapi.Windows.DrawTextW(Canvas.Handle, PWideChar(HintText), Length(HintText), R, DT_CALCRECT or DT_TOP or DT_NOPREFIX or DT_WORDBREAK); + if R.Right <> result.right - result.left then + begin + result.Right := result.Left + r.Right; - if not (toExtendedFocus in FSelectionOptions) then - FFocusedColumn := FHeader.MainColumn; - end; - end; -end; + //Space on right--taken from the code in the hmHint branch below. + if Assigned(Tree) then + Inc(Result.Right, Tree.FTextMargin + Tree.FMargin + Tree.ScaledPixels(4)); + end; + // Fix ends. -function TCustomVirtualTreeOptions.StyleServices(AControl: TControl): TCustomStyleServices; -begin - Result := VTStyleServices(FOwner); -end; + if toShowHorzGridLines in Tree.TreeOptions.PaintOptions then + Dec(Result.Bottom); + end; -//---------------------------------------------------------------------------------------------------------------------- + // Include a one pixel border. + InflateRect(Result, 1, 1); -procedure TCustomVirtualTreeOptions.AssignTo(Dest: TPersistent); + // Make the coordinates relative. They will again be offset by the caller code. + OffsetRect(Result, -Result.Left - 1, -Result.Top - 1); + end + else + begin + // Hint for a header or non-tooltip hint. -begin - if Dest is TCustomVirtualTreeOptions then - begin - with Dest as TCustomVirtualTreeOptions do - begin - PaintOptions := Self.PaintOptions; - AnimationOptions := Self.AnimationOptions; - AutoOptions := Self.AutoOptions; - SelectionOptions := Self.SelectionOptions; - MiscOptions := Self.MiscOptions; + // Start with the base size of the hint in client coordinates. + Result := Rect(0, 0, MaxWidth, FTextHeight); + // Calculate the true size of the text rectangle. + Winapi.Windows.DrawTextW(Canvas.Handle, PWideChar(HintText), Length(HintText), Result, DT_CALCRECT or DT_TOP or DT_NOPREFIX or DT_WORDBREAK); + // The height of the text plus 2 pixels vertical margin plus the border determine the hint window height. + // Minus 4 because THintWindow.ActivateHint adds 4 to Rect.Bottom anyway. Note that it is not scaled because the RTL itself does not do any scaling either. + Inc(Result.Bottom, Tree.ScaledPixels(6) - 4); + // The text is centered horizontally with usual text margin for left and right borders (plus border). + if not Assigned(Tree) then + Exit; // Workaround, because we have seen several exceptions here caught by Eurekalog. Submitted as issue #114 to http://code.google.com/p/virtual-treeview/ + { Issue #623 Fix for strange space on the right. + Original logic was adding FTextHeight. Changed it to add FMargin instead and + it looks OK even if the hint font is larger. + } + Inc(Result.Right, Tree.FTextMargin + + Tree.FMargin + Tree.ScaledPixels(4)); //Issue #623 space on right + //+ FTextHeight); // Old code: We are extending the width here, but the text height scales with the text width and has a similar value as AveCharWdith * 2. + end; + end; + end; + end; end; - end - else - inherited; + except + Application.HandleException(Self); + end; end; //---------------------------------------------------------------------------------------------------------------------- -// OLE drag and drop support classes -// This is quite heavy stuff (compared with the VCL implementation) but is much better suited to fit the needs -// of DD'ing various kinds of virtual data and works also between applications. - -//----------------- TEnumFormatEtc ------------------------------------------------------------------------------------- - -constructor TEnumFormatEtc.Create(Tree: TBaseVirtualTree; const AFormatEtcArray: TFormatEtcArray); +function TVirtualTreeHintWindow.IsHintMsg(var Msg: TMsg): Boolean; -var - I: Integer; +// The VCL is a bit too generous when telling that an existing hint can be cancelled. Need to specify further here. begin - inherited Create; - - FTree := Tree; - // Make a local copy of the format data. - SetLength(FFormatEtcArray, Length(AFormatEtcArray)); - for I := 0 to High(AFormatEtcArray) do - FFormatEtcArray[I] := AFormatEtcArray[I]; + Result := inherited IsHintMsg(Msg) and HandleAllocated and IsWindowVisible(Handle); + // Avoid that mouse moves over the non-client area or cursor key presses cancel the current hint. + if Result and ((Msg.Message = WM_NCMOUSEMOVE) or ((Msg.Message >= WM_KEYFIRST) and (Msg.Message <= WM_KEYLAST) and (Msg.wparam in [VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT]))) then + Result := False; end; -//---------------------------------------------------------------------------------------------------------------------- -function TEnumFormatEtc.Clone(out Enum: IEnumFormatEtc): HResult; +//----------------- TVTVirtualNodeEnumerator --------------------------------------------------------------------------- -var - AClone: TEnumFormatEtc; +function TVTVirtualNodeEnumerator.GetCurrent: PVirtualNode; begin - Result := S_OK; - try - AClone := TEnumFormatEtc.Create(nil, FFormatEtcArray); - AClone.FCurrentIndex := FCurrentIndex; - Enum := AClone as IEnumFormatEtc; - except - Result := E_FAIL; - end; + Result := FNode; end; //---------------------------------------------------------------------------------------------------------------------- -function TEnumFormatEtc.Next(celt: Integer; out elt; pceltFetched: PLongint): HResult; - -var - CopyCount: Integer; +function TVTVirtualNodeEnumerator.MoveNext: Boolean; begin - Result := S_FALSE; - CopyCount := Length(FFormatEtcArray) - FCurrentIndex; - if celt < CopyCount then - CopyCount := celt; - if CopyCount > 0 then + Result := FCanMoveNext; + if Result then begin - Move(FFormatEtcArray[FCurrentIndex], elt, CopyCount * SizeOf(TFormatEtc)); - Inc(FCurrentIndex, CopyCount); - Result := S_OK; + FNode := FEnumeration.GetNext(FNode); + Result := FNode <> nil; + FCanMoveNext := Result; end; - if Assigned(pceltFetched) then - pceltFetched^ := CopyCount; end; -//---------------------------------------------------------------------------------------------------------------------- +//----------------- TVTVirtualNodeEnumeration -------------------------------------------------------------------------- -function TEnumFormatEtc.Reset: HResult; +function TVTVirtualNodeEnumeration.GetEnumerator: TVTVirtualNodeEnumerator; begin - FCurrentIndex := 0; - Result := S_OK; + Result.FNode := nil; + Result.FCanMoveNext := True; + Result.FEnumeration := @Self; end; //---------------------------------------------------------------------------------------------------------------------- -function TEnumFormatEtc.Skip(celt: Integer): HResult; - +function TVTVirtualNodeEnumeration.GetNext(Node: PVirtualNode): PVirtualNode; begin - if FCurrentIndex + celt < High(FFormatEtcArray) then - begin - Inc(FCurrentIndex, celt); - Result := S_Ok; - end - else - Result := S_FALSE; -end; - -//----------------- TVTDataObject -------------------------------------------------------------------------------------- - -constructor TVTDataObject.Create(AOwner: TBaseVirtualTree; ForClipboard: Boolean); + case FMode of + vneAll: + if Node = nil then + Result := FTree.GetFirst(FConsiderChildrenAbove) + else + Result := FTree.GetNext(Node, FConsiderChildrenAbove); -begin - inherited Create; + vneChecked: + if Node = nil then + Result := FTree.GetFirstChecked(FState, FConsiderChildrenAbove) + else + Result := FTree.GetNextChecked(Node, FState, FConsiderChildrenAbove); - FOwner := AOwner; - FForClipboard := ForClipboard; - FOwner.GetNativeClipboardFormats(FFormatEtcArray); -end; + vneChild: + if Node = nil then + Result := FTree.GetFirstChild(FNode) + else + Result := FTree.GetNextSibling(Node); -//---------------------------------------------------------------------------------------------------------------------- + vneCutCopy: + if Node = nil then + Result := FTree.GetFirstCutCopy(FConsiderChildrenAbove) + else + Result := FTree.GetNextCutCopy(Node, FConsiderChildrenAbove); -destructor TVTDataObject.Destroy; + vneInitialized: + if Node = nil then + Result := FTree.GetFirstInitialized(FConsiderChildrenAbove) + else + Result := FTree.GetNextInitialized(Node, FConsiderChildrenAbove); -var - I: Integer; - StgMedium: PStgMedium; + vneLeaf: + if Node = nil then + Result := FTree.GetFirstLeaf + else + Result := FTree.GetNextLeaf(Node); -begin - // Cancel a pending clipboard operation if this data object was created for the clipboard and - // is freed because something else is placed there. - if FForClipboard and not (tsClipboardFlushing in FOwner.TreeStates) then - FOwner.CancelCutOrCopy; + vneLevel: + if Node = nil then + Result := FTree.GetFirstLevel(FNodeLevel) + else + Result := FTree.GetNextLevel(Node, FNodeLevel); - // Release any internal clipboard formats - for I := 0 to High(FormatEtcArray) do - begin - StgMedium := FindInternalStgMedium(FormatEtcArray[I].cfFormat); - if Assigned(StgMedium) then - ReleaseStgMedium(StgMedium^); - end; + vneNoInit: + if Node = nil then + Result := FTree.GetFirstNoInit(FConsiderChildrenAbove) + else + Result := FTree.GetNextNoInit(Node, FConsiderChildrenAbove); - FormatEtcArray := nil; - inherited; -end; + vneSelected: + if Node = nil then + Result := FTree.GetFirstSelected(FConsiderChildrenAbove) + else + Result := FTree.GetNextSelected(Node, FConsiderChildrenAbove); -//---------------------------------------------------------------------------------------------------------------------- + vneVisible: + begin + if Node = nil then + begin + Result := FTree.GetFirstVisible(FNode, FConsiderChildrenAbove, FIncludeFiltered); + if FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result] then + Exit; + end; + repeat + Result := FTree.GetNextVisible(Node{, FConsiderChildrenAbove}); + until not Assigned(Result) or FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result]; + end; -function TVTDataObject.CanonicalIUnknown(const TestUnknown: IUnknown): IUnknown; + vneVisibleChild: + if Node = nil then + Result := FTree.GetFirstVisibleChild(FNode, FIncludeFiltered) + else + Result := FTree.GetNextVisibleSibling(Node, FIncludeFiltered); -// Uses COM object identity: An explicit call to the IUnknown::QueryInterface method, requesting the IUnknown -// interface, will always return the same pointer. + vneVisibleNoInitChild: + if Node = nil then + Result := FTree.GetFirstVisibleChildNoInit(FNode, FIncludeFiltered) + else + Result := FTree.GetNextVisibleSiblingNoInit(Node, FIncludeFiltered); -begin - if Assigned(TestUnknown) then - begin - if TestUnknown.QueryInterface(IUnknown, Result) = 0 then - Result._Release // Don't actually need it just need the pointer value - else - Result := TestUnknown; - end + vneVisibleNoInit: + begin + if Node = nil then + begin + Result := FTree.GetFirstVisibleNoInit(FNode, FConsiderChildrenAbove, FIncludeFiltered); + if FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result] then + Exit; + end; + repeat + Result := FTree.GetNextVisibleNoInit(Node, FConsiderChildrenAbove); + until not Assigned(Result) or FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result]; + end; else - Result := TestUnknown; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVTDataObject.EqualFormatEtc(FormatEtc1, FormatEtc2: TFormatEtc): Boolean; - -begin - Result := (FormatEtc1.cfFormat = FormatEtc2.cfFormat) and (FormatEtc1.ptd = FormatEtc2.ptd) and - (FormatEtc1.dwAspect = FormatEtc2.dwAspect) and (FormatEtc1.lindex = FormatEtc2.lindex) and - (FormatEtc1.tymed and FormatEtc2.tymed <> 0); + Result := nil; + end; end; -//---------------------------------------------------------------------------------------------------------------------- - -function TVTDataObject.FindFormatEtc(TestFormatEtc: TFormatEtc; const FormatEtcArray: TFormatEtcArray): integer; -var - I: integer; -begin - Result := -1; - for I := 0 to High(FormatEtcArray) do - begin - if EqualFormatEtc(TestFormatEtc, FormatEtcArray[I]) then - begin - Result := I; - Break; - end; - end; -end; -//---------------------------------------------------------------------------------------------------------------------- +//----------------- TClipboardFormats ---------------------------------------------------------------------------------- -function TVTDataObject.FindInternalStgMedium(Format: TClipFormat): PStgMedium; +constructor TClipboardFormats.Create(AOwner: TBaseVirtualTree); -var - I: integer; begin - Result := nil; - for I := 0 to High(InternalStgMediumArray) do - begin - if Format = InternalStgMediumArray[I].Format then - begin - Result := @InternalStgMediumArray[I].Medium; - Break; - end; - end; + FOwner := AOwner; + Sorted := True; + Duplicates := dupIgnore; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTDataObject.HGlobalClone(HGlobal: THandle): THandle; +function TClipboardFormats.Add(const S: string): Integer; -// Returns a global memory block that is a copy of the passed memory block. +// Restrict additions to the clipbard formats to only those which are registered with the owner tree or one of its +// ancestors. var - Size: Cardinal; - Data, - NewData: PByte; + Format: Word; + RegisteredClass: TVirtualTreeClass; begin - Size := GlobalSize(HGlobal); - Result := GlobalAlloc(GPTR, Size); - Data := GlobalLock(hGlobal); - try - NewData := GlobalLock(Result); - try - Move(Data^, NewData^, Size); - finally - GlobalUnLock(Result); - end; - finally - GlobalUnLock(hGlobal); - end; + RegisteredClass := TClipboardFormatList.FindFormat(S, Format); + if Assigned(RegisteredClass) and FOwner.ClassType.InheritsFrom(RegisteredClass) then + Result := inherited Add(S) + else + Result := -1; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTDataObject.RenderInternalOLEData(const FormatEtcIn: TFormatEtc; var Medium: TStgMedium; - var OLEResult: HResult): Boolean; +procedure TClipboardFormats.Insert(Index: Integer; const S: string); -// Tries to render one of the formats which have been stored via the SetData method. -// Since this data is already there it is just copied or its reference count is increased (depending on storage medium). +// Restrict additions to the clipbard formats to only those which are registered with the owner tree or one of its +// ancestors. var - InternalMedium: PStgMedium; + Format: Word; + RegisteredClass: TVirtualTreeClass; begin - Result := True; - InternalMedium := FindInternalStgMedium(FormatEtcIn.cfFormat); - if Assigned(InternalMedium) then - OLEResult := StgMediumIncRef(InternalMedium^, Medium, False, Self as IDataObject) - else - Result := False; + RegisteredClass := TClipboardFormatList.FindFormat(S, Format); + if Assigned(RegisteredClass) and FOwner.ClassType.InheritsFrom(RegisteredClass) then + inherited Insert(Index, S); end; -//---------------------------------------------------------------------------------------------------------------------- - -function TVTDataObject.StgMediumIncRef(const InStgMedium: TStgMedium; var OutStgMedium: TStgMedium; - CopyInMedium: Boolean; const DataObject: IDataObject): HRESULT; - -// InStgMedium is the data that is requested, OutStgMedium is the data that we are to return either a copy of or -// increase the IDataObject's reference and send ourselves back as the data (unkForRelease). The InStgMedium is usually -// the result of a call to find a particular FormatEtc that has been stored locally through a call to SetData. -// If CopyInMedium is not true we already have a local copy of the data when the SetData function was called (during -// that call the CopyInMedium must be true). Then as the caller asks for the data through GetData we do not have to make -// copy of the data for the caller only to have them destroy it then need us to copy it again if necessary. -// This way we increase the reference count to ourselves and pass the STGMEDIUM structure initially stored in SetData. -// This way when the caller frees the structure it sees the unkForRelease is not nil and calls Release on the object -// instead of destroying the actual data. +//----------------- TBaseVirtualTree ----------------------------------------------------------------------------------- -var - Len: Integer; +constructor TBaseVirtualTree.Create(AOwner: TComponent); begin - Result := S_OK; + InitializeGlobalStructures(); - // Simply copy all fields to start with. - OutStgMedium := InStgMedium; - // The data handled here always results from a call of SetData we got. This ensures only one storage format - // is indicated and hence the case statement below is safe (IDataObject.GetData can optionally use several - // storage formats). - case InStgMedium.tymed of - TYMED_HGLOBAL: - begin - if CopyInMedium then - begin - // Generate a unique copy of the data passed - OutStgMedium.hGlobal := HGlobalClone(InStgMedium.hGlobal); - if OutStgMedium.hGlobal = 0 then - Result := E_OUTOFMEMORY; - end - else - // Don't generate a copy just use ourselves and the copy previously saved. - OutStgMedium.unkForRelease := Pointer(DataObject); // Does not increase RefCount. - end; - TYMED_FILE: - begin - Len := lstrLenW(InStgMedium.lpszFileName) + 1; // Don't forget the terminating null character. - OutStgMedium.lpszFileName := CoTaskMemAlloc(2 * Len); - Move(InStgMedium.lpszFileName^, OutStgMedium.lpszFileName^, 2 * Len); - end; - TYMED_ISTREAM: - IUnknown(OutStgMedium.stm)._AddRef; - TYMED_ISTORAGE: - IUnknown(OutStgMedium.stg)._AddRef; - TYMED_GDI: - if not CopyInMedium then - // Don't generate a copy just use ourselves and the previously saved data. - OutStgMedium.unkForRelease := Pointer(DataObject) // Does not increase RefCount. - else - Result := DV_E_TYMED; // Don't know how to copy GDI objects right now. - TYMED_MFPICT: - if not CopyInMedium then - // Don't generate a copy just use ourselves and the previously saved data. - OutStgMedium.unkForRelease := Pointer(DataObject) // Does not increase RefCount. - else - Result := DV_E_TYMED; // Don't know how to copy MetaFile objects right now. - TYMED_ENHMF: - if not CopyInMedium then - // Don't generate a copy just use ourselves and the previously saved data. - OutStgMedium.unkForRelease := Pointer(DataObject) // Does not increase RefCount. - else - Result := DV_E_TYMED; // Don't know how to copy enhanced metafiles objects right now. - else - Result := DV_E_TYMED; - end; + inherited; - if (Result = S_OK) and Assigned(OutStgMedium.unkForRelease) then - IUnknown(OutStgMedium.unkForRelease)._AddRef; -end; + ControlStyle := ControlStyle - [csSetCaption] + [csCaptureMouse, csOpaque, csReplicatable, csDisplayDragImage, + csReflector]; + FTotalInternalDataSize := 0; + FNodeDataSize := -1; + Width := 200; + Height := 100; + TabStop := True; + ParentColor := False; + FDefaultNodeHeight := 18; + FDragOperations := [doCopy, doMove]; + FHotCursor := crDefault; + FScrollBarOptions := TScrollBarOptions.Create(Self); + FFocusedColumn := NoColumn; + FDragImageKind := diComplete; + FLastSelectionLevel := -1; + FSelectionBlendFactor := 128; -//---------------------------------------------------------------------------------------------------------------------- + FIndent := 18; + + FPlusBM := TBitmap.Create; + FHotPlusBM := TBitmap.Create; + FMinusBM := TBitmap.Create; + FHotMinusBM := TBitmap.Create; + FSelectedHotPlusBM := TBitmap.Create; + FSelectedHotMinusBM := TBitmap.Create; -function TVTDataObject.DAdvise(const FormatEtc: TFormatEtc; advf: Integer; const advSink: IAdviseSink; - out dwConnection: Integer): HResult; + FBorderStyle := bsSingle; + FButtonStyle := bsRectangle; + FButtonFillMode := fmTreeColor; -// Advise sink management is greatly simplified by the IDataAdviseHolder interface. -// We use this interface and forward all concerning calls to it. + FHeader := GetHeaderClass.Create(Self); -begin - Result := S_OK; - if FAdviseHolder = nil then - Result := CreateDataAdviseHolder(FAdviseHolder); - if Result = S_OK then - Result := FAdviseHolder.Advise(Self as IDataObject, FormatEtc, advf, advSink, dwConnection); -end; + // we have an own double buffer handling + inherited DoubleBuffered := False; -//---------------------------------------------------------------------------------------------------------------------- + FCheckImageKind := ckSystemDefault; -function TVTDataObject.DUnadvise(dwConnection: Integer): HResult; + FImageChangeLink := TChangeLink.Create; + FImageChangeLink.OnChange := ImageListChange; + FStateChangeLink := TChangeLink.Create; + FStateChangeLink.OnChange := ImageListChange; + FCustomCheckChangeLink := TChangeLink.Create; + FCustomCheckChangeLink.OnChange := ImageListChange; -begin - if FAdviseHolder = nil then - Result := E_NOTIMPL - else - Result := FAdviseHolder.Unadvise(dwConnection); -end; + FAutoExpandDelay := 1000; + FAutoScrollDelay := 1000; + FAutoScrollInterval := 1; -//---------------------------------------------------------------------------------------------------------------------- + FBackground := TPicture.Create; + // Similar to the Transparent property of TImage, + // this flag is Off by default. + FBackGroundImageTransparent := False; -function TVTDataObject.EnumDAdvise(out enumAdvise: IEnumStatData): HResult; + FDefaultPasteMode := amAddChildLast; + FMargin := 4; + FTextMargin := 4; + FImagesMargin := 2; + FLastDragEffect := DROPEFFECT_NONE; + FDragType := dtOLE; + FDragHeight := 350; + FDragWidth := 200; -begin - if FAdviseHolder = nil then - Result := OLE_E_ADVISENOTSUPPORTED - else - Result := FAdviseHolder.EnumAdvise(enumAdvise); -end; + FColors := TVTColors.Create(Self); + FEditDelay := 1000; -//---------------------------------------------------------------------------------------------------------------------- + FDragImage := TVTDragImage.Create(Self); + with FDragImage do + begin + Fade := True; + PreBlendBias := 0; + Transparency := 200; + end; -function TVTDataObject.EnumFormatEtc(Direction: Integer; out EnumFormatEtc: IEnumFormatEtc): HResult; + FAnimationDuration := 200; + FSearchTimeout := 1000; + FSearchStart := ssFocusedNode; + FNodeAlignment := naProportional; + FLineStyle := lsDotted; + FIncrementalSearch := isNone; + FClipboardFormats := TClipboardFormats.Create(Self); + FOptions := GetOptionsClass.Create(Self); -var - NewList: TEnumFormatEtc; + Touch.InteractiveGestures := [igPan, igPressAndTap]; + Touch.InteractiveGestureOptions := [igoPanInertia, + igoPanSingleFingerHorizontal, igoPanSingleFingerVertical, + igoPanGutter, igoParentPassthrough]; -begin - Result := E_FAIL; - if Direction = DATADIR_GET then - begin - NewList := TEnumFormatEtc.Create(FOwner, FormatEtcArray); - EnumFormatEtc := NewList as IEnumFormatEtc; - Result := S_OK; - end - else - EnumFormatEtc := nil; - if EnumFormatEtc = nil then - Result := OLE_S_USEREG; + if not (csDesigning in ComponentState) then //Don't create worker thread in IDE, there is no use for it + TWorkerThread.AddThreadReference(); end; //---------------------------------------------------------------------------------------------------------------------- -function TVTDataObject.GetCanonicalFormatEtc(const FormatEtc: TFormatEtc; out FormatEtcOut: TFormatEtc): HResult; - +destructor TBaseVirtualTree.Destroy; +var + WasValidating: Boolean; begin - Result := DATA_S_SAMEFORMATETC; -end; + // Disconnect all remote MSAA connections + if Assigned(FAccessibleItem) then begin + CoDisconnectObject(FAccessibleItem, 0); + FAccessibleItem := nil; + end; + if Assigned(fAccessible) then begin + CoDisconnectObject(fAccessible, 0); + fAccessible := nil; + end; -//---------------------------------------------------------------------------------------------------------------------- + WasValidating := (tsValidating in FStates); + InterruptValidation(True); + if WasValidating then + begin + // Make sure we dequeue the two synchronized calls from ChangeTreeStatesAsync(), fixes mem leak and AV reported in issue #1001, but is more a workaround. + CheckSynchronize(); + CheckSynchronize(); + end;// if + FOptions.InternalSetMiscOptions(FOptions.MiscOptions - [toReadOnly]); //SetMiscOptions has side effects + // Make sure there is no reference remaining to the releasing tree. + TWorkerThread.ReleaseThreadReference(); + StopWheelPanning; + CancelEditNode; -function TVTDataObject.GetData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium): HResult; + // Just in case it didn't happen already release the edit link. + FEditLink := nil; + FClipboardFormats.Free; + // Clear will also free the drag manager if it is still alive. + Clear; + FDragImage.Free; + FColors.Free; + FBackground.Free; -// Data is requested by clipboard or drop target. This method dispatchs the call -// depending on the data being requested. + if CheckImageKind = ckSystemDefault then + FCheckImages.Free; + FScrollBarOptions.Free; -var - I: Integer; - Data: PVTReference; + // The window handle must be destroyed before the header is freed because it is needed in WM_NCDESTROY. + if HandleAllocated then + DestroyWindowHandle; -begin - // The tree reference format is always supported and returned from here. - if FormatEtcIn.cfFormat = CF_VTREFERENCE then - begin - // Note: this format is not used while flushing the clipboard to avoid a dangling reference - // when the owner tree is destroyed before the clipboard data is replaced with something else. - if tsClipboardFlushing in FOwner.TreeStates then - Result := E_FAIL - else - begin - Medium.hGlobal := GlobalAlloc(GHND or GMEM_SHARE, SizeOf(TVTReference)); - Data := GlobalLock(Medium.hGlobal); - Data.Process := GetCurrentProcessID; - Data.Tree := FOwner; - GlobalUnlock(Medium.hGlobal); - Medium.tymed := TYMED_HGLOBAL; - Medium.unkForRelease := nil; - Result := S_OK; - end; - end - else - begin - try - // See if we accept this type and if not get the correct return value. - Result := QueryGetData(FormatEtcIn); - if Result = S_OK then - begin - for I := 0 to High(FormatEtcArray) do - begin - if EqualFormatEtc(FormatEtcIn, FormatEtcArray[I]) then - begin - if not RenderInternalOLEData(FormatEtcIn, Medium, Result) then - Result := FOwner.RenderOLEData(FormatEtcIn, Medium, FForClipboard); - Break; - end; - end; - end; - except - ZeroMemory (@Medium, SizeOf(Medium)); - Result := E_FAIL; - end; - end; -end; + // Release FDottedBrush in case WM_NCDESTROY hasn't been triggered. + if FDottedBrush <> 0 then + DeleteObject(FDottedBrush); + FDottedBrush := 0; -//---------------------------------------------------------------------------------------------------------------------- + FHeader.Free; + FHeader := nil; // Do not use FreeAndNil() before checking issue #497 + FreeAndNil(FOptions); // WM_NCDESTROY accesses FOptions -function TVTDataObject.GetDataHere(const FormatEtc: TFormatEtc; out Medium: TStgMedium): HResult; + FreeMem(FRoot); -begin - Result := E_NOTIMPL; + FPlusBM.Free; + FHotPlusBM.Free; + FMinusBM.Free; + FHotMinusBM.Free; + FSelectedHotPlusBM.Free; + FSelectedHotMinusBM.Free; + + // Fixes issue #1002 + Images := nil; + StateImages := nil; + CustomCheckImages := nil; + + FImageChangeLink.Free; + FStateChangeLink.Free; + FCustomCheckChangeLink.Free; + + inherited; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTDataObject.QueryGetData(const FormatEtc: TFormatEtc): HResult; +procedure TBaseVirtualTree.AdjustTotalCount(Node: PVirtualNode; Value: Integer; Relative: Boolean = False); + +// Sets a node's total count to the given value and recursively adjusts the parent's total count +// (actually, the adjustment is done iteratively to avoid function call overheads). var - I: Integer; + Difference: Integer; + Run: PVirtualNode; begin - Result := DV_E_CLIPFORMAT; - for I := 0 to High(FFormatEtcArray) do + if Relative then + Difference := Value + else + Difference := Value - Integer(Node.TotalCount); + if Difference <> 0 then begin - if FormatEtc.cfFormat = FFormatEtcArray[I].cfFormat then + Run := Node; + // Root node has as parent the tree view. + while Assigned(Run) and (Run <> Pointer(Self)) do begin - if (FormatEtc.tymed and FFormatEtcArray[I].tymed) <> 0 then - begin - if FormatEtc.dwAspect = FFormatEtcArray[I].dwAspect then - begin - if FormatEtc.lindex = FFormatEtcArray[I].lindex then - begin - Result := S_OK; - Break; - end - else - Result := DV_E_LINDEX; - end - else - Result := DV_E_DVASPECT; - end - else - Result := DV_E_TYMED; + Inc(Integer(Run.TotalCount), Difference); + Run := Run.Parent; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTDataObject.SetData(const FormatEtc: TFormatEtc; var Medium: TStgMedium; DoRelease: BOOL): HResult; +procedure TBaseVirtualTree.AdjustTotalHeight(Node: PVirtualNode; Value: Integer; Relative: Boolean = False); -// Allows dynamic adding to the IDataObject during its existance. Most noteably it is used to implement -// IDropSourceHelper and allows to set a special format for optimized moves during a shell transfer. +// Sets a node's total height to the given value and recursively adjusts the parent's total height. var - Index: Integer; - LocalStgMedium: PStgMedium; + Difference: Integer; + Run: PVirtualNode; begin - // See if we already have a format of that type available. - Index := FindFormatEtc(FormatEtc, FormatEtcArray); - if Index > - 1 then - begin - // Just use the TFormatEct in the array after releasing the data. - LocalStgMedium := FindInternalStgMedium(FormatEtcArray[Index].cfFormat); - if Assigned(LocalStgMedium) then - begin - ReleaseStgMedium(LocalStgMedium^); - ZeroMemory(LocalStgMedium, SizeOf(LocalStgMedium^)); - end; - end + if Relative then + Difference := Value else + Difference := Value - Integer(Node.TotalHeight); + if Difference <> 0 then begin - // It is a new format so create a new TFormatCollectionItem, copy the - // FormatEtc parameter into the new object and and put it in the list. - SetLength(FFormatEtcArray, Length(FormatEtcArray) + 1); - FormatEtcArray[High(FormatEtcArray)] := FormatEtc; + Run := Node; + repeat + Inc(Integer(Run.TotalHeight), Difference); + // If the node is not visible or the parent node is not expanded or we are already at the top + // then nothing more remains to do. + if not (vsVisible in Run.States) or (Run = FRoot) or + (Run.Parent = nil) or not (vsExpanded in Run.Parent.States) then + Break; - // Create a new InternalStgMedium and initialize it and associate it with the format. - SetLength(FInternalStgMediumArray, Length(InternalStgMediumArray) + 1); - InternalStgMediumArray[High(InternalStgMediumArray)].Format := FormatEtc.cfFormat; - LocalStgMedium := @InternalStgMediumArray[High(InternalStgMediumArray)].Medium; - ZeroMemory(LocalStgMedium, SizeOf(LocalStgMedium^)); + Run := Run.Parent; + until False; end; - if DoRelease then - begin - // We are simply being given the data and we take control of it. - LocalStgMedium^ := Medium; - Result := S_OK; - end - else - begin - // We need to reference count or copy the data and keep our own references to it. - Result := StgMediumIncRef(Medium, LocalStgMedium^, True, Self as IDataObject); - - // Can get a circular reference if the client calls GetData then calls SetData with the same StgMedium. - // Because the unkForRelease for the IDataObject can be marshalled it is necessary to get pointers that - // can be correctly compared. See the IDragSourceHelper article by Raymond Chen at MSDN. - if Assigned(LocalStgMedium.unkForRelease) then - begin - if CanonicalIUnknown(Self) = CanonicalIUnknown(IUnknown(LocalStgMedium.unkForRelease)) then - IUnknown(LocalStgMedium.unkForRelease) := nil; // release the interface - end; - end; - - // Tell all registered advice sinks about the data change. - if Assigned(FAdviseHolder) then - FAdviseHolder.SendOnDataChange(Self as IDataObject, 0, 0); -end; - -//----------------- TVTDragManager ------------------------------------------------------------------------------------- - -constructor TVTDragManager.Create(AOwner: TBaseVirtualTree); - -begin - inherited Create; - FOwner := AOwner; - - // Create an instance of the drop target helper interface. This will fail but not harm on systems which do - // not support this interface (everything below Windows 2000); - CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, FDropTargetHelper); + UpdateVerticalRange; end; //---------------------------------------------------------------------------------------------------------------------- -destructor TVTDragManager.Destroy; - -begin - // Set the owner's reference to us to nil otherwise it will access an invalid pointer - // after our desctruction is complete. - FOwner.ClearDragManager; - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- +function TBaseVirtualTree.CalculateCacheEntryCount: Integer; -function TVTDragManager.GetDataObject: IDataObject; +// Calculates the size of the position cache. begin - // When the owner tree starts a drag operation then it gets a data object here to pass it to the OLE subsystem. - // In this case there is no local reference to a data object and one is created (but not stored). - // If there is a local reference then the owner tree is currently the drop target and the stored interface is - // that of the drag initiator. - if Assigned(FDataObject) then - Result := FDataObject + if FVisibleCount > 1 then + Result := Ceil(FVisibleCount / CacheThreshold) else - begin - Result := FOwner.DoCreateDataObject; - if (Result = nil) and not Assigned(FOwner.OnCreateDataObject) then - // Do not create a TVTDataObject if the event handler explicitely decided not to supply one, issue #736. - Result := TVTDataObject.Create(FOwner, False) as IDataObject; - end; + Result := 0; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTDragManager.GetDragSource: TBaseVirtualTree; - -begin - Result := FDragSource; -end; - -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.CalculateVerticalAlignments(var PaintInfo: TVTPaintInfo; var VButtonAlign: Integer); -function TVTDragManager.GetDropTargetHelperSupported: Boolean; +// Calculates the vertical alignment of the given node and its associated expand/collapse button during +// a node paint cycle depending on the required node alignment style. begin - Result := Assigned(FDropTargetHelper); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVTDragManager.GetIsDropTarget: Boolean; + With PaintInfo do begin + // For absolute alignment the calculation is trivial. + case FNodeAlignment of + naFromTop: + VAlign := Node.Align; + naFromBottom: + VAlign := Integer(NodeHeight[Node]) - Node.Align; + else // naProportional + // Consider button and line alignment, but make sure neither the image nor the button (whichever is taller) + // go out of the entire node height (100% means bottom alignment to the node's bounds). + if (ImageInfo[iiNormal].Index >= 0) or (ImageInfo[iiState].Index >= 0) then + begin + if (ImageInfo[iiNormal].Index >= 0) then + VAlign := ImageInfo[iiNormal].Images.Height + else + VAlign := ImageInfo[iiState].Images.Height; + VAlign := MulDiv((Integer(NodeHeight[Node]) - VAlign), Node.Align, 100) + VAlign div 2; + end + else + if toShowButtons in FOptions.PaintOptions then + VAlign := MulDiv((Integer(NodeHeight[Node]) - FPlusBM.Height), Node.Align, 100) + FPlusBM.Height div 2 + else + VAlign := MulDiv(Integer(Node.NodeHeight), Node.Align, 100); + end; -begin - Result := FIsDropTarget; + VButtonAlign := VAlign - FPlusBM.Height div 2 - (FPlusBM.Height and 1); + end;// With PaintInfo end; //---------------------------------------------------------------------------------------------------------------------- -function TVTDragManager.DragEnter(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint; - var Effect: Integer): HResult; - -begin - FDataObject := DataObject; - FIsDropTarget := True; - - SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, @FFullDragging, 0); - // If full dragging of window contents is disabled in the system then our tree windows will be locked - // and cannot be updated during a drag operation. With the following call painting is again enabled. - if not FFullDragging then - LockWindowUpdate(0); - if Assigned(FDropTargetHelper) and FFullDragging then begin - if toAutoScroll in Self.FOwner.TreeOptions.AutoOptions then - FDropTargetHelper.DragEnter(FOwner.Handle, DataObject, Pt, Effect) - else - FDropTargetHelper.DragEnter(0, DataObject, Pt, Effect);// Do not pass handle, otherwise the IDropTargetHelper will perform autoscroll. Issue #486 - end; - FDragSource := FOwner.GetTreeFromDataObject(DataObject); - Result := FOwner.DragEnter(KeyState, Pt, Effect); -end; +function TBaseVirtualTree.ChangeCheckState(Node: PVirtualNode; Value: TCheckState): Boolean; -//---------------------------------------------------------------------------------------------------------------------- +// Sets the check state of the node according to the given value and the node's check type. +// If the check state must be propagated to the parent nodes and one of them refuses to change then +// nothing happens and False is returned, otherwise True. -function TVTDragManager.DragLeave: HResult; +var + Run: PVirtualNode; + UncheckedCount, + MixedCheckCount, + CheckedCount: Cardinal; begin - if Assigned(FDropTargetHelper) and FFullDragging then - FDropTargetHelper.DragLeave; - - FOwner.DragLeave; - FIsDropTarget := False; - FDragSource := nil; - FDataObject := nil; - Result := NOERROR; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVTDragManager.DragOver(KeyState: Integer; Pt: TPoint; var Effect: Integer): HResult; + Result := not (vsChecking in Node.States); + with Node^ do + if Result then + begin + Include(States, vsChecking); + try + if not (vsInitialized in States) then + InitNode(Node) + else if CheckState = Value then + begin + // Value didn't change and node was initialized, so nothing to do + Result := False; + Exit; + end;//if -begin - if Assigned(FDropTargetHelper) and FFullDragging then - FDropTargetHelper.DragOver(Pt, Effect); + // Indicate that we are going to propagate check states up and down the hierarchy. + if FCheckPropagationCount = 0 then begin + // Do not enter tsCheckPropagation more than once + DoStateChange([tsCheckPropagation]); + BeginUpdate(); + end; + Inc(FCheckPropagationCount); + try + // Do actions which are associated with the given check state. + case CheckType of + // Check state change with additional consequences for check states of the children. + ctTriStateCheckBox: + begin + // Propagate state down to the children. + if toAutoTristateTracking in FOptions.AutoOptions then + case Value of + csUncheckedNormal: + if Node.ChildCount > 0 then + begin + Run := FirstChild; + CheckedCount := 0; + MixedCheckCount := 0; + UncheckedCount := 0; + while Assigned(Run) do + begin + if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then + begin + if not Self.GetCheckState(Run).IsDisabled() then + SetCheckState(Run, csUncheckedNormal); + // Check if the new child state was set successfully, otherwise we have to adjust the + // node's new check state accordingly. + case Self.GetCheckState(Run) of + csCheckedNormal, csCheckedDisabled: + Inc(CheckedCount); + csMixedNormal: + Inc(MixedCheckCount); + csUncheckedNormal, csUncheckedDisabled: + Inc(UncheckedCount); + end; + end; + Run := Run.NextSibling; + end; - Result := FOwner.DragOver(FDragSource, KeyState, dsDragMove, Pt, Effect); -end; + // If there is still a mixed state child node checkbox then this node must be mixed checked too. + if MixedCheckCount > 0 then + Value := csMixedNormal + else + // If nodes are normally checked child nodes then the unchecked count determines what + // to set for the node itself. + if CheckedCount > 0 then + if UncheckedCount > 0 then + Value := csMixedNormal + else + Value := csCheckedNormal; + end; + csCheckedNormal: + if Node.ChildCount > 0 then + begin + Run := FirstChild; + CheckedCount := 0; + MixedCheckCount := 0; + UncheckedCount := 0; + while Assigned(Run) do + begin + if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then + begin + if not Self.GetCheckState(Run).IsDisabled() then + SetCheckState(Run, csCheckedNormal); + // Check if the new child state was set successfully, otherwise we have to adjust the + // node's new check state accordingly. + case Self.GetCheckState(Run) of + csCheckedNormal: + Inc(CheckedCount); + csMixedNormal: + Inc(MixedCheckCount); + csUncheckedNormal: + Inc(UncheckedCount); + end; + end; + Run := Run.NextSibling; + end; -//---------------------------------------------------------------------------------------------------------------------- + // If there is still a mixed state child node checkbox then this node must be mixed checked too. + if MixedCheckCount > 0 then + Value := csMixedNormal + else + // If nodes are normally checked child nodes then the unchecked count determines what + // to set for the node itself. + if CheckedCount > 0 then + if UncheckedCount > 0 then + Value := csMixedNormal + else + Value := csCheckedNormal; + end; + end; + end; + // radio button check state change + ctRadioButton: + if Value = csCheckedNormal then + begin + Value := csCheckedNormal; + // Make sure only this node is checked. + Run := Parent.FirstChild; + while Assigned(Run) do + begin + if Run.CheckType = ctRadioButton then + Run.CheckState := csUncheckedNormal; + Run := Run.NextSibling; + end; + Invalidate; + end; + end; -function TVTDragManager.Drop(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint; - var Effect: Integer): HResult; + if Result then + CheckState := Value // Set new check state + else + CheckState := Self.GetCheckState(Node).GetUnpressed(); // Reset dynamic check state. -begin - if Assigned(FDropTargetHelper) and FFullDragging then - FDropTargetHelper.Drop(DataObject, Pt, Effect); + // Propagate state up to the parent. + if not (vsInitialized in Parent.States) then + InitNode(Parent); + if (toAutoTristateTracking in FOptions.AutoOptions) and ([vsChecking, vsDisabled] * Parent.States = []) and + (CheckType in [ctCheckBox, ctTriStateCheckBox]) and (Parent <> FRoot) and + (Parent.CheckType = ctTriStateCheckBox) then + Result := CheckParentCheckState(Node, Value) + else + Result := True; - Result := FOwner.DragDrop(DataObject, KeyState, Pt, Effect); - FIsDropTarget := False; - FDataObject := nil; + InvalidateNode(Node); + finally + Dec(FCheckPropagationCount); // WL, 05.02.2004 + if FCheckPropagationCount = 0 then begin + // Allow state change event after all check operations finished + DoStateChange([], [tsCheckPropagation]); + EndUpdate(); + end; + end; + finally + Exclude(States, vsChecking); + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTDragManager.ForceDragLeave; - -// Some drop targets, e.g. Internet Explorer leave a drag image on screen instead removing it when they receive -// a drop action. This method calls the drop target helper's DragLeave method to ensure it removes the drag image from -// screen. Unfortunately, sometimes not even this does help (e.g. when dragging text from VT to a text field in IE). - -begin - if Assigned(FDropTargetHelper) and FFullDragging then - FDropTargetHelper.DragLeave; -end; - -//---------------------------------------------------------------------------------------------------------------------- +function TBaseVirtualTree.CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRight: Integer; Alignment: TAlignment; + OldRect, NewRect: TRect): Boolean; -function TVTDragManager.GiveFeedback(Effect: Integer): HResult; - -begin - Result := DRAGDROP_S_USEDEFAULTCURSORS; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVTDragManager.QueryContinueDrag(EscapePressed: BOOL; KeyState: Integer): HResult; +// Helper routine used when a draw selection takes place. This version handles left-to-right directionality. +// In the process of adding or removing nodes the current selection is modified which requires to pack it after +// the function returns. Another side effect of this method is that a temporary list of nodes will be created +// (see also InternalCacheNode) which must be inserted into the current selection by the caller. var - RButton, - LButton: Boolean; - -begin - LButton := (KeyState and MK_LBUTTON) <> 0; - RButton := (KeyState and MK_RBUTTON) <> 0; - - // Drag'n drop canceled by pressing both mouse buttons or Esc? - if (LButton and RButton) or EscapePressed then - Result := DRAGDROP_S_CANCEL - else - // Drag'n drop finished? - if not (LButton or RButton) then - Result := DRAGDROP_S_DROP - else - Result := S_OK; -end; - - -//----------------- TVirtualTreeHintWindow ----------------------------------------------------------------------------- - -procedure TVirtualTreeHintWindow.CMTextChanged(var Message: TMessage); - -begin - // swallow this message to prevent the ancestor from resizing the window (we don't use the caption anyway) -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeHintWindow.WMEraseBkgnd(var Message: TWMEraseBkgnd); + Run, + NextNode: PVirtualNode; + TextRight, + TextLeft, + CurrentTop, + CurrentRight, + NextTop, + NextColumn, + NodeWidth, + Dummy: Integer; + MinY, MaxY: Integer; + LabelOffset: Integer; + IsInOldRect, + IsInNewRect: Boolean; -// The control is fully painted by own code so don't erase its background as this causes flickering. + // quick check variables for various parameters + DoSwitch, + AutoSpan: Boolean; + SimpleSelection: Boolean; begin - Message.Result := 1; -end; - -//---------------------------------------------------------------------------------------------------------------------- + // A priori nothing changes. + Result := False; -procedure TVirtualTreeHintWindow.CreateParams(var Params: TCreateParams); + // Determine minimum and maximum vertical coordinates to limit iteration to. + MinY := Min(OldRect.Top, NewRect.Top); + MaxY := Max(OldRect.Bottom, NewRect.Bottom); -begin - inherited CreateParams(Params); + // Initialize short hand variables to speed up tests below. + DoSwitch := ssCtrl in FDrawSelShiftState; + AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.AutoOptions); + SimpleSelection := toSimpleDrawSelection in FOptions.SelectionOptions; + // This is the node to start with. + Run := GetNodeAt(0, MinY, False, CurrentTop); - with Params do + if Assigned(Run) then begin - Style := WS_POPUP; - ExStyle := ExStyle and not WS_EX_CLIENTEDGE; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeHintWindow.Paint(); -var - R: TRect; - Y: Integer; - S: string; - DrawFormat: Cardinal; - HintKind: TVTHintKind; - LClipRect: TRect; + LabelOffset := GetOffset(TVTElement.ofsLabel, Run); - LColor: TColor; - LDetails: TThemedElementDetails; - LGradientStart: TColor; - LGradientEnd: TColor; + // ----- main loop + // Change selection depending on the node's rectangle being in the selection rectangle or not, but + // touch only those nodes which overlap either the old selection rectangle or the new one but not both. + repeat + // Collect offsets for check, normal and state images. + TextLeft := NodeLeft + LabelOffset; + NextTop := CurrentTop + Integer(NodeHeight[Run]); -begin - with FHintData do - begin - // Do actual painting only in the very first run. - // If the given node is nil then we have to display a header hint. - if (Node = nil) or (Tree.FHintMode <> hmToolTip) then - begin - Canvas.Font := Screen.HintFont; - Canvas.Font.Height := MulDiv(Canvas.Font.Height, Tree.ScaledPixels(96), Screen.PixelsPerInch); // See issue #992 - Y := 2; - end - else - begin - Tree.GetTextInfo(Node, Column, Canvas.Font, R, S); - if LineBreakStyle = hlbForceMultiLine then - Y := 1 + // Simple selection allows to draw the selection rectangle anywhere. No intersection with node captions is + // required. Only top and bottom bounds of the rectangle matter. + if SimpleSelection or (toFullRowSelect in FOptions.SelectionOptions) then + begin + IsInOldRect := (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom) and + ((FHeader.Columns.Count = 0) or (FHeader.Columns.TotalWidth > OldRect.Left)) and ((NodeLeft + LabelOffset) < OldRect.Right); + IsInNewRect := (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom) and + ((FHeader.Columns.Count = 0) or (FHeader.Columns.TotalWidth > NewRect.Left)) and ((NodeLeft + LabelOffset) < NewRect.Right); + end else - Y := (R.Top - R.Bottom + Self.Height) div 2; - end; - - R := Rect(0, 0, Width, Height); - - HintKind := vhkText; - if Assigned(Node) then - Tree.DoGetHintKind(Node, Column, HintKind); - - if HintKind = vhkOwnerDraw then - begin - Tree.DoDrawHint(Canvas, Node, R, Column); - end - else - with Canvas do begin - if Tree.VclStyleEnabled then + // The right column border might be extended if column spanning is enabled. + if AutoSpan then begin - InflateRect(R, -1, -1); // Fixes missing border when VCL styles are used - LDetails := StyleServices(Tree).GetElementDetails(thHintNormal); - if StyleServices(Tree).GetElementColor(LDetails, ecGradientColor1, LColor) and (LColor <> clNone) then - LGradientStart := LColor - else - LGradientStart := clInfoBk; - if StyleServices(Tree).GetElementColor(LDetails, ecGradientColor2, LColor) and (LColor <> clNone) then - LGradientEnd := LColor - else - LGradientEnd := clInfoBk; - if StyleServices(Tree).GetElementColor(LDetails, ecTextColor, LColor) and (LColor <> clNone) then - Font.Color := LColor - else - Font.Color := Screen.HintFont.Color; - GradientFillCanvas(Canvas, LGradientStart, LGradientEnd, R, gdVertical); + with FHeader.Columns do + begin + NextColumn := MainColumn; + repeat + Dummy := GetNextVisibleColumn(NextColumn); + if (Dummy = InvalidColumn) or not ColumnIsEmpty(Run, Dummy) or + (Items[Dummy].BidiMode <> bdLeftToRight) then + Break; + NextColumn := Dummy; + until False; + if NextColumn = MainColumn then + CurrentRight := NodeRight + else + GetColumnBounds(NextColumn, Dummy, CurrentRight); + end; end else - begin - // Still force tooltip back and text color. - Font.Color := clInfoText; - Pen.Color := clBlack; - Brush.Color := clInfoBk; - if IsWinVistaOrAbove and StyleServices(Tree).Enabled and ((toThemeAware in Tree.TreeOptions.PaintOptions) or - (toUseExplorerTheme in Tree.TreeOptions.PaintOptions)) then + CurrentRight := NodeRight; + // Check if we need the node's width. This is the case when the node is not left aligned or the + // left border of the selection rectangle is to the right of the left node border. + if (TextLeft < OldRect.Left) or (TextLeft < NewRect.Left) or (Alignment <> taLeftJustify) then begin - if toUseExplorerTheme in Tree.TreeOptions.PaintOptions then // ToolTip style - StyleServices(Tree).DrawElement(Canvas.Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) + NodeWidth := DoGetNodeWidth(Run, MainColumn); + if NodeWidth >= (CurrentRight - TextLeft) then + TextRight := CurrentRight else - begin // Hint style - LClipRect := R; - InflateRect(R, 4, 4); - StyleServices(Tree).DrawElement(Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R, @LClipRect{$IF CompilerVersion >= 34}, FCurrentPPI{$IFEND}); - R := LClipRect; - StyleServices(Tree).DrawEdge(Handle, StyleServices(Tree).GetElementDetails(twWindowRoot), R, [eeRaisedOuter], [efRect]); + case Alignment of + taLeftJustify: + TextRight := TextLeft + NodeWidth; + taCenter: + begin + TextLeft := (TextLeft + CurrentRight - NodeWidth) div 2; + TextRight := TextLeft + NodeWidth; + end; + else + // taRightJustify + TextRight := CurrentRight; + TextLeft := TextRight - NodeWidth; end; end else - if Tree.VclStyleEnabled then - StyleServices(Tree).DrawElement(Canvas.Handle, StyleServices(Tree).GetElementDetails(tttStandardNormal), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}) - else - Rectangle(R); - end; - // Determine text position and don't forget the border. - InflateRect(R, -1, -1); - DrawFormat := DT_TOP or DT_NOPREFIX; - SetBkMode(Handle, Winapi.Windows.TRANSPARENT); - R.Top := Y; - R.Left := R.Left + 3; // Make the text more centered - if Assigned(Node) and (LineBreakStyle = hlbForceMultiLine) then - DrawFormat := DrawFormat or DT_WORDBREAK; - Winapi.Windows.DrawTextW(Handle, PWideChar(HintText), Length(HintText), R, DrawFormat); + TextRight := CurrentRight; + + // Now determine whether we need to change the state. + IsInOldRect := (OldRect.Left <= TextRight) and (OldRect.Right >= TextLeft) and + (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom); + IsInNewRect := (NewRect.Left <= TextRight) and (NewRect.Right >= TextLeft) and + (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom); end; - end; -end; -function TVirtualTreeHintWindow.StyleServices(AControl: TControl): TCustomStyleServices; -begin - Result := VTStyleServices(AControl); + if IsInOldRect xor IsInNewRect then + begin + Result := True; + if DoSwitch then + begin + if vsSelected in Run.States then + InternalRemoveFromSelection(Run) + else + InternalCacheNode(Run); + end + else + begin + if IsInNewRect then + InternalCacheNode(Run) + else + InternalRemoveFromSelection(Run); + end; + end; + CurrentTop := NextTop; + // Get next visible node and update left node position. + NextNode := GetNextVisibleNoInit(Run, True); + if NextNode = nil then + Break; + Inc(NodeLeft, CountLevelDifference(Run, NextNode) * Integer(FIndent)); + Run := NextNode; + until CurrentTop > MaxY; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeHintWindow.CalcHintRect(MaxWidth: Integer; const AHint: string; AData: Pointer): TRect; +function TBaseVirtualTree.CollectSelectedNodesRTL(MainColumn, NodeLeft, NodeRight: Integer; Alignment: TAlignment; + OldRect, NewRect: TRect): Boolean; -var - TM: TTextMetric; - R: TRect; +// Helper routine used when a draw selection takes place. This version handles right-to-left directionality. +// See also comments in CollectSelectedNodesLTR. + +var + Run, + NextNode: PVirtualNode; + TextRight, + TextLeft, + CheckOffset, + CurrentTop, + CurrentLeft, + NextTop, + NextColumn, + NodeWidth, + Dummy: Integer; + MinY, MaxY: Integer; + IsInOldRect, + IsInNewRect: Boolean; + + // quick check variables for various parameters + WithCheck, + WithStateImages, + DoSwitch, + AutoSpan: Boolean; + SimpleSelection: Boolean; begin - try - if AData = nil then - // Defensive approach, it *can* happen that AData is nil. Maybe when several user defined hint classes are used. - Result := Rect(0, 0, 0, 0) + // A priori nothing changes. + Result := False; + // Switch the alignment to the opposite value in RTL context. + ChangeBiDiModeAlignment(Alignment); + + // Determine minimum and maximum vertical coordinates to limit iteration to. + MinY := Min(OldRect.Top, NewRect.Top); + MaxY := Max(OldRect.Bottom, NewRect.Bottom); + + // Initialize short hand variables to speed up tests below. + DoSwitch := ssCtrl in FDrawSelShiftState; + WithCheck := (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages); + // Don't check the events here as descendant trees might have overriden the DoGetImageIndex method. + WithStateImages := Assigned(FStateImages) or Assigned(OnGetImageIndexEx); + if WithCheck then + CheckOffset := FCheckImages.Width + FImagesMargin + else + CheckOffset := 0; + AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.AutoOptions); + SimpleSelection := toSimpleDrawSelection in FOptions.SelectionOptions; + // This is the node to start with. + Run := GetNodeAt(0, MinY, False, CurrentTop); + + if Assigned(Run) then + begin + // The initial minimal left border is determined by the identation level of the node and is dynamically adjusted. + if toShowRoot in FOptions.PaintOptions then + Dec(NodeRight, Integer((GetNodeLevel(Run) + 1) * FIndent) + FMargin) else - begin - // The hint window does not need any bidi mode setting but the caller of this method (TApplication.ActivateHint) - // does some unneccessary actions if the hint window is not left-to-right. - // The text alignment is based on the bidi mode passed in the hint data, hence we can - // simply set the window's mode to left-to-right (it might have been modified by the caller, if the - // tree window is right-to-left aligned). - BidiMode := bdLeftToRight; + Dec(NodeRight, Integer(GetNodeLevel(Run) * FIndent) + FMargin); - FHintData := PVTHintData(AData)^; + // ----- main loop + // Change selection depending on the node's rectangle being in the selection rectangle or not, but + // touch only those nodes which overlap either the old selection rectangle or the new one but not both. + repeat + // Collect offsets for check, normal and state images. + TextRight := NodeRight; + if WithCheck and (Run.CheckType <> ctNone) then + Dec(TextRight, CheckOffset); + Dec(TextRight, GetImageSize(Run, ikNormal, MainColumn).cx); + if WithStateImages then + Dec(TextRight, GetImageSize(Run, ikState, MainColumn).cx); + NextTop := CurrentTop + Integer(NodeHeight[Run]); - with FHintData do + // Simple selection allows to draw the selection rectangle anywhere. No intersection with node captions is + // required. Only top and bottom bounds of the rectangle matter. + if SimpleSelection then begin - // The draw tree gets its hint size by the application (but only if not a header hint is about to show). - // If the user will be drawing the hint, it gets its hint size by the application - // (but only if not a header hint is about to show). - // This size has already been determined in CMHintShow. - if Assigned(Node) and (not IsRectEmpty(HintRect)) then - Result := HintRect - else + IsInOldRect := (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom); + IsInNewRect := (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom); + end + else + begin // The left column border might be extended if column spanning is enabled. + if AutoSpan then begin - if Column <= NoColumn then - begin - BidiMode := Tree.BidiMode; - Alignment := Tree.Alignment; - end + NextColumn := MainColumn; + repeat + Dummy := FHeader.Columns.GetPreviousVisibleColumn(NextColumn); + if (Dummy = InvalidColumn) or not ColumnIsEmpty(Run, Dummy) or + (FHeader.Columns[Dummy].BiDiMode = bdLeftToRight) then + Break; + NextColumn := Dummy; + until False; + if NextColumn = MainColumn then + CurrentLeft := NodeLeft else + FHeader.Columns.GetColumnBounds(NextColumn, CurrentLeft, Dummy); + end + else + CurrentLeft := NodeLeft; + // Check if we need the node's width. This is the case when the node is not left aligned (in RTL context this // means actually right aligned) or the right border of the selection rectangle is to the left + // of the right node border. + if (TextRight > OldRect.Right) or (TextRight > NewRect.Right) or (Alignment <> taRightJustify) then begin - BidiMode := Tree.Header.Columns[Column].BidiMode; - Alignment := Tree.Header.Columns[Column].Alignment; - end; - - if BidiMode <> bdLeftToRight then - ChangeBidiModeAlignment(Alignment); - - if (Node = nil) or (Tree.FHintMode <> hmToolTip) then - begin - Canvas.Font := Screen.HintFont; - Canvas.Font.Height := MulDiv(Canvas.Font.Height, Tree.ScaledPixels(96), Screen.PixelsPerInch); // See issue #992 - end + NodeWidth := DoGetNodeWidth(Run, MainColumn); + if NodeWidth >= (TextRight - CurrentLeft) then + TextLeft := CurrentLeft else - begin - Canvas.Font := Tree.Font; - if Tree is TCustomVirtualStringTree then - with TCustomVirtualStringTree(Tree) do - DoPaintText(Node, Self.Canvas, Column, ttNormal); - end; + case Alignment of + taLeftJustify: + begin + TextLeft := CurrentLeft; + TextRight := TextLeft + NodeWidth; + end; + taCenter: + begin + TextLeft := (TextRight + CurrentLeft - NodeWidth) div 2; + TextRight := TextLeft + NodeWidth; + end; + else + // taRightJustify + TextLeft := TextRight - NodeWidth; + end; + end + else + TextLeft := CurrentLeft; - GetTextMetrics(Canvas.Handle, TM); - FTextHeight := TM.tmHeight; + // Now determine whether we need to change the state. + IsInOldRect := (OldRect.Right >= TextLeft) and (OldRect.Left <= TextRight) and + (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom); + IsInNewRect := (NewRect.Right >= TextLeft) and (NewRect.Left <= TextRight) and + (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom); + end; - if Length(HintText) = 0 then - Result := Rect(0, 0, 0, 0) + if IsInOldRect xor IsInNewRect then + begin + Result := True; + if DoSwitch then + begin + if vsSelected in Run.States then + InternalRemoveFromSelection(Run) else - begin - if Assigned(Node) and (Tree.FHintMode = hmToolTip) then - begin - // Determine actual line break style depending on what was returned by the methods and what's in the node. - if LineBreakStyle = hlbDefault then - if vsMultiline in Node.States then - LineBreakStyle := hlbForceMultiLine - else - LineBreakStyle := hlbForceSingleLine; + InternalCacheNode(Run); + end + else + begin + if IsInNewRect then + InternalCacheNode(Run) + else + InternalRemoveFromSelection(Run); + end; + end; + CurrentTop := NextTop; + // Get next visible node and update left node position. + NextNode := GetNextVisibleNoInit(Run, True); + if NextNode = nil then + Break; + Dec(NodeRight, CountLevelDifference(Run, NextNode) * Integer(FIndent)); + Run := NextNode; + until CurrentTop > MaxY; + end; +end; - // Hint for a node. - if LineBreakStyle = hlbForceMultiLine then - begin - // Multiline tooltips use the columns width but extend the bottom border to fit the whole caption. - Result := Tree.GetDisplayRect(Node, Column, True, False); - R := Result; +//---------------------------------------------------------------------------------------------------------------------- - // On Windows NT/2K/XP the behavior of the tooltip is slightly different to that on Windows 9x/Me. - // We don't have Unicode word wrap on the latter so the tooltip gets as wide as the largest line - // in the caption (limited by carriage return), which results in unoptimal overlay of the tooltip. - Winapi.Windows.DrawTextW(Canvas.Handle, PWideChar(HintText), Length(HintText), R, DT_CALCRECT or DT_WORDBREAK); - if BidiMode = bdLeftToRight then - Result.Right := R.Right + Tree.FTextMargin - else - Result.Left := R.Left - Tree.FTextMargin + 1; - Result.Bottom := R.Bottom; +procedure TBaseVirtualTree.ClearNodeBackground(const PaintInfo: TVTPaintInfo; UseBackground, Floating: Boolean; + R: TRect); - Inc(Result.Right); +// Erases a node's background depending on what the application decides to do. +// UseBackground determines whether or not to use the background picture, while Floating indicates +// that R is given in coordinates of the small node bitmap or the superordinated target bitmap used in PaintTree. - // If the node height and the column width are both already large enough to cover the entire text, - // then we don't need the hint, though. - // However if the text is partially scrolled out of the client area then a hint is useful as well. - if (Tree.Header.Columns.Count > 0) and ((Integer(Tree.NodeHeight[Node]) + 2) >= (Result.Bottom - Result.Top)) and - ((Tree.Header.Columns[Column].Width + 2) >= (Result.Right - Result.Left)) and not - ((Result.Left < 0) or (Result.Right > Tree.ClientWidth + 3) or - (Result.Top < 0) or (Result.Bottom > Tree.ClientHeight + 3)) then - begin - Result := Rect(0, 0, 0, 0); - Exit; - end; - end - else - begin - Result := Tree.FLastHintRect; // = Tree.GetDisplayRect(Node, Column, True, True, True); see TBaseVirtualTree.CMHintShow +var + BackColor: TColor; + EraseAction: TItemEraseAction; + Offset: TPoint; - { Fixes issue #623 +begin + BackColor := FColors.BackGroundColor; + with PaintInfo do + begin + EraseAction := eaDefault; - Measure the rectangle to draw the text. The width of the result - is always adjusted according to the hint text because it may - be a custom hint coming in which can be larger or smaller than - the node text. - Earlier logic was using the current width of the node that was - either cutting off the hint text or producing undesired space - on the right. - } - R := Rect(0, 0, MaxWidth, FTextHeight); - Winapi.Windows.DrawTextW(Canvas.Handle, PWideChar(HintText), Length(HintText), R, DT_CALCRECT or DT_TOP or DT_NOPREFIX or DT_WORDBREAK); - if R.Right <> result.right - result.left then - begin - result.Right := result.Left + r.Right; - - //Space on right--taken from the code in the hmHint branch below. - if Assigned(Tree) then - Inc(Result.Right, Tree.FTextMargin + Tree.FMargin + Tree.ScaledPixels(4)); - end; - // Fix ends. - - if toShowHorzGridLines in Tree.TreeOptions.PaintOptions then - Dec(Result.Bottom); - end; + if Floating then + begin + Offset := Point(-FEffectiveOffsetX, R.Top); + OffsetRect(R, 0, -Offset.Y); + end + else + Offset := Point(0, 0); - // Include a one pixel border. - InflateRect(Result, 1, 1); + DoBeforeItemErase(Canvas, Node, R, BackColor, EraseAction); - // Make the coordinates relative. They will again be offset by the caller code. - OffsetRect(Result, -Result.Left - 1, -Result.Top - 1); + with Canvas do + begin + case EraseAction of + eaNone: + ; + eaColor: + begin + // User has given a new background color. + Brush.Color := BackColor; + FillRect(R); + end; + else // eaDefault + if UseBackground then + begin + if toStaticBackground in TreeOptions.PaintOptions then + StaticBackground(FBackground, Canvas, Offset, R, FColors.BackGroundColor) + else + TileBackground(FBackground, Canvas, Offset, R, FColors.BackGroundColor); + end + else + begin + if (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.SelectionOptions) and + (vsSelected in Node.States) and not (toUseBlendedSelection in FOptions.PaintOptions) and not + (tsUseExplorerTheme in FStates) then + begin + if toShowHorzGridLines in FOptions.PaintOptions then + begin + Brush.Color := BackColor; + FillRect(Rect(R.Left, R.Bottom - 1, R.Right, R.Bottom)); + Dec(R.Bottom); + end; + if Focused or (toPopupMode in FOptions.PaintOptions) then + begin + Brush.Color := FColors.FocusedSelectionColor; + Pen.Color := FColors.FocusedSelectionBorderColor; end else begin - // Hint for a header or non-tooltip hint. - - // Start with the base size of the hint in client coordinates. - Result := Rect(0, 0, MaxWidth, FTextHeight); - // Calculate the true size of the text rectangle. - Winapi.Windows.DrawTextW(Canvas.Handle, PWideChar(HintText), Length(HintText), Result, DT_CALCRECT or DT_TOP or DT_NOPREFIX or DT_WORDBREAK); - // The height of the text plus 2 pixels vertical margin plus the border determine the hint window height. - // Minus 4 because THintWindow.ActivateHint adds 4 to Rect.Bottom anyway. Note that it is not scaled because the RTL itself does not do any scaling either. - Inc(Result.Bottom, Tree.ScaledPixels(6) - 4); - // The text is centered horizontally with usual text margin for left and right borders (plus border). - if not Assigned(Tree) then - Exit; // Workaround, because we have seen several exceptions here caught by Eurekalog. Submitted as issue #114 to http://code.google.com/p/virtual-treeview/ - { Issue #623 Fix for strange space on the right. - Original logic was adding FTextHeight. Changed it to add FMargin instead and - it looks OK even if the hint font is larger. - } - Inc(Result.Right, Tree.FTextMargin - + Tree.FMargin + Tree.ScaledPixels(4)); //Issue #623 space on right - //+ FTextHeight); // Old code: We are extending the width here, but the text height scales with the text width and has a similar value as AveCharWdith * 2. + Brush.Color := FColors.UnfocusedSelectionColor; + Pen.Color := FColors.UnfocusedSelectionBorderColor; end; + + with TWithSafeRect(R) do + RoundRect(Left, Top, Right, Bottom, FSelectionCurveRadius, FSelectionCurveRadius); + end + else + begin + Brush.Color := BackColor; + FillRect(R); end; end; end; + DoAfterItemErase(Canvas, Node, R); end; - except - Application.HandleException(Self); end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeHintWindow.IsHintMsg(var Msg: TMsg): Boolean; - -// The VCL is a bit too generous when telling that an existing hint can be cancelled. Need to specify further here. - -begin - Result := inherited IsHintMsg(Msg) and HandleAllocated and IsWindowVisible(Handle); - // Avoid that mouse moves over the non-client area or cursor key presses cancel the current hint. - if Result and ((Msg.Message = WM_NCMOUSEMOVE) or ((Msg.Message >= WM_KEYFIRST) and (Msg.Message <= WM_KEYLAST) and (Msg.wparam in [VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT]))) then - Result := False; -end; - -//----------------- TVTDragImage --------------------------------------------------------------------------------------- - -constructor TVTDragImage.Create(AOwner: TBaseVirtualTree); - -begin - FOwner := AOwner; - FTransparency := 128; - FPreBlendBias := 0; - FPostBlendBias := 0; - FFade := False; - FRestriction := dmrNone; - FColorKey := clNone; -end; +function TBaseVirtualTree.CompareNodePositions(Node1, Node2: PVirtualNode; ConsiderChildrenAbove: Boolean = False): Integer; -//---------------------------------------------------------------------------------------------------------------------- +// Tries hard and smart to quickly determine whether Node1's structural position is before Node2's position. +// If ConsiderChildrenAbove is True, the nodes will be compared with their visual order in mind. +// Returns 0 if Node1 = Node2, < 0 if Node1 is located before Node2 else > 0. -destructor TVTDragImage.Destroy; +var + Run1, + Run2: PVirtualNode; + Level1, + Level2: Cardinal; begin - EndDrag; - - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- + Assert(Assigned(Node1) and Assigned(Node2), 'Nodes must never be nil.'); -function TVTDragImage.GetVisible: Boolean; + if Node1 = Node2 then + Result := 0 + else + begin + if HasAsParent(Node1, Node2) then + Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions), -1, 1) + else + if HasAsParent(Node2, Node1) then + Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions), 1, -1) + else + begin + // the given nodes are neither equal nor are they parents of each other, so go up to FRoot + // for each node and compare the child indices of the top level parents + // Note: neither Node1 nor Node2 can be FRoot at this point as this (a bit strange) circumstance would + // be caught by the previous code. -// Returns True if the internal drag image is used (i.e. the system does not natively support drag images) and -// the internal image is currently visible on screen. + // start lookup at the same level + Level1 := GetNodeLevel(Node1); + Level2 := GetNodeLevel(Node2); + Run1 := Node1; + while Level1 > Level2 do + begin + Run1 := Run1.Parent; + Dec(Level1); + end; + Run2 := Node2; + while Level2 > Level1 do + begin + Run2 := Run2.Parent; + Dec(Level2); + end; -begin - Result := FStates * [disHidden, disInDrag, disPrepared, disSystemSupport] = [disInDrag, disPrepared]; + // now go up until we find a common parent node (loop will safely stop at FRoot if the nodes + // don't share a common parent) + while Run1.Parent <> Run2.Parent do + begin + Run1 := Run1.Parent; + Run2 := Run2.Parent; + end; + Result := Integer(Run1.Index) - Integer(Run2.Index); + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTDragImage.InternalShowDragImage(ScreenDC: HDC); +procedure TBaseVirtualTree.DrawLineImage(const PaintInfo: TVTPaintInfo; X, Y, H, VAlign: Integer; Style: TVTLineType; + Reverse: Boolean); -// Frequently called helper routine to actually do the blend and put it onto the screen. -// Only used if the system does not support drag images. +// Draws (depending on Style) one of the 5 line types of the tree. +// If Reverse is True then a right-to-left column is being drawn, hence horizontal lines must be mirrored. +// X and Y describe the left upper corner of the line image rectangle, while H denotes its height (and width). var - BlendMode: TBlendMode; - -begin - with FAlphaImage do - BitBlt(Canvas.Handle, 0, 0, Width, Height, FBackImage.Canvas.Handle, 0, 0, SRCCOPY); - if not FFade and (FColorKey = clNone) then - BlendMode := bmConstantAlpha - else - BlendMode := bmMasterAlpha; - with FDragImage do - AlphaBlend(Canvas.Handle, FAlphaImage.Canvas.Handle, Rect(0, 0, Width, Height), Point(0, 0), BlendMode, - FTransparency, FPostBlendBias); - - with FAlphaImage do - BitBlt(ScreenDC, FImagePosition.X, FImagePosition.Y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTDragImage.MakeAlphaChannel(Source, Target: TBitmap); - -// Helper method to create a proper alpha channel in Target (which must be in 32 bit pixel format), depending -// on the settings for the drag image and the color values in Source. -// Only used if the system does not support drag images. - -type - PBGRA = ^TBGRA; - TBGRA = packed record - case Boolean of - False: - (Color: Cardinal); - True: - (BGR: array[0..2] of Byte; - Alpha: Byte); - end; - -var - Color, - ColorKeyRef: COLORREF; - UseColorKey: Boolean; - SourceRun, - TargetRun: PBGRA; - X, Y, - MaxDimension, HalfWidth, - HalfHeight: Integer; - T: Extended; + TargetX: Integer; begin - UseColorKey := ColorKey <> clNone; - ColorKeyRef := ColorToRGB(ColorKey) and $FFFFFF; - // Color values are in the form BGR (red on LSB) while bitmap colors are in the form ARGB (blue on LSB) - // hence we have to swap red and blue in the color key. - with TBGRA(ColorKeyRef) do - begin - X := BGR[0]; - BGR[0] := BGR[2]; - BGR[2] := X; - end; + HalfWidth := (FIndent div 2); + if Reverse then + TargetX := 0 + else + TargetX := Integer(FIndent) + ScaledPixels(FImagesMargin); - with Target do + with PaintInfo.Canvas do begin - MaxDimension := Max(Width, Height); - - HalfWidth := Width div 2; - HalfHeight := Height div 2; - for Y := 0 to Height - 1 do - begin - TargetRun := Scanline[Y]; - SourceRun := Source.Scanline[Y]; - for X := 0 to Width - 1 do - begin - Color := SourceRun.Color and $FFFFFF; - if UseColorKey and (Color = ColorKeyRef) then - TargetRun.Alpha := 0 + case Style of + ltBottomRight: + begin + DrawDottedVLine(PaintInfo, Y + VAlign, Y + H, X + HalfWidth); + DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign); + end; + ltTopDown: + DrawDottedVLine(PaintInfo, Y, Y + H, X + HalfWidth); + ltTopDownRight: + begin + DrawDottedVLine(PaintInfo, Y, Y + H, X + HalfWidth); + DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign); + end; + ltRight: + DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign); + ltTopRight: + begin + DrawDottedVLine(PaintInfo, Y, Y + VAlign, X + HalfWidth); + DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign); + end; + ltLeft: // left can also mean right for RTL context + if Reverse then + DrawDottedVLine(PaintInfo, Y, Y + H, X + Integer(FIndent)) + else + DrawDottedVLine(PaintInfo, Y, Y + H, X); + ltLeftBottom: + if Reverse then + begin + DrawDottedVLine(PaintInfo, Y, Y + H, X + Integer(FIndent)); + DrawDottedHLine(PaintInfo, X, X + Integer(FIndent), Y + H); + end else begin - // If the color is not the given color key (or none is used) then do full calculation of a bell curve. - T := Exp(-8 * Sqrt(Sqr((X - HalfWidth) / MaxDimension) + Sqr((Y - HalfHeight) / MaxDimension))); - TargetRun.Alpha := Round(255 * T); + DrawDottedVLine(PaintInfo, Y, Y + H, X); + DrawDottedHLine(PaintInfo, X, X + Integer(FIndent), Y + H); end; - Inc(SourceRun); - Inc(TargetRun); - end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTDragImage.DragTo(P: TPoint; ForceRepaint: Boolean): Boolean; +function TBaseVirtualTree.FindInPositionCache(Node: PVirtualNode; var CurrentPos: Cardinal): PVirtualNode; -// Moves the drag image to a new position, which is determined from the passed point P and the previous -// mouse position. -// ForceRepaint is True if something on the screen changed and the back image must be refreshed. +// Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal +// to the position of the given node. var - ScreenDC: HDC; - DeltaX, - DeltaY: Integer; - - // optimized drag image move support - RSamp1, - RSamp2, // newly added parts from screen which will be overwritten - RDraw1, - RDraw2, // parts to be restored to screen - RScroll, - RClip: TRect; // ScrollDC of the existent background + L, H, I: Integer; begin - // Determine distances to move the drag image. Take care for restrictions. - case FRestriction of - dmrHorizontalOnly: - begin - DeltaX := FLastPosition.X - P.X; - DeltaY := 0; - end; - dmrVerticalOnly: - begin - DeltaX := 0; - DeltaY := FLastPosition.Y - P.Y; - end; - else // dmrNone - DeltaX := FLastPosition.X - P.X; - DeltaY := FLastPosition.Y - P.Y; + L := 0; + H := High(FPositionCache); + while L <= H do + begin + I := (L + H) shr 1; + if CompareNodePositions(FPositionCache[I].Node, Node) <= 0 then + L := I + 1 + else + H := I - 1; end; - - Result := (DeltaX <> 0) or (DeltaY <> 0) or ForceRepaint; - if Result then + if L = 0 then // High(FPositionCache) = -1 begin - if Visible then - begin - // All this stuff is only called if we have to handle the drag image ourselves. If the system supports - // drag image then this is all never executed. - ScreenDC := GetDC(0); - try - if (Abs(DeltaX) >= FDragImage.Width) or (Abs(DeltaY) >= FDragImage.Height) or ForceRepaint then - begin - // If moved more than image size then just restore old screen and blit image to new position. - BitBlt(ScreenDC, FImagePosition.X, FImagePosition.Y, FBackImage.Width, FBackImage.Height, - FBackImage.Canvas.Handle, 0, 0, SRCCOPY); - - if ForceRepaint then - UpdateWindow(FOwner.Handle); - - Inc(FImagePosition.X, -DeltaX); - Inc(FImagePosition.Y, -DeltaY); - - BitBlt(FBackImage.Canvas.Handle, 0, 0, FBackImage.Width, FBackImage.Height, ScreenDC, FImagePosition.X, - FImagePosition.Y, SRCCOPY); - end - else - begin - // overlapping copy - FillDragRectangles(FDragImage.Width, FDragImage.Height, DeltaX, DeltaY, RClip, RScroll, RSamp1, RSamp2, RDraw1, - RDraw2); - - with FBackImage.Canvas do - begin - // restore uncovered areas of the screen - if DeltaX = 0 then - begin - with TWithSafeRect(RDraw2) do - BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top, - SRCCOPY); - end - else - begin - if DeltaY = 0 then - begin - with TWithSafeRect(RDraw1) do - BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top, - SRCCOPY); - end - else - begin - with TWithSafeRect(RDraw1) do - BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top, - SRCCOPY); - with TWithSafeRect(RDraw2) do - BitBlt(ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, Right, Bottom, Handle, Left, Top, - SRCCOPY); - end; - end; - - // move existent background - ScrollDC(Handle, DeltaX, DeltaY, RScroll, RClip, 0, nil); - - Inc(FImagePosition.X, -DeltaX); - Inc(FImagePosition.Y, -DeltaY); - - // Get first and second additional rectangle from screen. - if DeltaX = 0 then - begin - with TWithSafeRect(RSamp2) do - BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, - SRCCOPY); - end - else - if DeltaY = 0 then - begin - with TWithSafeRect(RSamp1) do - BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, - SRCCOPY); - end - else - begin - with TWithSafeRect(RSamp1) do - BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, - SRCCOPY); - with TWithSafeRect(RSamp2) do - BitBlt(Handle, Left, Top, Right, Bottom, ScreenDC, FImagePosition.X + Left, FImagePosition.Y + Top, - SRCCOPY); - end; - end; - end; - InternalShowDragImage(ScreenDC); - finally - ReleaseDC(0, ScreenDC); - end; - end; - FLastPosition.X := P.X; - FLastPosition.Y := P.Y; + Result := nil; + CurrentPos := 0; + end + else + begin + Result := FPositionCache[L - 1].Node; + CurrentPos := FPositionCache[L - 1].AbsoluteTop; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTDragImage.EndDrag; - -begin - HideDragImage; - FStates := FStates - [disInDrag, disPrepared]; - - FBackImage.Free; - FBackImage := nil; - FDragImage.Free; - FDragImage := nil; - FAlphaImage.Free; - FAlphaImage := nil; -end; - -//---------------------------------------------------------------------------------------------------------------------- +function TBaseVirtualTree.FindInPositionCache(Position: Cardinal; var CurrentPos: Cardinal): PVirtualNode; -function TVTDragImage.GetDragImageRect: TRect; +// Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal +// to the given vertical position. +// The returned node does not necessarily occupy the given position but is the nearest one to start +// iterating from to approach the real node for a given position. CurrentPos receives the actual position of the found +// node which is needed for further iteration. -// Returns the current size and position of the drag image (screen coordinates). +var + L, H, I: Integer; begin - if Visible then + L := 0; + H := High(FPositionCache); + while L <= H do + begin + I := (L + H) shr 1; + if FPositionCache[I].AbsoluteTop <= Position then + L := I + 1 + else + H := I - 1; + end; + if L = 0 then // High(FPositionCache) = -1 begin - with FBackImage do - Result := Rect(FImagePosition.X, FImagePosition.Y, FImagePosition.X + Width, FImagePosition.Y + Height); + Result := nil; + CurrentPos := 0; end else - Result := Rect(0, 0, 0, 0); + begin + Result := FPositionCache[L - 1].Node; + CurrentPos := FPositionCache[L - 1].AbsoluteTop; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTDragImage.HideDragImage; +procedure TBaseVirtualTree.FixupTotalCount(Node: PVirtualNode); + +// Called after loading a subtree from stream. The child count in each node is already set but not +// their total count. var - ScreenDC: HDC; + Child: PVirtualNode; begin - if Visible then + // Initial total count is set to one on node creation. + Child := Node.FirstChild; + while Assigned(Child) do begin - Include(FStates, disHidden); - ScreenDC := GetDC(0); - try - // restore screen - with FBackImage do - BitBlt(ScreenDC, FImagePosition.X, FImagePosition.Y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY); - finally - ReleaseDC(0, ScreenDC); - end; + FixupTotalCount(Child); + Inc(Node.TotalCount, Child.TotalCount); + Child := Child.NextSibling; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTDragImage.PrepareDrag(DragImage: TBitmap; ImagePosition, HotSpot: TPoint; const DataObject: IDataObject); +procedure TBaseVirtualTree.FixupTotalHeight(Node: PVirtualNode); -// Creates all necessary structures to do alpha blended dragging using the given image. -// ImagePostion and HotSpot are given in screen coordinates. The first determines where to place the drag image while -// the second is the initial mouse position. -// This method also determines whether the system supports drag images natively. If so then only minimal structures -// are created. +// Called after loading a subtree from stream. The individual height of each node is set already, +// but their total height needs an adjustment depending on their visibility state. var - Width, - Height: Integer; - DragSourceHelper: IDragSourceHelper; - DragInfo: TSHDragImage; - lDragSourceHelper2: IDragSourceHelper2;// Needed to get Windows Vista+ style drag hints. - lNullPoint: TPoint; -begin - Width := DragImage.Width; - Height := DragImage.Height; - - // Determine whether the system supports the drag helper interfaces. - if Assigned(DataObject) and Succeeded(CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER, - IDragSourceHelper, DragSourceHelper)) then - begin - Include(FStates, disSystemSupport); - lNullPoint := Point(0,0); - if Supports(DragSourceHelper, IDragSourceHelper2, lDragSourceHelper2) then - lDragSourceHelper2.SetFlags(DSH_ALLOWDROPDESCRIPTIONTEXT);// Show description texts - // First let the system try to initialze the DragSourceHelper, this works fine for file system objects (CF_HDROP) - StandardOLEFormat.cfFormat := CF_HDROP; - if not Succeeded(DataObject.QueryGetData(StandardOLEFormat)) or not Succeeded(DragSourceHelper.InitializeFromWindow(0, lNullPoint, DataObject)) then - begin - // Supply the drag source helper with our drag image. - DragInfo.sizeDragImage.cx := Width; - DragInfo.sizeDragImage.cy := Height; - DragInfo.ptOffset.x := Width div 2; - DragInfo.ptOffset.y := Height div 2; - DragInfo.hbmpDragImage := CopyImage(DragImage.Handle, IMAGE_BITMAP, Width, Height, LR_COPYRETURNORG); - DragInfo.crColorKey := ColorToRGB(FColorKey); - if not Succeeded(DragSourceHelper.InitializeFromBitmap(@DragInfo, DataObject)) then - begin - DeleteObject(DragInfo.hbmpDragImage); - Exclude(FStates, disSystemSupport); - end; + Child: PVirtualNode; + +begin + // Initial total height is set to the node height on load. + Child := Node.FirstChild; + + if vsExpanded in Node.States then + begin + while Assigned(Child) do + begin + FixupTotalHeight(Child); + if vsVisible in Child.States then + Inc(Node.TotalHeight, Child.TotalHeight); + Child := Child.NextSibling; end; end else - Exclude(FStates, disSystemSupport); - - if not (disSystemSupport in FStates) then begin - FLastPosition := HotSpot; + // The node is collapsed, so just update the total height of its child nodes. + while Assigned(Child) do + begin + FixupTotalHeight(Child); + Child := Child.NextSibling; + end; + end; +end; - FDragImage := TBitmap.Create; - FDragImage.PixelFormat := pf32Bit; - FDragImage.SetSize(Width, Height); +//---------------------------------------------------------------------------------------------------------------------- - FAlphaImage := TBitmap.Create; - FAlphaImage.PixelFormat := pf32Bit; - FAlphaImage.SetSize(Width, Height); +function TBaseVirtualTree.GetBottomNode: PVirtualNode; - FBackImage := TBitmap.Create; - FBackImage.PixelFormat := pf32Bit; - FBackImage.SetSize(Width, Height); +begin + Result := GetNodeAt(0, ClientHeight - 1); +end; - // Copy the given drag image and apply pre blend bias if required. - if FPreBlendBias = 0 then - with FDragImage do - BitBlt(Canvas.Handle, 0, 0, Width, Height, DragImage.Canvas.Handle, 0, 0, SRCCOPY) - else - AlphaBlend(DragImage.Canvas.Handle, FDragImage.Canvas.Handle, Rect(0, 0, Width, Height), Point(0, 0), - bmConstantAlpha, 255, FPreBlendBias); +//---------------------------------------------------------------------------------------------------------------------- - // Create a proper alpha channel also if no fading is required (transparent parts). - MakeAlphaChannel(DragImage, FDragImage); +function TBaseVirtualTree.GetCheckedCount: Integer; - FImagePosition := ImagePosition; +var + Node: PVirtualNode; - // Initially the drag image is hidden and will be shown during the immediately following DragEnter event. - FStates := FStates + [disInDrag, disHidden, disPrepared]; +begin + Result := 0; + Node := GetFirstChecked; + while Assigned(Node) do + begin + Inc(Result); + Node := GetNextChecked(Node); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTDragImage.RecaptureBackground(Tree: TBaseVirtualTree; R: TRect; VisibleRegion: HRGN; - CaptureNCArea, ReshowDragImage: Boolean); - -// Notification by the drop target tree to update the background image because something in the tree has changed. -// Note: The passed rectangle is given in client coordinates of the current drop target tree (given in Tree). -// The caller does not check if the given rectangle is actually within the drag image. Hence this method must do -// all the checks. -// This method does nothing if the system manages the drag image. - -var - DragRect, - ClipRect: TRect; - PaintTarget: TPoint; - PaintOptions: TVTInternalPaintOptions; - ScreenDC: HDC; +function TBaseVirtualTree.GetCheckState(Node: PVirtualNode): TCheckState; begin - // Recapturing means we want the tree to paint the new part into our back bitmap instead to the screen. - if Visible then - begin - // Create the minimum rectangle to be recaptured. - MapWindowPoints(Tree.Handle, 0, R, 2); - DragRect := GetDragImageRect; - IntersectRect(R, R, DragRect); + if Assigned(FOnBeforeGetCheckState) then + FOnBeforeGetCheckState(Self, Node); - OffsetRgn(VisibleRegion, -DragRect.Left, -DragRect.Top); + Result := Node.CheckState; +end; - // The target position for painting in the drag image is relative and can be determined from screen coordinates too. - PaintTarget.X := R.Left - DragRect.Left; - PaintTarget.Y := R.Top - DragRect.Top; +//---------------------------------------------------------------------------------------------------------------------- - // The source rectangle is determined by the offsets in the tree. - MapWindowPoints(0, Tree.Handle, R, 2); - OffsetRect(R, -Tree.FOffsetX, -Tree.FOffsetY); +function TBaseVirtualTree.GetCheckType(Node: PVirtualNode): TCheckType; - // Finally let the tree paint the relevant part and upate the drag image on screen. - PaintOptions := [poBackground, poColumnColor, poDrawFocusRect, poDrawDropMark, poDrawSelection, poGridLines]; - with FBackImage do - begin - ClipRect.TopLeft := PaintTarget; - ClipRect.Right := ClipRect.Left + R.Right - R.Left; - ClipRect.Bottom := ClipRect.Top + R.Bottom - R.Top; - // TODO: somehow with clipping, the background image is not drawn on the - // backup image. Need to be diagnosed and fixed. For now, we have coded - // a work around in DragTo where this is used by using the condition - // IsInHeader. (found when solving issue 248) - ClipCanvas(Canvas, ClipRect, VisibleRegion); - Tree.PaintTree(Canvas, R, PaintTarget, PaintOptions); +begin + Result := Node.CheckType; +end; - if CaptureNCArea then - begin - // Header is painted in this part only so when you use this routine and want - // to capture the header in backup image, this flag should be ON. - // For the non-client area we only need the visible region of the window as limit for painting. - SelectClipRgn(Canvas.Handle, VisibleRegion); - // Since WM_PRINT cannot be given a position where to draw we simply move the window origin and - // get the same effect. - GetWindowRect(Tree.Handle, ClipRect); - SetCanvasOrigin(Canvas, DragRect.Left - ClipRect.Left, DragRect.Top - ClipRect.Top); - Tree.Perform(WM_PRINT, WPARAM(Canvas.Handle), PRF_NONCLIENT); - SetCanvasOrigin(Canvas, 0, 0); - end; - SelectClipRgn(Canvas.Handle, 0); +//---------------------------------------------------------------------------------------------------------------------- - if ReshowDragImage then - begin - GDIFlush; - ScreenDC := GetDC(0); - try - InternalShowDragImage(ScreenDC); - finally - ReleaseDC(0, ScreenDC); - end; - end; - end; - end; +function TBaseVirtualTree.GetChildCount(Node: PVirtualNode): Cardinal; +begin + if (Node = nil) or (Node = FRoot) then + Exit(FRoot.ChildCount); + if not GetChildrenInitialized(Node) then + InitChildren(Node); + Exit(Node.ChildCount); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTDragImage.ShowDragImage; +function TBaseVirtualTree.GetChildrenInitialized(Node: PVirtualNode): Boolean; + +begin + Result := not (vsHasChildren in Node.States) or (Node.ChildCount > 0); +end; + +//---------------------------------------------------------------------------------------------------------------------- -// Shows the drag image after it has been hidden by HideDragImage. -// Note: there might be a new background now. -// Also this method does nothing if the system manages the drag image. +function TBaseVirtualTree.GetCutCopyCount: Integer; var - ScreenDC: HDC; + Node: PVirtualNode; begin - if FStates * [disInDrag, disHidden, disPrepared, disSystemSupport] = [disInDrag, disHidden, disPrepared] then + Result := 0; + Node := GetFirstCutCopy; + while Assigned(Node) do begin - Exclude(FStates, disHidden); + Inc(Result); + Node := GetNextCutCopy(Node); + end; +end; - GDIFlush; - ScreenDC := GetDC(0); - try - BitBlt(FBackImage.Canvas.Handle, 0, 0, FBackImage.Width, FBackImage.Height, ScreenDC, FImagePosition.X, - FImagePosition.Y, SRCCOPY); +//---------------------------------------------------------------------------------------------------------------------- - InternalShowDragImage(ScreenDC); - finally - ReleaseDC(0, ScreenDC); - end; - end; +function TBaseVirtualTree.GetDisabled(Node: PVirtualNode): Boolean; + +begin + Result := Assigned(Node) and (vsDisabled in Node.States); +end; + +//---------------------------------------------------------------------------------------------------------------------- +// whether the sync of checkbox with selection is allowed for this node +function TBaseVirtualTree.GetSyncCheckstateWithSelection(Node: PVirtualNode): Boolean; + +begin + Result := (toSyncCheckboxesWithSelection in FOptions.SelectionOptions) + and (toCheckSupport in FOptions.MiscOptions) + and Assigned(FCheckImages) + and (Node.CheckType = ctCheckBox); ; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTDragImage.WillMove(P: TPoint): Boolean; +function TBaseVirtualTree.GetDragManager: IVTDragManager; -// This method determines whether the drag image would "physically" move when DragTo would be called with the same -// target point. -// Always returns False if the system drag image support is available. +// Returns the internal drag manager interface. If this does not yet exist then it is created here. begin - Result := Visible; - if Result then + if FDragManager = nil then begin - // Determine distances to move the drag image. Take care for restrictions. - case FRestriction of - dmrHorizontalOnly: - Result := FLastPosition.X <> P.X; - dmrVerticalOnly: - Result := FLastPosition.Y <> P.Y; - else // dmrNone - Result := (FLastPosition.X <> P.X) or (FLastPosition.Y <> P.Y); - end; + FDragManager := DoCreateDragManager; + if FDragManager = nil then + FDragManager := TVTDragManager.Create(Self) as IVTDragManager; end; + + Result := FDragManager; end; -//----------------- TVTVirtualNodeEnumerator --------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- -function TVTVirtualNodeEnumerator.GetCurrent: PVirtualNode; +function TBaseVirtualTree.GetExpanded(Node: PVirtualNode): Boolean; begin - Result := FNode; + if Assigned(Node) then + Result := vsExpanded in Node.States + else + Result := False; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTVirtualNodeEnumerator.MoveNext: Boolean; +function TBaseVirtualTree.GetFiltered(Node: PVirtualNode): Boolean; begin - Result := FCanMoveNext; - if Result then - begin - FNode := FEnumeration.GetNext(FNode); - Result := FNode <> nil; - FCanMoveNext := Result; - end; + Result := vsFiltered in Node.States; end; -//----------------- TVTVirtualNodeEnumeration -------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- -function TVTVirtualNodeEnumeration.GetEnumerator: TVTVirtualNodeEnumerator; +function TBaseVirtualTree.GetFullyVisible(Node: PVirtualNode): Boolean; + +// Determines whether the given node has the visibility flag set as well as all its parents are expanded. begin - Result.FNode := nil; - Result.FCanMoveNext := True; - Result.FEnumeration := @Self; + Assert(Assigned(Node), 'Invalid parameter.'); + Result := vsVisible in Node.States; + if Result and (Node <> FRoot) then + Result := VisiblePath[Node]; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTVirtualNodeEnumeration.GetNext(Node: PVirtualNode): PVirtualNode; +function TBaseVirtualTree.GetHasChildren(Node: PVirtualNode): Boolean; + begin - case FMode of - vneAll: - if Node = nil then - Result := FTree.GetFirst(FConsiderChildrenAbove) - else - Result := FTree.GetNext(Node, FConsiderChildrenAbove); - - vneChecked: - if Node = nil then - Result := FTree.GetFirstChecked(FState, FConsiderChildrenAbove) - else - Result := FTree.GetNextChecked(Node, FState, FConsiderChildrenAbove); - - vneChild: - if Node = nil then - Result := FTree.GetFirstChild(FNode) - else - Result := FTree.GetNextSibling(Node); - - vneCutCopy: - if Node = nil then - Result := FTree.GetFirstCutCopy(FConsiderChildrenAbove) - else - Result := FTree.GetNextCutCopy(Node, FConsiderChildrenAbove); - - vneInitialized: - if Node = nil then - Result := FTree.GetFirstInitialized(FConsiderChildrenAbove) - else - Result := FTree.GetNextInitialized(Node, FConsiderChildrenAbove); - - vneLeaf: - if Node = nil then - Result := FTree.GetFirstLeaf - else - Result := FTree.GetNextLeaf(Node); - - vneLevel: - if Node = nil then - Result := FTree.GetFirstLevel(FNodeLevel) - else - Result := FTree.GetNextLevel(Node, FNodeLevel); - - vneNoInit: - if Node = nil then - Result := FTree.GetFirstNoInit(FConsiderChildrenAbove) - else - Result := FTree.GetNextNoInit(Node, FConsiderChildrenAbove); - - vneSelected: - if Node = nil then - Result := FTree.GetFirstSelected(FConsiderChildrenAbove) - else - Result := FTree.GetNextSelected(Node, FConsiderChildrenAbove); - - vneVisible: - begin - if Node = nil then - begin - Result := FTree.GetFirstVisible(FNode, FConsiderChildrenAbove, FIncludeFiltered); - if FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result] then - Exit; - end; - repeat - Result := FTree.GetNextVisible(Node{, FConsiderChildrenAbove}); - until not Assigned(Result) or FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result]; - end; - - vneVisibleChild: - if Node = nil then - Result := FTree.GetFirstVisibleChild(FNode, FIncludeFiltered) - else - Result := FTree.GetNextVisibleSibling(Node, FIncludeFiltered); - - vneVisibleNoInitChild: - if Node = nil then - Result := FTree.GetFirstVisibleChildNoInit(FNode, FIncludeFiltered) - else - Result := FTree.GetNextVisibleSiblingNoInit(Node, FIncludeFiltered); - - vneVisibleNoInit: - begin - if Node = nil then - begin - Result := FTree.GetFirstVisibleNoInit(FNode, FConsiderChildrenAbove, FIncludeFiltered); - if FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result] then - Exit; - end; - repeat - Result := FTree.GetNextVisibleNoInit(Node, FConsiderChildrenAbove); - until not Assigned(Result) or FIncludeFiltered or not FTree.IsEffectivelyFiltered[Result]; - end; + if Assigned(Node) then + Result := vsHasChildren in Node.States else - Result := nil; - end; + Result := vsHasChildren in FRoot.States; end; -//----------------- TVirtualTreeColumn --------------------------------------------------------------------------------- - -constructor TVirtualTreeColumn.Create(Collection: TCollection); - -begin - FMinWidth := 10; - FMaxWidth := 10000; - FImageIndex := -1; - FMargin := 4; - FSpacing := cDefaultColumnSpacing; - FText := ''; - FOptions := DefaultColumnOptions; - FAlignment := taLeftJustify; - FBiDiMode := bdLeftToRight; - FColor := clWindow; - FLayout := blGlyphLeft; - FBonusPixel := False; - FCaptionAlignment := taLeftJustify; - FCheckType := ctCheckBox; - FCheckState := csUncheckedNormal; - FCheckBox := False; - FHasImage := False; - FDefaultSortDirection := sdAscending; - fEditNextColumn := -1; - - inherited Create(Collection); +//---------------------------------------------------------------------------------------------------------------------- - if Assigned(Owner) then begin - FWidth := Owner.FDefaultWidth; - FLastWidth := Owner.FDefaultWidth; - FPosition := Owner.Count - 1; - end; -end; +function TBaseVirtualTree.GetMultiline(Node: PVirtualNode): Boolean; -procedure TVirtualTreeColumn.SetCollection(Value: TCollection); begin - inherited; - // Read parent bidi mode and color values as default values. - ParentBiDiModeChanged; - ParentColorChanged; + Result := Assigned(Node) and (Node <> FRoot) and (vsMultiline in Node.States); end; //---------------------------------------------------------------------------------------------------------------------- -destructor TVirtualTreeColumn.Destroy; - -var - I: Integer; - - //--------------- local function --------------------------------------------- - - procedure AdjustColumnIndex(var ColumnIndex: TColumnIndex); - - begin - if Index = ColumnIndex then - ColumnIndex := NoColumn - else - if Index < ColumnIndex then - Dec(ColumnIndex); - end; - - //--------------- end local function ----------------------------------------- +function TBaseVirtualTree.GetNodeHeight(Node: PVirtualNode): Cardinal; begin - // Check if this column is somehow referenced by its collection parent or the header. - with Owner do + if Assigned(Node) and (Node <> FRoot) then begin - // If the columns collection object is currently deleting all columns - // then we don't need to check the various cached indices individually. - if not FClearing then + if (toVariableNodeHeight in FOptions.MiscOptions) and not (vsDeleting in Node.States) then begin - Header.Treeview.CancelEditNode; - IndexChanged(Index, -1); - - AdjustColumnIndex(FHoverIndex); - AdjustColumnIndex(FDownIndex); - AdjustColumnIndex(FTrackIndex); - AdjustColumnIndex(FClickIndex); + if not (vsInitialized in Node.States) then + InitNode(Node); - with Header do - begin - AdjustColumnIndex(FAutoSizeIndex); - if Index = FMainColumn then - begin - // If the current main column is about to be destroyed then we have to find a new main column. - FMainColumn := NoColumn; - for I := 0 to Count - 1 do - if I <> Index then - begin - FMainColumn := I; - Break; - end; - end; - AdjustColumnIndex(FSortColumn); - end; + // Ensure the node's height is determined. + MeasureItemHeight(Self.Canvas, Node); end; - end; - - inherited; + Result := Node.NodeHeight; + end + else + Result := 0; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumn.GetCaptionAlignment: TAlignment; +function TBaseVirtualTree.GetNodeParent(Node: PVirtualNode): PVirtualNode; begin - if coUseCaptionAlignment in FOptions then - Result := FCaptionAlignment + if Assigned(Node) and (Node.Parent <> FRoot) then + Result := Node.Parent else - Result := FAlignment; + Result := nil; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumn.GetCaptionWidth: TDimension; +function TBaseVirtualTree.GetOffset(pElement: TVTElement; pNode: PVirtualNode): integer; +// Calculates the offset of the given element var - Theme: HTHEME; - AdvancedOwnerDraw: Boolean; - PaintInfo: THeaderPaintInfo; - RequestedElements: THeaderPaintElements; - - TextSize: TSize; - HeaderGlyphSize: TPoint; - UseText: Boolean; - R: TRect; + lOffsets: TVTOffsets; begin - AdvancedOwnerDraw := (hoOwnerDraw in Owner.Header.Options) and Assigned(Owner.Header.Treeview.FOnAdvancedHeaderDraw) and Assigned(Owner.Header.Treeview.FOnHeaderDrawQueryElements) and - not(csDesigning in Owner.Header.Treeview.ComponentState); - - PaintInfo.Column := Self; - PaintInfo.TargetCanvas := Owner.FHeaderBitmap.Canvas; - - with PaintInfo, Column do - begin - ShowHeaderGlyph := (hoShowImages in Owner.Header.Options) and ((Assigned(Owner.Header.Images) and (FImageIndex > -1)) or FCheckBox); - ShowSortGlyph := ((Owner.Header.FSortColumn > -1) and (Self = Owner.Items[Owner.Header.SortColumn])) and (hoShowSortGlyphs in Owner.Header.Options); + GetOffsets(pNode, lOffsets, pElement); + Exit(lOffsets[pElement]); +end; - // This path for text columns or advanced owner draw. - // See if the application wants to draw part of the header itself. - RequestedElements := []; - if AdvancedOwnerDraw then - begin - PaintInfo.Column := Self; - Owner.Header.Treeview.DoHeaderDrawQueryElements(PaintInfo, RequestedElements); - end; - end; +procedure TBaseVirtualTree.GetOffsets(pNode: PVirtualNode; out pOffsets: TVTOffsets; pElement: TVTElement = TVTElement.ofsEndOfClientArea; pColumn: Integer = NoColumn); +// Calculates the offset up to the given element and supplies them in an array. +var + lNodeLevel: Integer; +begin + // If no specific column was given, assume the main column + if pColumn = -1 then + pColumn := Header.MainColumn; - UseText := Length(FText) > 0; - // If nothing is to show then don't waste time with useless preparation. - if not(UseText or PaintInfo.ShowHeaderGlyph or PaintInfo.ShowSortGlyph) then - Exit(0); + // Left Margin + pOffsets[TVTElement.ofsMargin] := FMargin; + if pElement = ofsMargin then + exit; + pOffsets[TVTElement.ofsCheckBox] := FMargin + fImagesMargin; + if (pColumn = Header.MainColumn) then + begin + if not (toFixedIndent in TreeOptions.PaintOptions) then begin + // plus Indent + lNodeLevel := GetNodeLevel(pNode); + if toShowRoot in FOptions.PaintOptions then + Inc(lNodeLevel); + end + else + lNodeLevel := 1; + Inc(pOffsets[TVTElement.ofsCheckBox], lNodeLevel * Integer(FIndent)); + // toggle buttons + pOffsets[TVTElement.ofsToggleButton] := pOffsets[TVTElement.ofsCheckBox] - fImagesMargin - ((Integer(FIndent) - FPlusBM.Width) div 2) + 1 - FPlusBM.Width; //Compare PaintTree() relative line 107 + end;//if MainColumn - // Calculate sizes of the involved items. - with Owner, Header do - begin - if PaintInfo.ShowHeaderGlyph then - if not FCheckBox then - begin - if Assigned(FImages) then - HeaderGlyphSize := Point(FImages.Width, FImages.Height); - end - else - with Self.Owner.Header.Treeview do - begin - if Assigned(FCheckImages) then - HeaderGlyphSize := Point(FCheckImages.Width, FCheckImages.Height); - end - else - HeaderGlyphSize := Point(0, 0); - if PaintInfo.ShowSortGlyph then - begin - if tsUseExplorerTheme in FHeader.Treeview.FStates then - begin - R := Rect(0, 0, 100, 100); - Theme := OpenThemeData(FHeader.Treeview.Handle, 'HEADER'); - GetThemePartSize(Theme, PaintInfo.TargetCanvas.Handle, HP_HEADERSORTARROW, HSAS_SORTEDUP, @R, TS_TRUE, PaintInfo.SortGlyphSize); - CloseThemeData(Theme); - end - else - begin - PaintInfo.SortGlyphSize.cx := Header.Treeview.ScaledPixels(16); - PaintInfo.SortGlyphSize.cy := Header.Treeview.ScaledPixels(4); - end; - end - else - begin - PaintInfo.SortGlyphSize.cx := 0; - PaintInfo.SortGlyphSize.cy := 0; - end; - end; + // The area in which the toggle buttons are painted must have exactly the size of one indent level + if pElement <= TVTElement.ofsCheckBox then + exit; - if UseText then - begin - GetTextExtentPoint32W(PaintInfo.TargetCanvas.Handle, PWideChar(FText), Length(FText), TextSize); - Inc(TextSize.cx, 2); - end + // right of checkbox, left of state image + if (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages) and (pNode.CheckType <> ctNone) and (pColumn = Header.MainColumn) then + pOffsets[TVTElement.ofsStateImage] := pOffsets[TVTElement.ofsCheckBox] + FCheckImages.Width + fImagesMargin else - begin - TextSize.cx := 0; - TextSize.cy := 0; - end; + pOffsets[TVTElement.ofsStateImage] := pOffsets[TVTElement.ofsCheckBox]; + if pElement = TVTElement.ofsStateImage then + exit; + // right of left image, left of normal image + pOffsets[TVTElement.ofsImage] := pOffsets[TVTElement.ofsStateImage] + GetImageSize(pNode, TVTImageKind.ikState, pColumn).cx; + if pElement = TVTElement.ofsImage then + exit; + // label + pOffsets[TVTElement.ofsLabel] := pOffsets[TVTElement.ofsImage] + GetImageSize(pNode, TVTImageKind.ikNormal, pColumn).cx; + pOffsets[TVTElement.ofsText] := pOffsets[TVTElement.ofsLabel] + FTextMargin; + Dec(pOffsets[TVTElement.ofsText]); //TODO: This should no longer be necessary once issue #369 is resolved. + if pElement <= TVTElement.ofsText then + exit; - // if CalculateTextRect then - Result := TextSize.cx; - if PaintInfo.ShowHeaderGlyph then - if Layout in [blGlyphLeft, blGlyphRight] then - Inc(Result, HeaderGlyphSize.X + FSpacing) - else // if Layout in [ blGlyphTop, blGlyphBottom] then - Result := Max(Result, HeaderGlyphSize.X); - if PaintInfo.ShowSortGlyph then - Inc(Result, PaintInfo.SortGlyphSize.cx + FSpacing + 2); // without this +2, there is a slight movement of the sort glyph when expanding the column + // End of text + pOffsets[TVTElement.ofsRightOfText] := pOffsets[TVTElement.ofsText] + DoGetNodeWidth(pNode, pColumn) + DoGetNodeExtraWidth(pNode, pColumn); + // end of client area + pOffsets[TVTElement.ofsEndOfClientArea] := Max(FRangeX, ClientWidth) - FTextMargin; end; -//---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumn.GetLeft: Integer; +function TBaseVirtualTree.GetOffsetXY: TPoint; begin - Result := FLeft; - if [coVisible, coFixed] * FOptions <> [coVisible, coFixed] then - Dec(Result, Owner.Header.Treeview.FEffectiveOffsetX); + Result := Point(FOffsetX, FOffsetY); end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumn.IsBiDiModeStored: Boolean; - +function TBaseVirtualTree.GetRangeX: Cardinal; begin - Result := not (coParentBiDiMode in FOptions); + Result := Max(0, FRangeX); end; -//---------------------------------------------------------------------------------------------------------------------- - -function TVirtualTreeColumn.IsCaptionAlignmentStored: Boolean; +function TBaseVirtualTree.GetRootNodeCount: Cardinal; begin - Result := coUseCaptionAlignment in FOptions; + Result := FRoot.ChildCount; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumn.IsColorStored: Boolean; +function TBaseVirtualTree.GetSelected(Node: PVirtualNode): Boolean; begin - Result := not (coParentColor in FOptions); + Result := Assigned(Node) and (vsSelected in Node.States); end; -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.SetAlignment(const Value: TAlignment); - +function TBaseVirtualTree.GetSelectedData: TArray; +var + lItem: PVirtualNode; + i: Integer; begin - if FAlignment <> Value then + SetLEngth(Result, Self.SelectedCount); + i := 0; + lItem := Self.GetFirstSelected; + while Assigned(lItem) do begin - FAlignment := Value; - Changed(False); - // Setting the alignment affects also the tree, hence invalidate it too. - Owner.Header.TreeView.Invalidate; + Result[i] := Self.GetNodeData(lItem); + lItem := Self.GetNextSelected(lItem); + Inc(i); end; + SetLength(Result, i); // See issue #927, SelectedCount may not yet be updated. end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetBiDiMode(Value: TBiDiMode); +function TBaseVirtualTree.GetTopNode: PVirtualNode; + +var + Dummy: Integer; begin - if Value <> FBiDiMode then - begin - FBiDiMode := Value; - Exclude(FOptions, coParentBiDiMode); - Changed(False); - // Setting the alignment affects also the tree, hence invalidate it too. - Owner.Header.TreeView.Invalidate; - end; + Result := GetNodeAt(0, 0, True, Dummy); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetCaptionAlignment(const Value: TAlignment); +function TBaseVirtualTree.GetTotalCount(): Cardinal; begin - if not (coUseCaptionAlignment in FOptions) or (FCaptionAlignment <> Value) then - begin - FCaptionAlignment := Value; - Include(FOptions, coUseCaptionAlignment); - // Setting the alignment affects also the tree, hence invalidate it too. - Owner.Header.Invalidate(Self); + Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be used in UI thread.'); // FUpdateCount is not thread-safe! So do not write it in non-UI threads. + Inc(FUpdateCount); + try + ValidateNode(FRoot, True); + finally + Dec(FUpdateCount); end; + // The root node itself doesn't count as node. + Result := FRoot.TotalCount - 1; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetColor(const Value: TColor); - -begin - if FColor <> Value then - begin - FColor := Value; - Exclude(FOptions, coParentColor); - Exclude(FOptions, coStyleColor); // Issue #919 - Changed(False); - Owner.Header.TreeView.Invalidate; - end; -end; - -function TVirtualTreeColumn.GetEffectiveColor(): TColor; -// Returns the color that should effectively be used as background color for this -// column considering all flags in the TVirtualTreeColumn.Options property +function TBaseVirtualTree.GetVclStyleEnabled: Boolean; begin - if (coParentColor in Options) or ((coStyleColor in Options) and Owner.Header.TreeView.VclStyleEnabled) then - Result := Owner.Header.TreeView.FColors.BackGroundColor - else - Result := Self.Color; + Exit(FVclStyleEnabled); end; -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.SetCheckBox(Value: Boolean); +function TBaseVirtualTree.GetVerticalAlignment(Node: PVirtualNode): Byte; begin - if Value <> FCheckBox then - begin - FCheckBox := Value; - if Value and (csDesigning in Owner.Header.Treeview.ComponentState) then - Owner.Header.Options := Owner.Header.Options + [hoShowImages]; - Changed(False); - end; + Result := Node.Align; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetCheckState(Value: TCheckState); +function TBaseVirtualTree.GetVisible(Node: PVirtualNode): Boolean; -begin - if Value <> FCheckState then - begin - FCheckState := Value; - Changed(False); - end; -end; +// Determines if the given node is marked as being visible. -//---------------------------------------------------------------------------------------------------------------------- +begin + if Node = nil then + Node := FRoot; -procedure TVirtualTreeColumn.SetCheckType(Value: TCheckType); + if not (vsInitialized in Node.States) then + InitNode(Node); -begin - if Value <> FCheckType then - begin - FCheckType := Value; - Changed(False); - end; + Result := vsVisible in Node.States; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetImageIndex(Value: TImageIndex); +function TBaseVirtualTree.GetVisiblePath(Node: PVirtualNode): Boolean; -begin - if Value <> FImageIndex then - begin - FImageIndex := Value; - Changed(False); - end; -end; +// Determines if all parents of the given node are expanded and have the visibility flag set. -//---------------------------------------------------------------------------------------------------------------------- +begin + Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameters.'); -procedure TVirtualTreeColumn.SetLayout(Value: TVTHeaderColumnLayout); + // FRoot is always expanded + repeat + Node := Node.Parent; + until (Node = FRoot) or not (vsExpanded in Node.States) or not (vsVisible in Node.States); -begin - if FLayout <> Value then - begin - FLayout := Value; - Changed(False); - end; + Result := Node = FRoot; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetMargin(Value: Integer); +procedure TBaseVirtualTree.HandleClickSelection(LastFocused, NewNode: PVirtualNode; Shift: TShiftState; + DragPending: Boolean); + +// Handles multi-selection with mouse click. begin - // Compatibility setting for -1. - if Value < 0 then - Value := 4; - if FMargin <> Value then + // Ctrl key down + if ssCtrl in Shift then begin - FMargin := Value; - Changed(False); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.SetMaxWidth(Value: Integer); + if ssShift in Shift then + begin + SelectNodes(FRangeAnchor, NewNode, True); + end + else + begin + if not (toSiblingSelectConstraint in FOptions.SelectionOptions) then + FRangeAnchor := NewNode; + // Delay selection change if a drag operation is pending. + // Otherwise switch selection state here. + if DragPending then + DoStateChange([tsToggleFocusedSelection]) + else + if vsSelected in NewNode.States then + RemoveFromSelection(NewNode) + else + AddToSelection(NewNode, True); + end; + end + else + // Shift key down + if ssShift in Shift then + begin + if FRangeAnchor = nil then + FRangeAnchor := FRoot.FirstChild; -begin - if Value < FMinWidth then - Value := FMinWidth; - FMaxWidth := Value; - SetWidth(FWidth); + // select node range + if Assigned(FRangeAnchor) then + begin + SelectNodes(FRangeAnchor, NewNode, False); + Invalidate; + end; + end + else + begin + // any other case + if not (vsSelected in NewNode.States) then + AddToSelection(NewNode, True); + // assign new reference item + FRangeAnchor := NewNode; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetMinWidth(Value: Integer); - -begin - if Value < 0 then - Value := 0; - if Value > FMaxWidth then - Value := FMaxWidth; - FMinWidth := Value; - SetWidth(FWidth); -end; - -//---------------------------------------------------------------------------------------------------------------------- +function TBaseVirtualTree.HandleDrawSelection(X, Y: Integer): Boolean; -procedure TVirtualTreeColumn.SetOptions(Value: TVTColumnOptions); +// Handles multi-selection with a focus rectangle. +// Result is True if something changed in selection. var - ToBeSet, - ToBeCleared: TVTColumnOptions; - VisibleChanged, - lParentColorSet: Boolean; - lTreeView: TBaseVirtualTree; -begin - if FOptions <> Value then - begin - ToBeCleared := FOptions - Value; - ToBeSet := Value - FOptions; + OldRect, + NewRect: TRect; + MainColumn: TColumnIndex; + MaxValue: Integer; - FOptions := Value; + // limits of a node and its text + NodeLeft, + NodeRight: Integer; - VisibleChanged := coVisible in (ToBeSet + ToBeCleared); - lParentColorSet := coParentColor in ToBeSet; + // alignment and directionality + CurrentBidiMode: TBidiMode; + CurrentAlignment: TAlignment; - if coParentBidiMode in ToBeSet then - ParentBiDiModeChanged; - if lParentColorSet then begin - Include(FOptions, coStyleColor);// Issue #919 - ParentColorChanged(); - end; +begin + Result := False; - if coAutoSpring in ToBeSet then - FSpringRest := 0; + // Selection changes are only done if the user drew a selection rectangle large + // enough to exceed the threshold. + if (FRoot.TotalCount > 1) and (tsDrawSelecting in FStates) then + begin + // Effective handling of node selection is done by using two rectangles stored in FSelectRec. + OldRect := OrderRect(FLastSelRect); + NewRect := OrderRect(FNewSelRect); + ClearTempCache; - if coVisible in ToBeCleared then - Owner.Header.UpdateMainColumn(); // Fixes issue #946 + MainColumn := FHeader.MainColumn; - if ((coFixed in ToBeSet) or (coFixed in ToBeCleared)) and (coVisible in FOptions) then - Owner.Header.RescaleHeader; + // Alignment and bidi mode determine where the node text is located within a node. + if MainColumn <= NoColumn then + begin + CurrentBidiMode := BidiMode; + CurrentAlignment := Alignment; + end + else + begin + CurrentBidiMode := FHeader.Columns[MainColumn].BidiMode; + CurrentAlignment := FHeader.Columns[MainColumn].Alignment; + end; - Changed(False); - // Need to repaint and adjust the owner tree too. - lTreeView := Owner.Header.Treeview; - if not (csLoading in lTreeview.ComponentState) and (VisibleChanged or lParentColorSet) and (Owner.UpdateCount = 0) and lTreeView.HandleAllocated then + // Determine initial left border of first node (take column reordering into account). + if FHeader.UseColumns then begin - lTreeview.Invalidate(); - if VisibleChanged then begin - lTreeview.DoColumnVisibilityChanged(Self.Index, coVisible in ToBeSet); - lTreeview.UpdateHorizontalScrollBar(False); - end; + // The mouse coordinates don't include any horizontal scrolling hence take this also + // out from the returned column position. + NodeLeft := FHeader.Columns[MainColumn].Left + FEffectiveOffsetX; + NodeRight := NodeLeft + FHeader.Columns[MainColumn].Width; + end + else + begin + NodeLeft := 0 + FEffectiveOffsetX; + NodeRight := NodeLeft + ClientWidth; end; + if CurrentBidiMode = bdLeftToRight then + Result := CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRight, CurrentAlignment, OldRect, NewRect) + else + Result := CollectSelectedNodesRTL(MainColumn, NodeLeft, NodeRight, CurrentAlignment, OldRect, NewRect); end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.SetPosition(Value: TColumnPosition); - -var - Temp: TColumnIndex; -begin - if (csLoading in Owner.Header.Treeview.ComponentState) or (Owner.UpdateCount > 0) then - // Only cache the position for final fixup when loading from DFM. - FPosition := Value - else + if Result then begin - if Value >= TColumnPosition(Collection.Count) then - Value := Collection.Count - 1; - if FPosition <> Value then + // Do some housekeeping if there was a change. + MaxValue := PackArray(FSelection, FSelectionCount); + if MaxValue > -1 then + begin + FSelectionCount := MaxValue; + SetLength(FSelection, FSelectionCount); + end; + if FTempNodeCount > 0 then begin - with Owner do + if tsClearOnNewSelection in fStates then begin - InitializePositionArray; - Header.Treeview.CancelEditNode; - AdjustPosition(Self, Value); - Self.Changed(False); - - // Need to repaint. - with Header do - begin - if (UpdateCount = 0) and Treeview.HandleAllocated then - begin - Invalidate(Self); - Treeview.Invalidate; - end; - end; + DoStateChange([], [tsClearOnNewSelection]); + ClearSelection(False); end; - // If the moved column is now within the fixed columns then we make it fixed as well. If it's not - // we clear the fixed state (in case that fixed column is moved outside fixed area). - if (coFixed in FOptions) and (FPosition > 0) then - Temp := Owner.ColumnFromPosition(FPosition - 1) - else - Temp := Owner.ColumnFromPosition(FPosition + 1); - - if Temp <> NoColumn then - begin - if coFixed in Owner[Temp].Options then - Options := Options + [coFixed] - else - Options := Options - [coFixed]; - end; + AddToSelection(FTempNodeCache, FTempNodeCount); + ClearTempCache; end; + + Change(nil); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetSpacing(Value: Integer); +function TBaseVirtualTree.HasVisibleNextSibling(Node: PVirtualNode): Boolean; + +// Helper method to determine if the given node has a visible next sibling. This is needed to +// draw correct tree lines. begin - if FSpacing <> Value then + // Check if there is a sibling at all. + Result := Assigned(Node.NextSibling); + + if Result then begin - FSpacing := Value; - Changed(False); + repeat + Node := Node.NextSibling; + Result := IsEffectivelyVisible[Node]; + until Result or (Node.NextSibling = nil); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetStyle(Value: TVirtualTreeColumnStyle); +function TBaseVirtualTree.HasVisiblePreviousSibling(Node: PVirtualNode): Boolean; + +// Helper method to determine if the given node has a visible previous sibling. This is needed to +// draw correct tree lines. begin - if FStyle <> Value then + // Check if there is a sibling at all. + Result := Assigned(Node.PrevSibling); + + if Result then begin - FStyle := Value; - Changed(False); + repeat + Node := Node.PrevSibling; + Result := IsEffectivelyVisible[Node]; + until Result or (Node.PrevSibling = nil); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetText(const Value: string); +procedure TBaseVirtualTree.ImageListChange(Sender: TObject); begin - if FText <> Value then - begin - FText := Value; - FCaptionText := ''; - Changed(False); - end; + if not (csDestroying in ComponentState) then + Invalidate; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.SetWidth(Value: Integer); +procedure TBaseVirtualTree.InitializeFirstColumnValues(var PaintInfo: TVTPaintInfo); -var - EffectiveMaxWidth, - EffectiveMinWidth, - TotalFixedMaxWidth, - TotalFixedMinWidth: Integer; - I: TColumnIndex; +// Determines initial index, position and cell size of the first visible column. begin - if not (hsScaling in Owner.Header.States) then - if ([coVisible, coFixed] * FOptions = [coVisible, coFixed]) then + PaintInfo.Column := FHeader.Columns.GetFirstVisibleColumn; + with FHeader.Columns, PaintInfo do + begin + if Column > NoColumn then begin - with Owner, FHeader, FFixedAreaConstraints, TreeView do - begin - TotalFixedMinWidth := 0; - TotalFixedMaxWidth := 0; - for I := 0 to FColumns.Count - 1 do - if ([coVisible, coFixed] * FColumns[I].Options = [coVisible, coFixed]) then - begin - Inc(TotalFixedMaxWidth, FColumns[I].MaxWidth); - Inc(TotalFixedMinWidth, FColumns[I].MinWidth); - end; - - // The percentage values have precedence over the pixel values. - If FMaxWidthPercent > 0 then - TotalFixedMinWidth:= Min((ClientWidth * FMaxWidthPercent) div 100, TotalFixedMinWidth); - If FMinWidthPercent > 0 then - TotalFixedMaxWidth := Max((ClientWidth * FMinWidthPercent) div 100, TotalFixedMaxWidth); - - EffectiveMaxWidth := Min(TotalFixedMaxWidth - (GetVisibleFixedWidth - Self.FWidth), FMaxWidth); - EffectiveMinWidth := Max(TotalFixedMinWidth - (GetVisibleFixedWidth - Self.FWidth), FMinWidth); - Value := Min(Max(Value, EffectiveMinWidth), EffectiveMaxWidth); - - if FMinWidthPercent > 0 then - Value := Max((ClientWidth * FMinWidthPercent) div 100 - GetVisibleFixedWidth + Self.FWidth, Value); - if FMaxWidthPercent > 0 then - Value := Min((ClientWidth * FMaxWidthPercent) div 100 - GetVisibleFixedWidth + Self.FWidth, Value); - end; + CellRect.Right := CellRect.Left + Items[Column].Width; + Position := Items[Column].Position; end else - Value := Min(Max(Value, FMinWidth), FMaxWidth); - - if FWidth <> Value then - begin - FLastWidth := FWidth; - if not (hsResizing in Owner.Header.States) then - FBonusPixel := False; - if not (hoAutoResize in Owner.Header.Options) or (Index <> Owner.Header.AutoSizeIndex) then - begin - FWidth := Value; - Owner.UpdatePositions; - end; - if not (csLoading in Owner.Header.Treeview.ComponentState) and (Owner.UpdateCount = 0) then - begin - if hoAutoResize in Owner.Header.Options then - Owner.AdjustAutoSize(Index); - Owner.Header.Treeview.DoColumnResize(Index); - end; + Position := 0; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.ChangeScale(M, D: TDimension; isDpiChange: Boolean); -begin - FMinWidth := MulDiv(FMinWidth, M, D); - FMaxWidth := MulDiv(FMaxWidth, M, D); - FSpacing := MulDiv(FSpacing, M, D); - Self.Width := MulDiv(Self.Width, M, D); -end; - -procedure TVirtualTreeColumn.ComputeHeaderLayout(var PaintInfo: THeaderPaintInfo; DrawFormat: Cardinal; CalculateTextRect: Boolean = False); +procedure TBaseVirtualTree.InitRecursive(Node: PVirtualNode; Levels: Cardinal = MaxInt; pVisibleOnly: Boolean = True); -// The layout of a column header is determined by a lot of factors. This method takes them all into account and -// determines all necessary positions and bounds: -// - for the header text -// - the header glyph -// - the sort glyph +// Initializes a node and optionally its children up to a certain level. +// The sepcified number of levels are latrive to the givne Node. var - TextSize: TSize; - TextPos, - ClientSize, - HeaderGlyphSize: TPoint; - CurrentAlignment: TAlignment; - MinLeft, - MaxRight, - TextSpacing: Integer; - UseText: Boolean; - R: TRect; - Theme: HTHEME; - + Run: PVirtualNode; begin - UseText := Length(FText) > 0; - // If nothing is to show then don't waste time with useless preparation. - if not (UseText or PaintInfo.ShowHeaderGlyph or PaintInfo.ShowSortGlyph) then - Exit; + if not Assigned(Node) then + Node := FRoot; - CurrentAlignment := CaptionAlignment; - if FBiDiMode <> bdLeftToRight then - ChangeBiDiModeAlignment(CurrentAlignment); + if (Node <> FRoot) and not (vsInitialized in Node.States) then + InitNode(Node); + if (Levels = 0) or (pVisibleOnly and not (vsExpanded in Node.States)) then + exit; + Run := Node.FirstChild; - // Calculate sizes of the involved items. - ClientSize := Point(PaintInfo.PaintRectangle.Right - PaintInfo.PaintRectangle.Left, PaintInfo.PaintRectangle.Bottom - PaintInfo.PaintRectangle.Top); - with Owner, Header do + while Assigned(Run) do begin - if PaintInfo.ShowHeaderGlyph then - if not FCheckBox then - HeaderGlyphSize := Point(FImages.Width, FImages.Height) - else - with Self.Owner.Header.Treeview do - begin - if Assigned(FCheckImages) then - HeaderGlyphSize := Point(FCheckImages.Width, FCheckImages.Height); - end - else - HeaderGlyphSize := Point(0, 0); - if PaintInfo.ShowSortGlyph then - begin - if tsUseExplorerTheme in FHeader.Treeview.FStates then - begin - R := Rect(0, 0, 100, 100); - Theme := OpenThemeData(FHeader.Treeview.Handle, 'HEADER'); - GetThemePartSize(Theme, PaintInfo.TargetCanvas.Handle, HP_HEADERSORTARROW, HSAS_SORTEDUP, @R, TS_TRUE, PaintInfo.SortGlyphSize); - CloseThemeData(Theme); - end - else - begin - PaintInfo.SortGlyphSize.cx := Header.Treeview.ScaledPixels(16); - PaintInfo.SortGlyphSize.cy := Header.Treeview.ScaledPixels(4); - end; - - // In any case, the sort glyph is vertically centered. - PaintInfo.SortGlyphPos.Y := (ClientSize.Y - PaintInfo.SortGlyphSize.cy) div 2; - end - else - begin - PaintInfo.SortGlyphSize.cx := 0; - PaintInfo.SortGlyphSize.cy := 0; - end; + InitRecursive(Run, Levels - 1, pVisibleOnly); + Run := Run.NextSibling; end; +end; - if UseText then - begin - if not (coWrapCaption in FOptions) then - begin - FCaptionText := FText; - GetTextExtentPoint32W(PaintInfo.TargetCanvas.Handle, PWideChar(FText), Length(FText), TextSize); - Inc(TextSize.cx, 2); - PaintInfo.TextRectangle := Rect(0, 0, TextSize.cx, TextSize.cy); - end - else - begin - R := PaintInfo.PaintRectangle; - if FCaptionText = '' then - FCaptionText := WrapString(PaintInfo.TargetCanvas.Handle, FText, R, DT_RTLREADING and DrawFormat <> 0, DrawFormat); +procedure TBaseVirtualTree.InitRootNode(OldSize: Cardinal = 0); - GetStringDrawRect(PaintInfo.TargetCanvas.Handle, FCaptionText, R, DrawFormat); - TextSize.cx := PaintInfo.PaintRectangle.Right - PaintInfo.PaintRectangle.Left; - TextSize.cy := R.Bottom - R.Top; - PaintInfo.TextRectangle := Rect(0, 0, TextSize.cx, TextSize.cy); - end; - TextSpacing := FSpacing; - end - else - begin - TextSpacing := 0; - TextSize.cx := 0; - TextSize.cy := 0; - end; +// Reinitializes the root node. - // Check first for the special case where nothing is shown except the sort glyph. - if PaintInfo.ShowSortGlyph and not (UseText or PaintInfo.ShowHeaderGlyph) then - begin - // Center the sort glyph in the available area if nothing else is there. - PaintInfo.SortGlyphPos := Point((ClientSize.X - PaintInfo.SortGlyphSize.cx) div 2, (ClientSize.Y - PaintInfo.SortGlyphSize.cy) div 2); - end +var + NewSize: Cardinal; + +begin + NewSize := TreeNodeSize + FTotalInternalDataSize; + if FRoot = nil then + FRoot := AllocMem(NewSize) else begin - // Determine extents of text and glyph and calculate positions which are clear from the layout. - if (Layout in [blGlyphLeft, blGlyphRight]) or not PaintInfo.ShowHeaderGlyph then - begin - PaintInfo.GlyphPos.Y := (ClientSize.Y - HeaderGlyphSize.Y) div 2; - // If the text is taller than the given height, perform no vertical centration as this - // would make the text even less readable. - //Using Max() fixes badly positioned text if Extra Large fonts have been activated in the Windows display options - TextPos.Y := Max(-5, (ClientSize.Y - TextSize.cy) div 2); - end - else - begin - if Layout = blGlyphTop then - begin - PaintInfo.GlyphPos.Y := (ClientSize.Y - HeaderGlyphSize.Y - TextSize.cy - TextSpacing) div 2; - TextPos.Y := PaintInfo.GlyphPos.Y + HeaderGlyphSize.Y + TextSpacing; - end - else - begin - TextPos.Y := (ClientSize.Y - HeaderGlyphSize.Y - TextSize.cy - TextSpacing) div 2; - PaintInfo.GlyphPos.Y := TextPos.Y + TextSize.cy + TextSpacing; - end; - end; - - // Each alignment needs special consideration. - case CurrentAlignment of - taLeftJustify: - begin - MinLeft := FMargin; - if PaintInfo.ShowSortGlyph and (FBiDiMode <> bdLeftToRight) then - begin - // In RTL context is the sort glyph placed on the left hand side. - PaintInfo.SortGlyphPos.X := MinLeft; - Inc(MinLeft, PaintInfo.SortGlyphSize.cx + FSpacing); - end; - if Layout in [blGlyphTop, blGlyphBottom] then - begin - // Header glyph is above or below text, so both must be considered when calculating - // the left positition of the sort glyph (if it is on the right hand side). - TextPos.X := MinLeft; - if PaintInfo.ShowHeaderGlyph then - begin - PaintInfo.GlyphPos.X := (ClientSize.X - HeaderGlyphSize.X) div 2; - if PaintInfo.GlyphPos.X < MinLeft then - PaintInfo.GlyphPos.X := MinLeft; - MinLeft := Max(TextPos.X + TextSize.cx + TextSpacing, PaintInfo.GlyphPos.X + HeaderGlyphSize.X + FSpacing); - end - else - MinLeft := TextPos.X + TextSize.cx + TextSpacing; - end - else - begin - // Everything is lined up. TextSpacing might be 0 if there is no text. - // This simplifies the calculation because no extra tests are necessary. - if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphLeft) then - begin - PaintInfo.GlyphPos.X := MinLeft; - Inc(MinLeft, HeaderGlyphSize.X + FSpacing); - end; - TextPos.X := MinLeft; - Inc(MinLeft, TextSize.cx + TextSpacing); - if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphRight) then - begin - PaintInfo.GlyphPos.X := MinLeft; - Inc(MinLeft, HeaderGlyphSize.X + FSpacing); - end; - end; - if PaintInfo.ShowSortGlyph and (FBiDiMode = bdLeftToRight) then - PaintInfo.SortGlyphPos.X := MinLeft; - end; - taCenter: - begin - if Layout in [blGlyphTop, blGlyphBottom] then - begin - PaintInfo.GlyphPos.X := (ClientSize.X - HeaderGlyphSize.X) div 2; - TextPos.X := (ClientSize.X - TextSize.cx) div 2; - if PaintInfo.ShowSortGlyph then - Dec(TextPos.X, PaintInfo.SortGlyphSize.cx div 2); - end - else - begin - MinLeft := (ClientSize.X - HeaderGlyphSize.X - TextSpacing - TextSize.cx) div 2; - if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphLeft) then - begin - PaintInfo.GlyphPos.X := MinLeft; - Inc(MinLeft, HeaderGlyphSize.X + TextSpacing); - end; - TextPos.X := MinLeft; - Inc(MinLeft, TextSize.cx + TextSpacing); - if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphRight) then - PaintInfo.GlyphPos.X := MinLeft; - end; - if PaintInfo.ShowHeaderGlyph then - begin - MinLeft := Min(PaintInfo.GlyphPos.X, TextPos.X); - MaxRight := Max(PaintInfo.GlyphPos.X + HeaderGlyphSize.X, TextPos.X + TextSize.cx); - end - else - begin - MinLeft := TextPos.X; - MaxRight := TextPos.X + TextSize.cx; - end; - // Place the sort glyph directly to the left or right of the larger item. - if PaintInfo.ShowSortGlyph then - if FBiDiMode = bdLeftToRight then - begin - // Sort glyph on the right hand side. - PaintInfo.SortGlyphPos.X := MaxRight + FSpacing; - end - else - begin - // Sort glyph on the left hand side. - PaintInfo.SortGlyphPos.X := MinLeft - FSpacing - PaintInfo.SortGlyphSize.cx; - end; - end; - else - // taRightJustify - MaxRight := ClientSize.X - FMargin; - if PaintInfo.ShowSortGlyph and (FBiDiMode = bdLeftToRight) then - begin - // In LTR context is the sort glyph placed on the right hand side. - Dec(MaxRight, PaintInfo.SortGlyphSize.cx); - PaintInfo.SortGlyphPos.X := MaxRight; - Dec(MaxRight, FSpacing); - end; - if Layout in [blGlyphTop, blGlyphBottom] then - begin - TextPos.X := MaxRight - TextSize.cx; - if PaintInfo.ShowHeaderGlyph then - begin - PaintInfo.GlyphPos.X := (ClientSize.X - HeaderGlyphSize.X) div 2; - if PaintInfo.GlyphPos.X + HeaderGlyphSize.X + FSpacing > MaxRight then - PaintInfo.GlyphPos.X := MaxRight - HeaderGlyphSize.X - FSpacing; - MaxRight := Min(TextPos.X - TextSpacing, PaintInfo.GlyphPos.X - FSpacing); - end - else - MaxRight := TextPos.X - TextSpacing; - end - else - begin - // Everything is lined up. TextSpacing might be 0 if there is no text. - // This simplifies the calculation because no extra tests are necessary. - if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphRight) then - begin - PaintInfo.GlyphPos.X := MaxRight - HeaderGlyphSize.X; - MaxRight := PaintInfo.GlyphPos.X - FSpacing; - end; - TextPos.X := MaxRight - TextSize.cx; - MaxRight := TextPos.X - TextSpacing; - if PaintInfo.ShowHeaderGlyph and (Layout = blGlyphLeft) then - begin - PaintInfo.GlyphPos.X := MaxRight - HeaderGlyphSize.X; - MaxRight := PaintInfo.GlyphPos.X - FSpacing; - end; - end; - if PaintInfo.ShowSortGlyph and (FBiDiMode <> bdLeftToRight) then - PaintInfo.SortGlyphPos.X := MaxRight - PaintInfo.SortGlyphSize.cx; - end; + ReallocMem(FRoot, NewSize); + ZeroMemory(PByte(FRoot) + OldSize, NewSize - OldSize); end; - // Once the position of each element is determined there remains only one but important step. - // The horizontal positions of every element must be adjusted so that it always fits into the - // given header area. This is accomplished by shorten the text appropriately. - - // These are the maximum bounds. Nothing goes beyond them. - MinLeft := FMargin; - MaxRight := ClientSize.X - FMargin; - if PaintInfo.ShowSortGlyph then - begin - if FBiDiMode = bdLeftToRight then - begin - // Sort glyph on the right hand side. - if PaintInfo.SortGlyphPos.X + PaintInfo.SortGlyphSize.cx > MaxRight then - PaintInfo.SortGlyphPos.X := MaxRight - PaintInfo.SortGlyphSize.cx; - MaxRight := PaintInfo.SortGlyphPos.X - FSpacing; - end; - - // Consider also the left side of the sort glyph regardless of the bidi mode. - if PaintInfo.SortGlyphPos.X < MinLeft then - PaintInfo.SortGlyphPos.X := MinLeft; - // Left border needs only adjustment if the sort glyph marks the left border. - if FBiDiMode <> bdLeftToRight then - MinLeft := PaintInfo.SortGlyphPos.X + PaintInfo.SortGlyphSize.cx + FSpacing; - - // Finally transform sort glyph to its actual position. - Inc(PaintInfo.SortGlyphPos.X, PaintInfo.PaintRectangle.Left); - Inc(PaintInfo.SortGlyphPos.Y, PaintInfo.PaintRectangle.Top); - end; - if PaintInfo.ShowHeaderGlyph then - begin - if PaintInfo.GlyphPos.X + HeaderGlyphSize.X > MaxRight then - PaintInfo.GlyphPos.X := MaxRight - HeaderGlyphSize.X; - if Layout = blGlyphRight then - MaxRight := PaintInfo.GlyphPos.X - FSpacing; - if PaintInfo.GlyphPos.X < MinLeft then - PaintInfo.GlyphPos.X := MinLeft; - if Layout = blGlyphLeft then - MinLeft := PaintInfo.GlyphPos.X + HeaderGlyphSize.X + FSpacing; - if FCheckBox and (Owner.Header.MainColumn = Self.Index) then - Dec(PaintInfo.GlyphPos.X, 2) - else - if Owner.Header.MainColumn <> Self.Index then - Dec(PaintInfo.GlyphPos.X, 2); - - // Finally transform header glyph to its actual position. - Inc(PaintInfo.GlyphPos.X, PaintInfo.PaintRectangle.Left); - Inc(PaintInfo.GlyphPos.Y, PaintInfo.PaintRectangle.Top); - end; - if UseText then + with FRoot^ do begin - if TextPos.X < MinLeft then - TextPos.X := MinLeft; - OffsetRect(PaintInfo.TextRectangle, TextPos.X, TextPos.Y); - if PaintInfo.TextRectangle.Right > MaxRight then - PaintInfo.TextRectangle.Right := MaxRight; - OffsetRect(PaintInfo.TextRectangle, PaintInfo.PaintRectangle.Left, PaintInfo.PaintRectangle.Top); - - if coWrapCaption in FOptions then - begin - // Wrap the column caption if necessary. - R := PaintInfo.TextRectangle; - FCaptionText := WrapString(PaintInfo.TargetCanvas.Handle, FText, R, DT_RTLREADING and DrawFormat <> 0, DrawFormat); - GetStringDrawRect(PaintInfo.TargetCanvas.Handle, FCaptionText, R, DrawFormat); - end; + // Indication that this node is the root node. + PrevSibling := FRoot; + NextSibling := FRoot; + Parent := Pointer(Self); + States := [vsInitialized, vsExpanded, vsHasChildren, vsVisible]; + TotalHeight := FDefaultNodeHeight; + TotalCount := 1; + NodeHeight := FDefaultNodeHeight; + Align := 50; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.DefineProperties(Filer: TFiler); +procedure TBaseVirtualTree.InterruptValidation(pWaitForValidationTermination: Boolean = True); +var + WasValidating: Boolean; begin - inherited; + DoStateChange([tsStopValidation], [tsUseCache]); - // These properites are remains from non-Unicode Delphi versions, readers remain for backward compatibility. - Filer.DefineProperty('WideText', ReadText, nil, False); - Filer.DefineProperty('WideHint', ReadHint, nil, False); + // Check the worker thread existance. It might already be gone (usually on destruction of the last tree). + WasValidating := (tsValidating in FStates); + TWorkerThread.RemoveTree(Self, pWaitForValidationTermination); + if WasValidating then + InvalidateCache(); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.GetAbsoluteBounds(var Left, Right: Integer); +function TBaseVirtualTree.IsFirstVisibleChild(Parent, Node: PVirtualNode): Boolean; + +// Helper method to check if Node is the same as the first visible child of Parent. -// Returns the column's left and right bounds in header coordinates, that is, independant of the scrolling position. +var + Run: PVirtualNode; begin - Left := FLeft; - Right := FLeft + FWidth; + // Find first visible child. + Run := Parent.FirstChild; + while Assigned(Run) and not IsEffectivelyVisible[Run] do + Run := Run.NextSibling; + + Result := Assigned(Run) and (Run = Node); end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumn.GetDisplayName: string; +function TBaseVirtualTree.IsLastVisibleChild(Parent, Node: PVirtualNode): Boolean; -// Returns the column text if it only contains ANSI characters, otherwise the column id is returned because the IDE -// still cannot handle Unicode strings. +// Helper method to check if Node is the same as the last visible child of Parent. var - I: Integer; + Run: PVirtualNode; begin - // Check if the text of the column contains characters > 255 - I := 1; - while I <= Length(FText) do - begin - if Ord(FText[I]) > 255 then - Break; - Inc(I); - end; + // Find last visible child. + Run := Parent.LastChild; + while Assigned(Run) and not IsEffectivelyVisible[Run] do + Run := Run.PrevSibling; - if I > Length(FText) then - Result := FText // implicit conversion - else - Result := Format('Column %d', [Index]); + Result := Assigned(Run) and (Run = Node); end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumn.GetOwner: TVirtualTreeColumns; - -begin - Result := Collection as TVirtualTreeColumns; -end; +function TBaseVirtualTree.MakeNewNode: PVirtualNode; -//---------------------------------------------------------------------------------------------------------------------- +var + Size: Cardinal; -procedure TVirtualTreeColumn.InternalSetWidth(const value : TDimension); begin - FWidth := value; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.ReadText(Reader: TReader); + Size := TreeNodeSize; + if not (csDesigning in ComponentState) then + begin // Make sure FNodeDataSize is valid. + if FNodeDataSize <= 0 then + ValidateNodeDataSize(FNodeDataSize); -begin - case Reader.NextValue of - vaLString, vaString: - SetText(Reader.ReadString); + // Take record alignment into account. + Inc(Size, FNodeDataSize); + end//not csDesigning else - SetText(Reader.ReadString); - end; -end; + Inc(Size, SizeOf(Pointer)); // Fixes #702 -//---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumn.ReadHint(Reader: TReader); + Result := AllocMem(Size + FTotalInternalDataSize); -begin - case Reader.NextValue of - vaLString, vaString: - FHint := Reader.ReadString; - else - FHint := Reader.ReadString; + // Fill in some default values. + with Result^ do + begin + TotalCount := 1; + TotalHeight := FDefaultNodeHeight; + NodeHeight := FDefaultNodeHeight; + States := [vsVisible]; + Align := 50; end; end; //---------------------------------------------------------------------------------------------------------------------- +function TBaseVirtualTree.PackArray({*}const TheArray: TNodeArray; Count: Integer): Integer; assembler; +// *This is an optimization to get as near as possible with the PUREPASCAL code without the +// compiler generating a _DynArrayAddRef call. We still modify the array's content via pointers. -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.Assign(Source: TPersistent); +// Removes all entries from the selection array which are no longer in use. The selection array must be sorted for this +// algo to work. Values which must be removed are marked with bit 0 (LSB) set. This little trick works because memory +// is always allocated DWORD aligned. Since the selection array must be sorted while determining the entries to be +// removed it is much more efficient to increment the entry in question instead of setting it to nil (which would break +// the ordered appearance of the list). +// +// On enter EAX contains self reference, EDX the address to TheArray and ECX Count +// The returned value is the number of remaining entries in the array, so the caller can reallocate (shorten) +// the selection array if needed or -1 if nothing needs to be changed. +{$ifdef CPUX64} var - OldOptions: TVTColumnOptions; - + Source, Dest: ^PVirtualNode; + ConstOne: NativeInt; begin - if Source is TVirtualTreeColumn then + Source := Pointer(TheArray); + ConstOne := 1; + Result := 0; + // Do the fastest scan possible to find the first entry + while (Count <> 0) and {not Odd(NativeInt(Source^))} (NativeInt(Source^) and ConstOne = 0) do begin - OldOptions := FOptions; - FOptions := []; - - BiDiMode := TVirtualTreeColumn(Source).BiDiMode; - ImageIndex := TVirtualTreeColumn(Source).ImageIndex; - Layout := TVirtualTreeColumn(Source).Layout; - Margin := TVirtualTreeColumn(Source).Margin; - MaxWidth := TVirtualTreeColumn(Source).MaxWidth; - MinWidth := TVirtualTreeColumn(Source).MinWidth; - Position := TVirtualTreeColumn(Source).Position; - Spacing := TVirtualTreeColumn(Source).Spacing; - Style := TVirtualTreeColumn(Source).Style; - Text := TVirtualTreeColumn(Source).Text; - Hint := TVirtualTreeColumn(Source).Hint; - Width := TVirtualTreeColumn(Source).Width; - Alignment := TVirtualTreeColumn(Source).Alignment; - CaptionAlignment := TVirtualTreeColumn(Source).CaptionAlignment; - Color := TVirtualTreeColumn(Source).Color; - Tag := TVirtualTreeColumn(Source).Tag; - EditOptions := TVirtualTreeColumn(Source).EditOptions; - EditNextColumn := TVirtualTreeColumn(Source).EditNextColumn; - - // Order is important. Assign options last. - FOptions := OldOptions; - Options := TVirtualTreeColumn(Source).Options; + Inc(Result); + Inc(Source); + Dec(Count); + end; - Changed(False); - end - else - inherited Assign(Source); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVirtualTreeColumn.Equals(OtherColumnObj: TObject): Boolean; -var - OtherColumn : TVirtualTreeColumn; -begin - if OtherColumnObj is TVirtualTreeColumn then - begin - OtherColumn := TVirtualTreeColumn (OtherColumnObj); - Result := (BiDiMode = OtherColumn.BiDiMode) and - (ImageIndex = OtherColumn.ImageIndex) and - (Layout = OtherColumn.Layout) and - (Margin = OtherColumn.Margin) and - (MaxWidth = OtherColumn.MaxWidth) and - (MinWidth = OtherColumn.MinWidth) and - (Position = OtherColumn.Position) and - (Spacing = OtherColumn.Spacing) and - (Style = OtherColumn.Style) and - (Text = OtherColumn.Text) and - (Hint = OtherColumn.Hint) and - (Width = OtherColumn.Width) and - (Alignment = OtherColumn.Alignment) and - (CaptionAlignment = OtherColumn.CaptionAlignment) and - (Color = OtherColumn.Color) and - (Tag = OtherColumn.Tag) and - (Options = OtherColumn.Options); - end - else - Result := False; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVirtualTreeColumn.GetRect: TRect; - -// Returns the rectangle this column occupies in the header (relative to (0, 0) of the non-client area). - -begin - with TVirtualTreeColumns(GetOwner).FHeader do - Result := Treeview.FHeaderRect; - Inc(Result.Left, FLeft); - Result.Right := Result.Left + FWidth; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -// [IPK] -function TVirtualTreeColumn.GetText: string; - -begin - Result := FText; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.LoadFromStream(const Stream: TStream; Version: Integer); -var - Dummy: Integer; - S: string; - -begin - with Stream do + if Count <> 0 then begin - ReadBuffer(Dummy, SizeOf(Dummy)); - SetLength(S, Dummy); - ReadBuffer(PWideChar(S)^, 2 * Dummy); - Text := S; - ReadBuffer(Dummy, SizeOf(Dummy)); - SetLength(FHint, Dummy); - ReadBuffer(PWideChar(FHint)^, 2 * Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - Width := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - MinWidth := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - MaxWidth := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - Style := TVirtualTreeColumnStyle(Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - ImageIndex := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - Layout := TVTHeaderColumnLayout(Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - Margin := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - Spacing := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - BiDiMode := TBiDiMode(Dummy); - - ReadBuffer(Dummy, SizeOf(Dummy)); - if Version >= 3 then - Options := TVTColumnOptions(Dummy); - - if Version > 0 then - begin - // Parts which have been introduced/changed with header stream version 1+. - ReadBuffer(Dummy, SizeOf(Dummy)); - Tag := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - Alignment := TAlignment(Dummy); - - if Version > 1 then - begin - ReadBuffer(Dummy, SizeOf(Dummy)); - Color := TColor(Dummy); - end; - - if Version > 5 then + Dest := Source; + repeat + // Skip odd entries + if {not Odd(NativeInt(Source^))} NativeInt(Source^) and ConstOne = 0 then begin - if coUseCaptionAlignment in FOptions then - begin - ReadBuffer(Dummy, SizeOf(Dummy)); - CaptionAlignment := TAlignment(Dummy); - end; + Dest^ := Source^; + Inc(Result); + Inc(Dest); end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.ParentBiDiModeChanged; - -var - Columns: TVirtualTreeColumns; - -begin - if coParentBiDiMode in FOptions then - begin - Columns := GetOwner as TVirtualTreeColumns; - if Assigned(Columns) and (FBiDiMode <> Columns.FHeader.Treeview.BiDiMode) then - begin - FBiDiMode := Columns.FHeader.Treeview.BiDiMode; - Changed(False); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.ParentColorChanged; - -var - Columns: TVirtualTreeColumns; - -begin - if coParentColor in FOptions then - begin - Columns := GetOwner as TVirtualTreeColumns; - if Assigned(Columns) and (FColor <> Columns.FHeader.Treeview.Color) then - begin - FColor := Columns.FHeader.Treeview.Color; - Changed(False); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.RestoreLastWidth; - -begin - TVirtualTreeColumns(GetOwner).AnimatedResize(Index, FLastWidth); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumn.SaveToStream(const Stream: TStream); - -var - Dummy: Integer; - -begin - with Stream do - begin - Dummy := Length(FText); - WriteBuffer(Dummy, SizeOf(Dummy)); - WriteBuffer(PWideChar(FText)^, 2 * Dummy); - Dummy := Length(FHint); - WriteBuffer(Dummy, SizeOf(Dummy)); - WriteBuffer(PWideChar(FHint)^, 2 * Dummy); - WriteBuffer(FWidth, SizeOf(FWidth)); - WriteBuffer(FMinWidth, SizeOf(FMinWidth)); - WriteBuffer(FMaxWidth, SizeOf(FMaxWidth)); - Dummy := Ord(FStyle); - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := FImageIndex; - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Ord(FLayout); - WriteBuffer(Dummy, SizeOf(Dummy)); - WriteBuffer(FMargin, SizeOf(FMargin)); - WriteBuffer(FSpacing, SizeOf(FSpacing)); - Dummy := Ord(FBiDiMode); - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Integer(FOptions); - WriteBuffer(Dummy, SizeOf(Dummy)); - - // parts introduced with stream version 1 - WriteBuffer(FTag, SizeOf(Dummy)); - Dummy := Cardinal(FAlignment); - WriteBuffer(Dummy, SizeOf(Dummy)); - - // parts introduced with stream version 2 - Dummy := Integer(FColor); - WriteBuffer(Dummy, SizeOf(Dummy)); - - // parts introduced with stream version 6 - if coUseCaptionAlignment in FOptions then - begin - Dummy := Cardinal(FCaptionAlignment); - WriteBuffer(Dummy, SizeOf(Dummy)); - end; + Inc(Source); // Point to the next entry + Dec(Count); + until Count = 0; end; end; +{$else} +asm + PUSH EBX + PUSH EDI + PUSH ESI + MOV ESI, EDX + MOV EDX, -1 + JCXZ @@Finish // Empty list? + INC EDX // init remaining entries counter + MOV EDI, ESI // source and destination point to the list memory + MOV EBX, 1 // use a register instead of immediate operant to check against +@@PreScan: + TEST [ESI], EBX // do the fastest scan possible to find the first entry + // which must be removed + JNZ @@DoMainLoop + INC EDX + ADD ESI, 4 + DEC ECX + JNZ @@PreScan + JMP @@Finish -//---------------------------------------------------------------------------------------------------------------------- - -function TVirtualTreeColumn.UseRightToLeftReading: Boolean; - -begin - Result := FBiDiMode <> bdLeftToRight; -end; - -//----------------- TVirtualTreeColumns -------------------------------------------------------------------------------- - -constructor TVirtualTreeColumns.Create(AOwner: TVTHeader); - -var - ColumnClass: TVirtualTreeColumnClass; - -begin - FHeader := AOwner; - - // Determine column class to be used in the header. - ColumnClass := AOwner.FOwner.GetColumnClass; - // The owner tree always returns the default tree column class if not changed by application/descendants. - inherited Create(ColumnClass); - - FHeaderBitmap := TBitmap.Create; - FHeaderBitmap.PixelFormat := pf32Bit; - - FHoverIndex := NoColumn; - FDownIndex := NoColumn; - FClickIndex := NoColumn; - FDropTarget := NoColumn; - FTrackIndex := NoColumn; - FDefaultWidth := 50; - Self.FColumnPopupMenu := nil; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -destructor TVirtualTreeColumns.Destroy; - -begin - FreeAndNil(FColumnPopupMenu); - FreeAndNil(FHeaderBitmap); - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVirtualTreeColumns.GetCount: Integer; +@@DoMainLoop: + MOV EDI, ESI +@@MainLoop: + TEST [ESI], EBX // odd entry? + JNE @@Skip // yes, so skip this one + MOVSD // else move the entry to new location + INC EDX // count the moved entries + DEC ECX + JNZ @@MainLoop // do it until all entries are processed + JMP @@Finish -begin - Result := inherited Count; +@@Skip: + ADD ESI, 4 // point to the next entry + DEC ECX + JNZ @@MainLoop // do it until all entries are processed +@@Finish: + MOV EAX, EDX // prepare return value + POP ESI + POP EDI + POP EBX end; +{$endif CPUX64} //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetItem(Index: TColumnIndex): TVirtualTreeColumn; - -begin - Result := TVirtualTreeColumn(inherited GetItem(Index)); -end; +procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); -//---------------------------------------------------------------------------------------------------------------------- +// initializes the contents of the internal bitmaps -function TVirtualTreeColumns.GetNewIndex(P: TPoint; var OldIndex: TColumnIndex): Boolean; +const + LineBitsDotted: array [0..8] of Word = ($55, $AA, $55, $AA, $55, $AA, $55, $AA, $55); + LineBitsSolid: array [0..7] of Word = (0, 0, 0, 0, 0, 0, 0, 0); var - NewIndex: Integer; - -begin - Result := False; - // convert to local coordinates - Inc(P.Y, FHeader.Height); - NewIndex := ColumnFromPosition(P); - if NewIndex <> OldIndex then - begin - if OldIndex > NoColumn then - FHeader.Invalidate(Items[OldIndex], False, True); - OldIndex := NewIndex; - if OldIndex > NoColumn then - FHeader.Invalidate(Items[OldIndex], False, True); - Result := True; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.SetDefaultWidth(Value: Integer); - -begin - FDefaultWidth := Value; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.SetItem(Index: TColumnIndex; Value: TVirtualTreeColumn); - -begin - inherited SetItem(Index, Value); -end; - -function TVirtualTreeColumns.StyleServices(AControl: TControl): TCustomStyleServices; -begin - if AControl = nil then - AControl := FHeader.Treeview; - Result := VTStyleServices(AControl); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.AdjustAutoSize(CurrentIndex: TColumnIndex; Force: Boolean = False); + PatternBitmap: HBITMAP; + Bits: Pointer; + Size: TSize; + Theme: HTHEME; + R: TRect; -// Called only if the header is in auto-size mode which means a column needs to be so large -// that it fills all the horizontal space not occupied by the other columns. -// CurrentIndex (if not InvalidColumn) describes which column has just been resized. + //--------------- local function -------------------------------------------- -var - NewValue, - AutoIndex, - Index, - RestWidth: Integer; - WasUpdating: Boolean; -begin - if Count > 0 then + procedure FillBitmap (ABitmap: TBitmap); begin - // Determine index to be used for auto resizing. This is usually given by the owner's AutoSizeIndex, but - // could be different if the column whose resize caused the invokation here is either the auto column itself - // or visually to the right of the auto size column. - AutoIndex := FHeader.AutoSizeIndex; - if (AutoIndex < 0) or (AutoIndex >= Count) then - AutoIndex := Count - 1; - - if AutoIndex >= 0 then + with ABitmap, Canvas do begin - with FHeader.Treeview do + SetSize(Size.cx, Size.cy); + + if IsWinVistaOrAbove and (tsUseThemes in FStates) and (toUseExplorerTheme in FOptions.PaintOptions) or VclStyleEnabled then begin - if HandleAllocated then - RestWidth := ClientWidth + if (FHeader.MainColumn > NoColumn) then + Brush.Color := FHeader.Columns[FHeader.MainColumn].GetEffectiveColor else - RestWidth := Width; - end; + Brush.Color := FColors.BackGroundColor; + end + else + Brush.Color := clFuchsia; - // Go through all columns and calculate the rest space remaining. - for Index := 0 to Count - 1 do - if (Index <> AutoIndex) and (coVisible in Items[Index].Options) then - Dec(RestWidth, Items[Index].Width); + Transparent := True; + TransparentColor := Brush.Color; - with Items[AutoIndex] do - begin - NewValue := Max(MinWidth, Min(MaxWidth, RestWidth)); - if Force or (FWidth <> NewValue) then - begin - FWidth := NewValue; - UpdatePositions; - WasUpdating := csUpdating in FHeader.Treeview.ComponentState; - if not WasUpdating then - FHeader.Treeview.Updating();// Fixes #398 - try - FHeader.Treeview.DoColumnResize(AutoIndex); - finally - if not WasUpdating then - FHeader.Treeview.Updated(); - end; - end; - end; + FillRect(Rect(0, 0, Width, Height)); end; end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVirtualTreeColumns.AdjustDownColumn(P: TPoint): TColumnIndex; - -// Determines the column from the given position and returns it. If this column is allowed to be clicked then -// it is also kept for later use. - -begin - // Convert to local coordinates. - Inc(P.Y, FHeader.Height); - Result := ColumnFromPosition(P); - if (Result > NoColumn) and (Result <> FDownIndex) and (coAllowClick in Items[Result].Options) and - (coEnabled in Items[Result].Options) then - begin - if FDownIndex > NoColumn then - FHeader.Invalidate(Items[FDownIndex]); - FDownIndex := Result; - FCheckBoxHit := Items[Result].HasImage and PtInRect(Items[Result].ImageRect, P) and Items[Result].CheckBox; - FHeader.Invalidate(Items[FDownIndex]); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVirtualTreeColumns.AdjustHoverColumn(P: TPoint): Boolean; - -// Determines the new hover column index and returns True if the index actually changed else False. - -begin - Result := GetNewIndex(P, FHoverIndex); -end; - -//---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.AdjustPosition(Column: TVirtualTreeColumn; Position: Cardinal); - -// Reorders the column position array so that the given column gets the given position. - -var - OldPosition: Cardinal; + //--------------- end local function ---------------------------------------- +const + cMinExpandoHeight = 11; // pixels @100% begin - OldPosition := Column.Position; - if OldPosition <> Position then + if VclStyleEnabled and (seClient in StyleElements) then begin - if OldPosition < Position then - begin - // column will be moved up so move down other entries - Move(FPositionToIndex[OldPosition + 1], FPositionToIndex[OldPosition], (Position - OldPosition) * SizeOf(Cardinal)); - end - else - begin - // column will be moved down so move up other entries - Move(FPositionToIndex[Position], FPositionToIndex[Position + 1], (OldPosition - Position) * SizeOf(Cardinal)); - end; - FPositionToIndex[Position] := Column.Index; - end; -end; + if NeedButtons then begin + if StyleServices.GetElementSize(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), TElementSize.esActual, Size) then + begin + Size.cx := Max(Size.cx, cMinExpandoHeight); // Use min size of 11, see issue #1035 / RSP-33715 + Size.cx := ScaledPixels(Size.cx) // I would have expected that the returned value is dpi-sclaed, but this is not the case in RAD Studio 10.4.1. See issue #984 + end + else + Size.cx := ScaledPixels(cMinExpandoHeight); + Size.cy := Size.cx; + FillBitmap(FPlusBM); + FillBitmap(FHotPlusBM); + FillBitmap(FSelectedHotPlusBM); + FillBitmap(FMinusBM); + FillBitmap(FHotMinusBM); + FillBitmap(FSelectedHotMinusBM); + R := Rect(0,0,Size. cx,Size.cy); + // tcbCategoryGlyphClosed, tcbCategoryGlyphOpened from CategoryButtons + StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); + StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); + FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); + FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); + FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); + FSelectedHotPlusBM.Canvas.Draw(0, 0, FPlusBM); + if Assigned(FOnPrepareButtonImages) then + FOnPrepareButtonImages(Self, FPlusBM, FHotPlusBM, FSelectedHotPlusBM, FMinusBM, FHotMinusBM, FSelectedHotMinusBM, size); + end;//if NeedButtons + end// if VclStyleEnabled + else + begin // No stlye + Size.cx := ScaledPixels(9); + Size.cy := ScaledPixels(9); + if tsUseThemes in FStates then + begin + R := Rect(0, 0, 100, 100); + {$if CompilerVersion >= 33} + if TOSVersion.Check(10) and (TOSVersion.Build >= 15063) then + Theme := OpenThemeDataForDPI(Handle, 'TREEVIEW', Self.FCurrentPPI) + else + Theme := OpenThemeData(Handle, 'TREEVIEW'); + {$else} + Theme := OpenThemeData(Handle, 'TREEVIEW'); + {$ifend} + GetThemePartSize(Theme, FPlusBM.Canvas.Handle, TVP_GLYPH, GLPS_OPENED, @R, TS_TRUE, Size); + end + else + Theme := 0; -//---------------------------------------------------------------------------------------------------------------------- + if NeedButtons then + begin + //VCL Themes do not really have ability to provide tree plus/minus images when not using the + //windows theme. The bitmap style designer doesn't have any elements for for them, and you + //cannot name any elements you add, which makes it useless. + //To mitigate this, Hook up the OnPrepareButtonImages and draw them yourself. + if Assigned(FOnPrepareButtonImages) then + begin + FillBitmap(FPlusBM); + FillBitmap(FHotPlusBM); + FillBitmap(FSelectedHotPlusBM); + FillBitmap(FMinusBM); + FillBitmap(FHotMinusBM); + FillBitmap(FSelectedHotMinusBM); + FOnPrepareButtonImages(Self, FPlusBM, FHotPlusBM, FSelectedHotPlusBM, FMinusBM, FHotMinusBM, FSelectedHotMinusBM, size); + end + else + begin + with FMinusBM, Canvas do + begin + // box is always of odd size + FillBitmap(FMinusBM); + FillBitmap(FHotMinusBM); + FillBitmap(FSelectedHotMinusBM); + // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style scheiße aus + // Because the self-drawn bitmaps view Vcl Style shit + if Theme = 0 then + begin + if not(tsUseExplorerTheme in FStates) then + begin + if FButtonStyle = bsTriangle then + begin + Brush.Color := clBlack; + Pen.Color := clBlack; + Polygon([Point(0, 2), Point(8, 2), Point(4, 6)]); + end + else + begin + // Button style is rectangular. Now ButtonFillMode determines how to fill the interior. + if FButtonFillMode in [fmTreeColor, fmWindowColor, fmTransparent] then + begin + case FButtonFillMode of + fmTreeColor: + Brush.Color := FColors.BackGroundColor; + fmWindowColor: + Brush.Color := clWindow; + end; + Pen.Color := FColors.TreeLineColor; + Rectangle(0, 0, Width, Height); + Pen.Color := FColors.NodeFontColor; + MoveTo(2, Width div 2); + LineTo(Width - 2, Width div 2); + end + else + FMinusBM.Handle := LoadBitmap(HInstance, 'VT_XPBUTTONMINUS'); + end; + FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); + FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); + end; + end; + end; + with FPlusBM, Canvas do + begin + FillBitmap(FPlusBM); + FillBitmap(FHotPlusBM); + FillBitmap(FSelectedHotPlusBM); + if Theme = 0 then + begin + if not(tsUseExplorerTheme in FStates) then + begin + if FButtonStyle = bsTriangle then + begin + Brush.Color := clBlack; + Pen.Color := clBlack; + Polygon([Point(2, 0), Point(6, 4), Point(2, 8)]); + end + else + begin + // Button style is rectangular. Now ButtonFillMode determines how to fill the interior. + if FButtonFillMode in [fmTreeColor, fmWindowColor, fmTransparent] then + begin + case FButtonFillMode of + fmTreeColor: + Brush.Color := FColors.BackGroundColor; + fmWindowColor: + Brush.Color := clWindow; + end; + Pen.Color := FColors.TreeLineColor; + Rectangle(0, 0, Width, Height); + Pen.Color := FColors.NodeFontColor; + MoveTo(2, Width div 2); + LineTo(Width - 2, Width div 2); + MoveTo(Width div 2, 2); + LineTo(Width div 2, Width - 2); + end + else + FPlusBM.Handle := LoadBitmap(HInstance, 'VT_XPBUTTONPLUS'); + end; + FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); + FSelectedHotPlusBM.Canvas.Draw(0, 0, FPlusBM); + end; + end; + end; -function TVirtualTreeColumns.CanSplitterResize(P: TPoint; Column: TColumnIndex): Boolean; -begin - Result := (Column > NoColumn) and ([coResizable, coVisible] * Items[Column].Options = [coResizable, coVisible]); - DoCanSplitterResize(P, Column, Result); + // Overwrite glyph images if theme is active. + if (tsUseThemes in FStates) and (Theme <> 0) then + begin + R := Rect(0, 0, Size.cx, Size.cy); + DrawThemeBackground(Theme, FPlusBM.Canvas.Handle, TVP_GLYPH, GLPS_CLOSED, R, nil); + DrawThemeBackground(Theme, FMinusBM.Canvas.Handle, TVP_GLYPH, GLPS_OPENED, R, nil); + if tsUseExplorerTheme in FStates then + begin + DrawThemeBackground(Theme, FHotPlusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_CLOSED, R, nil); + DrawThemeBackground(Theme, FSelectedHotPlusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_CLOSED, R, nil); + DrawThemeBackground(Theme, FHotMinusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_OPENED, R, nil); + DrawThemeBackground(Theme, FSelectedHotMinusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_OPENED, R, nil); + end + else + begin + FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); + FSelectedHotPlusBM.Canvas.Draw(0, 0, FPlusBM); + FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); + FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); + end; + end; + end; + if tsUseThemes in FStates then + CloseThemeData(Theme); + end;// if NeedButtons + end;// else + + if NeedLines then + begin + if FDottedBrush <> 0 then + DeleteObject(FDottedBrush); + case FLineStyle of + lsDotted: + Bits := @LineBitsDotted; + lsSolid: + Bits := @LineBitsSolid; + else // lsCustomStyle + Bits := @LineBitsDotted; + DoGetLineStyle(Bits); + end; + PatternBitmap := CreateBitmap(8, 8, 1, 1, Bits); + FDottedBrush := CreatePatternBrush(PatternBitmap); + DeleteObject(PatternBitmap); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.DoCanSplitterResize(P: TPoint; Column: TColumnIndex; var Allowed: Boolean); +procedure TBaseVirtualTree.SetAlignment(const Value: TAlignment); begin - if Assigned(FHeader.Treeview.FOnCanSplitterResizeColumn) then - FHeader.Treeview.FOnCanSplitterResizeColumn(FHeader, P, Column, Allowed); + if FAlignment <> Value then + begin + FAlignment := Value; + if not (csLoading in ComponentState) then + Invalidate; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.DrawButtonText(DC: HDC; Caption: string; Bounds: TRect; Enabled, Hot: Boolean; - DrawFormat: Cardinal; WrapCaption: Boolean); - -var - TextSpace: Integer; - Size: TSize; +procedure TBaseVirtualTree.SetAnimationDuration(const Value: Cardinal); begin - if not WrapCaption then - begin - // Do we need to shorten the caption due to limited space? - GetTextExtentPoint32W(DC, PWideChar(Caption), Length(Caption), Size); - TextSpace := Bounds.Right - Bounds.Left; - if TextSpace < Size.cx then - Caption := ShortenString(DC, Caption, TextSpace); - end; - - SetBkMode(DC, TRANSPARENT); - if not Enabled then - if FHeader.Treeview.VclStyleEnabled then - begin - SetTextColor(DC, ColorToRGB(FHeader.Treeview.FColors.HeaderFontColor)); - Winapi.Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat); - end - else - begin - OffsetRect(Bounds, 1, 1); - SetTextColor(DC, ColorToRGB(clBtnHighlight)); - Winapi.Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat); - OffsetRect(Bounds, -1, -1); - SetTextColor(DC, ColorToRGB(clBtnShadow)); - Winapi.Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat); - end + FAnimationDuration := Value; + if FAnimationDuration = 0 then + FOptions.AnimationOptions := FOptions.AnimationOptions - [toAnimatedToggle] else - begin - if Hot then - SetTextColor(DC, ColorToRGB(FHeader.Treeview.FColors.HeaderHotColor)) - else - SetTextColor(DC, ColorToRGB(FHeader.Treeview.FColors.HeaderFontColor)); - Winapi.Windows.DrawTextW(DC, PWideChar(Caption), Length(Caption), Bounds, DrawFormat); - end; + FOptions.AnimationOptions := FOptions.AnimationOptions + [toAnimatedToggle] end; //---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.FixPositions; - -// Fixes column positions after loading from DFM or Bidi mode change. - -var - I: Integer; +{ New, Support for transparent background: + * Image types: BMP, PNG, GIF, ICO, EMF, TIFF and WMF are automatically identified to support transparent background + * Also detects certain third party image classes registered for PNG, GIF and other image types so that the + transparency related code is used for them. See the code below. + * If some other third party image class is registered that is not detected, + set the flag BackgroundTransparentExternalType explicitly in order to properly do + transparent painting. +} +procedure TBaseVirtualTree.SetBackground(const Value: TPicture); begin - for I := 0 to Count - 1 do - FPositionToIndex[Items[I].Position] := I; - - FNeedPositionsFix := False; - UpdatePositions(True); + FBackground.Assign(Value); + Invalidate; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetColumnAndBounds(P: TPoint; var ColumnLeft, ColumnRight: Integer; - Relative: Boolean = True): Integer; - -// Returns the column where the mouse is currently in as well as the left and right bound of -// this column (Left and Right are undetermined if no column is involved). - -var - I: Integer; +procedure TBaseVirtualTree.SetBackGroundImageTransparent(const Value: Boolean); begin - Result := InvalidColumn; - if Relative and (P.X >= Header.Columns.GetVisibleFixedWidth) then - ColumnLeft := -FHeader.Treeview.FEffectiveOffsetX - else - ColumnLeft := 0; + if Value <> FBackGroundImageTransparent then + begin + FBackGroundImageTransparent := Value; + Invalidate; + end; +end; - if FHeader.Treeview.UseRightToLeftAlignment then - Inc(ColumnLeft, FHeader.Treeview.ComputeRTLOffset(True)); +//---------------------------------------------------------------------------------------------------------------------- - for I := 0 to Count - 1 do - with Items[FPositionToIndex[I]] do - if coVisible in FOptions then - begin - ColumnRight := ColumnLeft + FWidth; +procedure TBaseVirtualTree.SetBackgroundOffset(const Index, Value: Integer); - //fix: in right to left alignment, X can be in the - //area on the left of first column which is OUT. - if (P.X < ColumnLeft) and (I = 0) then - begin - Result := InvalidColumn; - exit; - end; - if P.X < ColumnRight then - begin - Result := FPositionToIndex[I]; - Exit; - end; - ColumnLeft := ColumnRight; +begin + case Index of + 0: + if FBackgroundOffsetX <> Value then + begin + FBackgroundOffsetX := Value; + Invalidate; + end; + 1: + if FBackgroundOffsetY <> Value then + begin + FBackgroundOffsetY := Value; + Invalidate; end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetOwner: TPersistent; +procedure TBaseVirtualTree.SetBorderStyle(Value: TBorderStyle); begin - Result := FHeader; + if FBorderStyle <> Value then + begin + FBorderStyle := Value; + RecreateWnd; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.HandleClick(P: TPoint; Button: TMouseButton; Force, DblClick: Boolean): Boolean; - -// Generates a click event if the mouse button has been released over the same column it was pressed first. -// Alternatively, Force might be set to True to indicate that the down index does not matter (right, middle and -// double click). -// Returns true if the click was handled, False otherwise. +procedure TBaseVirtualTree.SetBottomNode(Node: PVirtualNode); var - HitInfo: TVTHeaderHitInfo; - NewClickIndex: Integer; - Menu: TPopupMenu; -begin - Result := False; - if (csDesigning in Header.Treeview.ComponentState) then - exit; - // Convert vertical position to local coordinates. - Inc(P.Y, FHeader.Height); - NewClickIndex := ColumnFromPosition(P); - with HitInfo do - begin - X := P.X; - Y := P.Y; - Shift := FHeader.GetShiftState; - if DblClick then - Shift := Shift + [ssDouble]; - end; - HitInfo.Button := Button; + Run: PVirtualNode; + R: TRect; - if (NewClickIndex > NoColumn) and (coAllowClick in Items[NewClickIndex].Options) and - ((NewClickIndex = FDownIndex) or Force) then +begin + if Assigned(Node) then begin - FClickIndex := NewClickIndex; - HitInfo.Column := NewClickIndex; - HitInfo.HitPosition := [hhiOnColumn]; - - if Items[NewClickIndex].HasImage and PtInRect(Items[NewClickIndex].ImageRect, P) then + // make sure all parents of the node are expanded + Run := Node.Parent; + while Run <> FRoot do begin - Include(HitInfo.HitPosition, hhiOnIcon); - if Items[NewClickIndex].CheckBox then - begin - if Button = mbLeft then - FHeader.Treeview.UpdateColumnCheckState(Items[NewClickIndex]); - Include(HitInfo.HitPosition, hhiOnCheckbox); - end; + if not (vsExpanded in Run.States) then + ToggleNode(Run); + Run := Run.Parent; end; - end - else - begin - FClickIndex := NoColumn; - HitInfo.Column := NoColumn; - HitInfo.HitPosition := [hhiNoWhere]; + R := GetDisplayRect(Node, FHeader.MainColumn, True); + DoSetOffsetXY(Point(FOffsetX, FOffsetY + ClientHeight - R.Top - Integer(NodeHeight[Node])), + [suoRepaintScrollBars, suoUpdateNCArea]); end; - - if DblClick then - FHeader.Treeview.DoHeaderDblClick(HitInfo) - else begin - if (hoHeaderClickAutoSort in Header.Options) and (HitInfo.Button = mbLeft) and not (hhiOnCheckbox in HitInfo.HitPosition) and (HitInfo.Column >= 0) then - begin - // handle automatic setting of SortColumn and toggling of the sort order - if HitInfo.Column <> Header.SortColumn then - begin - // set sort column - Header.DoSetSortColumn(HitInfo.Column, Self[HitInfo.Column].DefaultSortDirection); - end//if - else - begin - // toggle sort direction - if Header.SortDirection = sdDescending then - Header.SortDirection := sdAscending - else - Header.SortDirection := sdDescending; - end;//else - Result := True; - end;//if - - if (Button = mbRight) then - begin - Dec(P.Y, FHeader.Height); // popup menus at actual clicked point - FreeAndNil(fColumnPopupMenu);// Attention: Do not free the TVTHeaderPopupMenu at the end of this method, otherwise the clikc events of the menu item will not be fired. - Self.FDownIndex := NoColumn; - Self.FTrackIndex := NoColumn; - Self.FCheckBoxHit := False; - Menu := Header.DoGetPopupMenu(Self.ColumnFromPosition(Point(P.X, P.Y + Integer(Header.Treeview.Height))), P); - if Assigned(Menu) then - begin - Header.Treeview.StopTimer(ScrollTimer); - Header.Treeview.StopTimer(HeaderTimer); - Header.Columns.SetHoverIndex(NoColumn); - Header.Treeview.DoStateChange([], [tsScrollPending, tsScrolling]); - - Menu.PopupComponent := Header.Treeview; - With Header.Treeview.ClientToScreen(P) do - Menu.Popup(X, Y); - Result := True; - end - else if (hoAutoColumnPopupMenu in Header.Options) then - begin - fColumnPopupMenu := TVTHeaderPopupMenu.Create(Header.TreeView); - TVTHeaderPopupMenu(fColumnPopupMenu).OnAddHeaderPopupItem := HeaderPopupMenuAddHeaderPopupItem; - TVTHeaderPopupMenu(fColumnPopupMenu).OnColumnChange := HeaderPopupMenuColumnChange; - fColumnPopupMenu.PopupComponent := Header.Treeview; - if (hoDblClickResize in Header.Options) and ((Header.Treeview.ChildCount[nil] > 0) or (hoAutoResizeInclCaption in Header.Options)) then - TVTHeaderPopupMenu(fColumnPopupMenu).Options := TVTHeaderPopupMenu(fColumnPopupMenu).Options + [poResizeToFitItem] - else - TVTHeaderPopupMenu(fColumnPopupMenu).Options := TVTHeaderPopupMenu(fColumnPopupMenu).Options - [poResizeToFitItem]; - With Header.Treeview.ClientToScreen(P) do - fColumnPopupMenu.Popup(X, Y); - Result := True; - end; // if hoAutoColumnPopupMenu - end;//if mbRight - FHeader.Treeview.DoHeaderClick(HitInfo); - end;//else (not DblClick) - - if not (hhiNoWhere in HitInfo.HitPosition) then - FHeader.Invalidate(Items[NewClickIndex]); - if (FClickIndex > NoColumn) and (FClickIndex <> NewClickIndex) then - FHeader.Invalidate(Items[FClickIndex]); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.HeaderPopupMenuAddHeaderPopupItem(const Sender: TBaseVirtualTree; const Column: TColumnIndex; - var Cmd: TAddPopupItemType); -begin - Sender.DoHeaderAddPopupItem(Column, Cmd); end; //---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.SetBottomSpace(const Value: Cardinal); -procedure TVirtualTreeColumns.HeaderPopupMenuColumnChange(const Sender: TBaseVirtualTree; const Column: TColumnIndex; Visible: Boolean); begin - Sender.DoColumnVisibilityChanged(Column, Visible); + if FBottomSpace <> Value then + begin + FBottomSpace := Value; + UpdateVerticalScrollBar(True); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.IndexChanged(OldIndex, NewIndex: Integer); - -// Called by a column when its index in the collection changes. If NewIndex is -1 then the column is -// about to be removed, otherwise it is moved to a new index. -// The method will then update the position array to reflect the change. - -var - I: Integer; - Increment: Integer; - Lower, - Upper: Integer; +procedure TBaseVirtualTree.SetButtonFillMode(const Value: TVTButtonFillMode); begin - if NewIndex = -1 then - begin - // Find position in the array with the old index. - Upper := High(FPositionToIndex); - for I := 0 to Upper do - begin - if FPositionToIndex[I] = OldIndex then - begin - // Index found. Move all higher entries one step down and remove the last entry. - if I < Upper then - Move(FPositionToIndex[I + 1], FPositionToIndex[I], (Upper - I) * SizeOf(TColumnIndex)); - end; - // Decrease all indices, which are greater than the index to be deleted. - if FPositionToIndex[I] > OldIndex then - Dec(FPositionToIndex[I]); - end; - SetLength(FPositionToIndex, High(FPositionToIndex)); - end - else + if FButtonFillMode <> Value then begin - if OldIndex < NewIndex then - Increment := -1 - else - Increment := 1; - - Lower := Min(OldIndex, NewIndex); - Upper := Max(OldIndex, NewIndex); - for I := 0 to High(FPositionToIndex) do + FButtonFillMode := Value; + if not (csLoading in ComponentState) then begin - if (FPositionToIndex[I] >= Lower) and (FPositionToIndex[I] < Upper) then - Inc(FPositionToIndex[I], Increment) - else - if FPositionToIndex[I] = OldIndex then - FPositionToIndex[I] := NewIndex; + PrepareBitmaps(True, False); + if HandleAllocated then + Invalidate; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.InitializePositionArray; - -// Ensures that the column position array contains as many entries as columns are defined. -// The array is resized and initialized with default values if needed. - -var - I, OldSize: Integer; - Changed: Boolean; +procedure TBaseVirtualTree.SetButtonStyle(const Value: TVTButtonStyle); begin - if Count <> Length(FPositionToIndex) then + if FButtonStyle <> Value then begin - OldSize := Length(FPositionToIndex); - SetLength(FPositionToIndex, Count); - if Count > OldSize then - begin - // New items have been added, just set their position to the same as their index. - for I := OldSize to Count - 1 do - FPositionToIndex[I] := I; - end - else + FButtonStyle := Value; + if not (csLoading in ComponentState) then begin - // Items have been deleted, so reindex remaining entries by decrementing values larger than the highest - // possible index until no entry is higher than this limit. - repeat - Changed := False; - for I := 0 to Count - 1 do - if FPositionToIndex[I] >= Count then - begin - Dec(FPositionToIndex[I]); - Changed := True; - end; - until not Changed; + PrepareBitmaps(True, False); + if HandleAllocated then + Invalidate; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.Notify(Item: TCollectionItem; Action: System.Classes.TCollectionNotification); -var - I: Integer; -begin - if Action in [cnExtracting, cnDeleting] then - begin - // Adjust all positions larger than the deleted column's position. Fixes #959 - for I := 0 to Count - 1 do begin - if Items[I].Position > TVirtualTreeColumn(Item).Position then - Items[I].Position := Items[I].Position - 1; - end;//for I - - with Header.Treeview do - if not (csLoading in ComponentState) and (FFocusedColumn = Item.Index) then - FFocusedColumn := NoColumn; - end;// if cnDeleting -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.ReorderColumns(RTL: Boolean); - -var - I: Integer; +procedure TBaseVirtualTree.SetCheckState(Node: PVirtualNode; Value: TCheckState); begin - if RTL then - begin - for I := 0 to Count - 1 do - FPositionToIndex[I] := Count - I - 1; - end - else - begin - for I := 0 to Count - 1 do - FPositionToIndex[I] := I; - end; - - UpdatePositions(True); + if (Node.CheckState <> Value) and DoChecking(Node, Value) then + DoCheckClick(Node, Value); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.SetHoverIndex(index : TColumnIndex); -begin - FHoverIndex := index; -end; +procedure TBaseVirtualTree.SetCheckStateForAll(aCheckState: TCheckState; pSelectedOnly: Boolean; pExcludeDisabled: Boolean = True); -//---------------------------------------------------------------------------------------------------------------------- +// Changes the check state for all or for all seledcted nodes. +// aCheckState: The new check state. +// pSelectedOnly: If passed True, only the selected nodes will bechnaged, if passed False all nodes in the control will be changed. +// pExcludeDisabled: Optiopnal. If passed True (the default value), disabled checkboxes won't be changed, if passed False disabled checkboxes will be altered too. -procedure TVirtualTreeColumns.EndUpdate; +var + lItem : PVirtualNode; begin - InitializePositionArray(); - FixPositions(); // Accept the cuurent order. See issue #753 - inherited; + With Self do begin + Screen.Cursor := crHourGlass; + BeginUpdate; + try + if pSelectedOnly then + lItem := GetFirstSelected + else + lItem := GetFirst; + //for i:=0 to List.Items.Count-1 do begin + while Assigned(lItem) do begin + if not pExcludeDisabled or not CheckState[lItem].IsDisabled() then + CheckState[lItem] := aCheckState; + if pSelectedOnly then + lItem := GetNextSelected(lItem) + else + lItem := GetNext(lItem); + end;//while + finally + Screen.Cursor := crDefault; + EndUpdate; + end;//try..finally + end;//With end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.Update(Item: TCollectionItem); +procedure TBaseVirtualTree.SetCheckType(Node: PVirtualNode; Value: TCheckType); begin - // This is the only place which gets notified when a new column has been added or removed - // and we need this event to adjust the column position array. - InitializePositionArray; - if csLoading in Header.Treeview.ComponentState then - FNeedPositionsFix := True - else - UpdatePositions; - - // The first column which is created is by definition also the main column. - if (Count > 0) and (Header.FMainColumn < 0) then - FHeader.MainColumn := 0; - - if not (csLoading in FHeader.Treeview.ComponentState) and not (hsLoading in FHeader.States) then + if (Node.CheckType <> Value) and not (toReadOnly in FOptions.MiscOptions) then begin - with FHeader do + Node.CheckType := Value; + if (Value <> ctTriStateCheckBox) and (Node.CheckState in [csMixedNormal, csMixedPressed]) then + Node.CheckState := csUncheckedNormal;// reset check state if it doesn't fit the new check type + // For check boxes with tri-state check box parents we have to initialize differently. + if (toAutoTriStateTracking in FOptions.AutoOptions) and (Value in [ctCheckBox, ctTriStateCheckBox]) and + (Node.Parent <> FRoot) then begin - if hoAutoResize in FOptions then - AdjustAutoSize(InvalidColumn); - if Assigned(Item) then - Invalidate(Item as TVirtualTreeColumn) - else - if Treeview.HandleAllocated then - begin - Treeview.UpdateHorizontalScrollBar(False); - Invalidate(nil); - Treeview.Invalidate; - end; - - if not (Treeview.IsUpdating) then - // This is mainly to let the designer know when a change occurs at design time which - // doesn't involve the object inspector (like column resizing with the mouse). - // This does NOT include design time code as the communication is done via an interface. - Treeview.UpdateDesigner; - end; + if not (vsInitialized in Node.Parent.States) then + InitNode(Node.Parent); + if (Node.Parent.CheckType = ctTriStateCheckBox) then begin + if (GetCheckState(Node.Parent) in [csUncheckedNormal, csUncheckedDisabled]) then + CheckState[Node] := csUncheckedNormal + else if (GetCheckState(Node.Parent) in [csCheckedNormal, csCheckedDisabled]) then + CheckState[Node] := csCheckedNormal; + end;//if + end;//if + InvalidateNode(Node); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.UpdatePositions(Force: Boolean = False); +procedure TBaseVirtualTree.SetChildCount(Node: PVirtualNode; NewChildCount: Cardinal); -// Recalculates the left border of every column and updates their position property according to the -// PostionToIndex array which primarily determines where each column is placed visually. +// Changes a node's child structure to accomodate the new child count. This is used to add or delete +// child nodes to/from the end of the node's child list. To insert or delete a specific node a separate +// routine is used. var - I, RunningPos: Integer; - + Remaining: Cardinal; + Index: Cardinal; + Child: PVirtualNode; + Count: Integer; + NewHeight: Integer; begin - if not (csDestroying in FHeader.Treeview.ComponentState) and not FNeedPositionsFix and (Force or (UpdateCount = 0)) then + if not (toReadOnly in FOptions.MiscOptions) then begin - RunningPos := 0; - for I := 0 to High(FPositionToIndex) do - with Items[FPositionToIndex[I]] do + if Node = nil then + Node := FRoot; + + Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be changed in UI thread.'); + if NewChildCount = 0 then + DeleteChildren(Node) + else + begin + // If nothing changed then do nothing. + if NewChildCount <> Node.ChildCount then begin - FPosition := I; - FLeft := RunningPos; - if coVisible in FOptions then - Inc(RunningPos, FWidth); - end; - FHeader.Treeview.UpdateHorizontalScrollBar(False); - end; -end; + InterruptValidation; -//---------------------------------------------------------------------------------------------------------------------- + if NewChildCount > Node.ChildCount then + begin + Remaining := NewChildCount - Node.ChildCount; + Count := Remaining; + NewHeight := Node.TotalHeight; -function TVirtualTreeColumns.Add: TVirtualTreeColumn; + // New nodes to add. + if Assigned(Node.LastChild) then + Index := Node.LastChild.Index + 1 + else + begin + Index := 0; + Include(Node.States, vsHasChildren); + end; + Node.States := Node.States - [vsAllChildrenHidden, vsHeightMeasured]; + if (vsExpanded in Node.States) and FullyVisible[Node] then + Inc(FVisibleCount, Count); // Do this before a possible init of the sub-nodes in DoMeasureItem() -begin - Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be changed in UI thread.'); - Result := TVirtualTreeColumn(inherited Add); -end; + // New nodes are by default always visible, so we don't need to check the visibility. + while Remaining > 0 do + begin + Child := MakeNewNode; + Child.Index := Index; + Child.PrevSibling := Node.LastChild; + if Assigned(Node.LastChild) then + Node.LastChild.NextSibling := Child; + Child.Parent := Node; + Node.LastChild := Child; + if Node.FirstChild = nil then + Node.FirstChild := Child; + Dec(Remaining); + Inc(Index); -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.AnimatedResize(Column: TColumnIndex; NewWidth: Integer); - -// Resizes the given column animated by scrolling the window DC. - -var - OldWidth: Integer; - DC: HDC; - I, - Steps, - DX: Integer; - HeaderScrollRect, - ScrollRect, - R: TRect; - - NewBrush, - LastBrush: HBRUSH; + if (toVariableNodeHeight in FOptions.MiscOptions) then + GetNodeHeight(Child); + Inc(NewHeight, Child.TotalHeight); + end; -begin - if not IsValidColumn(Column) then - Exit; // Just in case. + if vsExpanded in Node.States then + AdjustTotalHeight(Node, NewHeight, False); - // Make sure the width constrains are considered. - if NewWidth < Items[Column].MinWidth then - NewWidth := Items[Column].MinWidth; - if NewWidth > Items[Column].MaxWidth then - NewWidth := Items[Column].MaxWidth; + AdjustTotalCount(Node, Count, True); + Node.ChildCount := NewChildCount; + if (FUpdateCount = 0) and (toAutoSort in FOptions.AutoOptions) and (FHeader.SortColumn > InvalidColumn) then + Sort(Node, FHeader.SortColumn, FHeader.SortDirection, True); - OldWidth := Items[Column].Width; - // Nothing to do if the width is the same. - if OldWidth <> NewWidth then - begin - if not ( (hoDisableAnimatedResize in FHeader.Options) or - (coDisableAnimatedResize in Items[Column].Options) ) then - begin - DC := GetWindowDC(FHeader.Treeview.Handle); - with FHeader.Treeview do - try - Steps := 32; - DX := (NewWidth - OldWidth) div Steps; - - // Determination of the scroll rectangle is a bit complicated since we neither want - // to scroll the scrollbars nor the border of the treeview window. - HeaderScrollRect := FHeaderRect; - ScrollRect := HeaderScrollRect; - // Exclude the header itself from scrolling. - ScrollRect.Top := ScrollRect.Bottom; - ScrollRect.Bottom := ScrollRect.Top + ClientHeight; - ScrollRect.Right := ScrollRect.Left + ClientWidth; - with Items[Column] do - Inc(ScrollRect.Left, FLeft + FWidth); - HeaderScrollRect.Left := ScrollRect.Left; - HeaderScrollRect.Right := ScrollRect.Right; - - // When the new width is larger then avoid artefacts on the left hand side - // by deleting a small stripe - if NewWidth > OldWidth then - begin - R := ScrollRect; - NewBrush := CreateSolidBrush(ColorToRGB(Color)); - LastBrush := SelectObject(DC, NewBrush); - R.Right := R.Left + DX; - FillRect(DC, R, NewBrush); - SelectObject(DC, LastBrush); - DeleteObject(NewBrush); - end + InvalidateCache; + end//if NewChildCount > Node.ChildCount else begin - Inc(HeaderScrollRect.Left, DX); - Inc(ScrollRect.Left, DX); + // Nodes have to be deleted. + Remaining := Node.ChildCount - NewChildCount; + while Remaining > 0 do + begin + DeleteNode(Node.LastChild); + Dec(Remaining); + end; end; - for I := 0 to Steps - 1 do + if FUpdateCount = 0 then begin - ScrollDC(DC, DX, 0, HeaderScrollRect, HeaderScrollRect, 0, nil); - Inc(HeaderScrollRect.Left, DX); - ScrollDC(DC, DX, 0, ScrollRect, ScrollRect, 0, nil); - Inc(ScrollRect.Left, DX); - Sleep(1); + ValidateCache; + UpdateScrollBars(True); + Invalidate; end; - finally - ReleaseDC(Handle, DC); - end; - end; - Items[Column].Width := NewWidth; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.Assign(Source: TPersistent); - -begin - // Let the collection class assign the items. - inherited; - - if Source is TVirtualTreeColumns then - begin - // Copying the position array is the only needed task here. - FPositionToIndex := Copy(TVirtualTreeColumns(Source).FPositionToIndex, 0, MaxInt); - - // Make sure the left edges are correct after assignment. - FNeedPositionsFix := False; - UpdatePositions(True); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.Clear; - -begin - FClearing := True; - try - Header.Treeview.CancelEditNode; - // Since we're freeing all columns, the following have to be true when we're done. - FHoverIndex := NoColumn; - FDownIndex := NoColumn; - FTrackIndex := NoColumn; - FClickIndex := NoColumn; - FCheckBoxHit := False; - - with Header do - if not (hsLoading in FStates) then - begin - FAutoSizeIndex := NoColumn; - FMainColumn := NoColumn; - FSortColumn := NoColumn; + if Node = FRoot then + StructureChange(nil, crChildAdded) + else + StructureChange(Node, crChildAdded); end; - - with Header.Treeview do - if not (csLoading in ComponentState) then - FFocusedColumn := NoColumn; - - inherited Clear; - finally - FClearing := False; + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.ColumnFromPosition(P: TPoint; Relative: Boolean = True): TColumnIndex; - -// Determines the current column based on the position passed in P. +procedure TBaseVirtualTree.SetClipboardFormats(const Value: TClipboardFormats); var - I, Sum: Integer; + I: Integer; begin - Result := InvalidColumn; - - // The position must be within the header area, but we extend the vertical bounds to the entire treeview area. - if (P.X >= 0) and (P.Y >= 0) and (P.Y <= FHeader.TreeView.Height) then - with FHeader, Treeview do - begin - if Relative and (P.X >= GetVisibleFixedWidth) then - Sum := -FEffectiveOffsetX - else - Sum := 0; - - if UseRightToLeftAlignment then - Inc(Sum, ComputeRTLOffset(True)); - - for I := 0 to Count - 1 do - if coVisible in Items[FPositionToIndex[I]].Options then - begin - Inc(Sum, Items[FPositionToIndex[I]].Width); - if P.X < Sum then - begin - Result := FPositionToIndex[I]; - Break; - end; - end; - end; + // Add string by string instead doing an Assign or AddStrings because the list may return -1 for + // invalid entries which cause trouble for the standard implementation. + FClipboardFormats.Clear; + for I := 0 to Value.Count - 1 do + FClipboardFormats.Add(Value[I]); end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.ColumnFromPosition(PositionIndex: TColumnPosition): TColumnIndex; - -// Returns the index of the column at the given position. +procedure TBaseVirtualTree.SetColors(const Value: TVTColors); begin - if Integer(PositionIndex) < Length(FPositionToIndex) then - Result := FPositionToIndex[PositionIndex] - else - Result := NoColumn; + FColors.Assign(Value); end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.Equals(OtherColumnsObj: TObject): Boolean; - -// Compares itself with the given set of columns and returns True if all published properties are the same -// (including column order), otherwise False is returned. - -var - I: Integer; - OtherColumns : TVirtualTreeColumns; - +procedure TBaseVirtualTree.SetCheckImageKind(Value: TCheckImageKind); begin - if not (OtherColumnsObj is TVirtualTreeColumns) then - begin - Result := False; - Exit; - end; - - OtherColumns := TVirtualTreeColumns (OtherColumnsObj); - - // Same number of columns? - Result := OtherColumns.Count = Count; - if Result then + if (Value < Low(Value)) or (Value> High(Value)) then + Value := ckSystemDefault; + // property is deprecated. See issue #622 + if FCheckImageKind <> Value then begin - // Same order of columns? - Result := CompareMem(Pointer(FPositionToIndex), Pointer(OtherColumns.FPositionToIndex), - Length(FPositionToIndex) * SizeOf(TColumnIndex)); - if Result then - begin - for I := 0 to Count - 1 do - if not Items[I].Equals(OtherColumns[I]) then - begin - Result := False; - Break; - end; - end; + if FCheckImageKind = ckSystemDefault then + FreeAndNil(FCheckImages); + FCheckImageKind := Value; + if Value = ckCustom then + FCheckImages := FCustomCheckImages + else if HandleAllocated then + FCheckImages := CreateSystemImageSet(Self); + if HandleAllocated and (FUpdateCount = 0) and not (csLoading in ComponentState) then + InvalidateRect(Handle, nil, False); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.GetColumnBounds(Column: TColumnIndex; var Left, Right: Integer); - -// Returns the left and right bound of the given column. If Column is NoColumn then the entire client width is returned. +procedure TBaseVirtualTree.SetCustomCheckImages(const Value: TCustomImageList); begin - if Column <= NoColumn then - begin - Left := 0; - Right := FHeader.Treeview.ClientWidth; - end - else + if FCustomCheckImages <> Value then begin - Left := Items[Column].Left; - Right := Left + Items[Column].Width; - if FHeader.Treeview.UseRightToLeftAlignment then + if Assigned(FCustomCheckImages) then begin - Inc(Left, FHeader.Treeview.ComputeRTLOffset(True)); - Inc(Right, FHeader.Treeview.ComputeRTLOffset(True)); + FCustomCheckImages.UnRegisterChanges(FCustomCheckChangeLink); + FCustomCheckImages.RemoveFreeNotification(Self); + // Reset the internal check image list reference too, if necessary. + if FCheckImages = FCustomCheckImages then + FCheckImages := nil; end; + FCustomCheckImages := Value; + if Assigned(FCustomCheckImages) then + begin + // If custom check images are assigned, we switch the property CheckImageKind to ckCustom so that they are actually used + CheckImageKind := ckCustom; + FCustomCheckImages.RegisterChanges(FCustomCheckChangeLink); + FCustomCheckImages.FreeNotification(Self); + end + else + CheckImageKind := ckSystemDefault; + if not (csLoading in ComponentState) then + Invalidate; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetScrollWidth: Integer; - -// Returns the average width of all visible, non-fixed columns. If there is no such column the indent is returned. - -var - I: Integer; - ScrollColumnCount: Integer; +procedure TBaseVirtualTree.SetDefaultNodeHeight(Value: Cardinal); begin - - Result := 0; - - ScrollColumnCount := 0; - for I := 0 to FHeader.Columns.Count - 1 do + if Value = 0 then + Value := 18; + if FDefaultNodeHeight <> Value then begin - if ([coVisible, coFixed] * FHeader.Columns[I].Options = [coVisible]) then + Inc(Integer(FRoot.TotalHeight), Integer(Value) - Integer(FDefaultNodeHeight)); + Inc(SmallInt(FRoot.NodeHeight), Integer(Value) - Integer(FDefaultNodeHeight)); + FDefaultNodeHeight := Value; + InvalidateCache; + if (FUpdateCount = 0) and HandleAllocated and not (csLoading in ComponentState) then begin - Inc(Result, FHeader.Columns[I].Width); - Inc(ScrollColumnCount); + ValidateCache; + UpdateScrollBars(True); + ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, True); + Invalidate; end; end; - - if ScrollColumnCount > 0 then // use average width - Result := Round(Result / ScrollColumnCount) - else // use indent - Result := Integer(FHeader.Treeview.FIndent); - end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetFirstVisibleColumn(ConsiderAllowFocus: Boolean = False): TColumnIndex; - -// Returns the index of the first visible column or "InvalidColumn" if either no columns are defined or -// all columns are hidden. -// If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed. - -var - I: Integer; +procedure TBaseVirtualTree.SetDisabled(Node: PVirtualNode; Value: Boolean); begin - Result := InvalidColumn; - if (UpdateCount > 0) or (csLoading in Header.TreeView.ComponentState) then - exit; // See issue #760 - for I := 0 to Count - 1 do - if (coVisible in Items[FPositionToIndex[I]].Options) and - ( (not ConsiderAllowFocus) or - (coAllowFocus in Items[FPositionToIndex[I]].Options) - ) then - begin - Result := FPositionToIndex[I]; - Break; - end; + if Assigned(Node) and (Value xor (vsDisabled in Node.States)) then + begin + if Value then + Include(Node.States, vsDisabled) + else + Exclude(Node.States, vsDisabled); + + if FUpdateCount = 0 then + InvalidateNode(Node); + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetLastVisibleColumn(ConsiderAllowFocus: Boolean = False): TColumnIndex; - -// Returns the index of the last visible column or "InvalidColumn" if either no columns are defined or -// all columns are hidden. -// If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed. - -var - I: Integer; - +procedure TBaseVirtualTree.SetDoubleBuffered(const Value: Boolean); begin - Result := InvalidColumn; - if (UpdateCount > 0) or (csLoading in Header.TreeView.ComponentState) then - exit; // See issue #760 - for I := Count - 1 downto 0 do - if (coVisible in Items[FPositionToIndex[I]].Options) and - ( (not ConsiderAllowFocus) or - (coAllowFocus in Items[FPositionToIndex[I]].Options) - ) then - begin - Result := FPositionToIndex[I]; - Break; - end; + // empty by intention, we do our own buffering end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetFirstColumn: TColumnIndex; - -// Returns the first column in display order. - +function TBaseVirtualTree.GetDoubleBuffered: Boolean; begin - if Count = 0 then - Result := InvalidColumn - else - Result := FPositionToIndex[0]; + Result := True; // we do our own buffering end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetNextColumn(Column: TColumnIndex): TColumnIndex; - -// Returns the next column in display order. Column is the index of an item in the collection (a column). - -var - Position: Integer; +procedure TBaseVirtualTree.SetEmptyListMessage(const Value: string); begin - if Column < 0 then - Result := InvalidColumn - else + if Value <> EmptyListMessage then begin - Position := Items[Column].Position; - if Position < Count - 1 then - Result := FPositionToIndex[Position + 1] - else - Result := InvalidColumn; + FEmptyListMessage := Value; + Invalidate; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetNextVisibleColumn(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex; - -// Returns the next visible column in display order, Column is an index into the columns list. -// If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed. +procedure TBaseVirtualTree.SetExpanded(Node: PVirtualNode; Value: Boolean); begin - Result := Column; - repeat - Result := GetNextColumn(Result); - until (Result = InvalidColumn) or - ( (coVisible in Items[Result].Options) and - ( (not ConsiderAllowFocus) or - (coAllowFocus in Items[Result].Options) - ) - ); + if Assigned(Node) and (Node <> FRoot) and (Value xor (vsExpanded in Node.States)) then + ToggleNode(Node); end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetPreviousColumn(Column: TColumnIndex): TColumnIndex; - -// Returns the previous column in display order, Column is an index into the columns list. - -var - Position: Integer; +procedure TBaseVirtualTree.SetFocusedColumn(Value: TColumnIndex); begin - if Column < 0 then - Result := InvalidColumn - else + if (FFocusedColumn <> Value) and + DoFocusChanging(FFocusedNode, FFocusedNode, FFocusedColumn, Value) then begin - Position := Items[Column].Position; - if Position > 0 then - Result := FPositionToIndex[Position - 1] - else - Result := InvalidColumn; + CancelEditNode; + InvalidateColumn(FFocusedColumn); + InvalidateColumn(Value); + FFocusedColumn := Value; + if Assigned(FFocusedNode) and not (toDisableAutoscrollOnFocus in FOptions.AutoOptions) then + begin + if ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, True) then + InvalidateNode(FFocusedNode); + end; + + if Assigned(FDropTargetNode) then + InvalidateNode(FDropTargetNode); + + DoFocusChange(FFocusedNode, FFocusedColumn); end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetPreviousVisibleColumn(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex; +procedure TBaseVirtualTree.SetFocusedNode(Value: PVirtualNode); -// Returns the previous visible column in display order, Column is an index into the columns list. -// If ConsiderAllowFocus is True then the column has not only to be visible but also focus has to be allowed. +var + WasDifferent: Boolean; begin - Result := Column; - repeat - Result := GetPreviousColumn(Result); - until (Result = InvalidColumn) or - ( (coVisible in Items[Result].Options) and - ( (not ConsiderAllowFocus) or - (coAllowFocus in Items[Result].Options) - ) - ); + WasDifferent := Value <> FFocusedNode; + DoFocusNode(Value, True); + // Do change event only if there was actually a change. + if WasDifferent and (FFocusedNode = Value) then + DoFocusChange(FFocusedNode, FFocusedColumn); end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetVisibleColumns: TColumnsArray; +procedure TBaseVirtualTree.SetFullyVisible(Node: PVirtualNode; Value: Boolean); -// Returns a list of all currently visible columns in actual order. - -var - I, Counter: Integer; +// This method ensures that a node is visible and all its parent nodes are expanded and also visible +// if Value is True. Otherwise the visibility flag of the node is reset but the expand state +// of the parent nodes stays untouched. begin - SetLength(Result, Count); - Counter := 0; + Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter'); - for I := 0 to Count - 1 do - if coVisible in Items[FPositionToIndex[I]].Options then - begin - Result[Counter] := Items[FPositionToIndex[I]]; - Inc(Counter); - end; - // Set result length to actual visible count. - SetLength(Result, Counter); + IsVisible[Node] := Value; + if Value then + begin + repeat + Node := Node.Parent; + if Node = FRoot then + Break; + if not (vsExpanded in Node.States) then + ToggleNode(Node); + if not (vsVisible in Node.States) then + IsVisible[Node] := True; + until False; + end; + ScrollIntoView(Node, False); end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.GetVisibleFixedWidth: Integer; - -// Determines the horizontal space all visible and fixed columns occupy. - -var - I: Integer; +procedure TBaseVirtualTree.SetHasChildren(Node: PVirtualNode; Value: Boolean); begin - Result := 0; - for I := 0 to Count - 1 do + if Assigned(Node) and not (toReadOnly in FOptions.MiscOptions) then begin - if Items[I].Options * [coVisible, coFixed] = [coVisible, coFixed] then - Inc(Result, Items[I].Width); + if Value then + Include(Node.States, vsHasChildren) + else + begin + Exclude(Node.States, vsHasChildren); + DeleteChildren(Node); + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVirtualTreeColumns.IsValidColumn(Column: TColumnIndex): Boolean; - -// Determines whether the given column is valid or not, that is, whether it is one of the current columns. +procedure TBaseVirtualTree.SetHeader(const Value: TVTHeader); begin - Result := (Column > NoColumn) and (Column < Count); + FHeader.Assign(Value); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.LoadFromStream(const Stream: TStream; Version: Integer); - -var - I, - ItemCount: Integer; +procedure TBaseVirtualTree.SetHotNode(Value: PVirtualNode); begin - Clear; - Stream.ReadBuffer(ItemCount, SizeOf(ItemCount)); - // number of columns - if ItemCount > 0 then - begin - BeginUpdate; - try - for I := 0 to ItemCount - 1 do - Add.LoadFromStream(Stream, Version); - SetLength(FPositionToIndex, ItemCount); - Stream.ReadBuffer(FPositionToIndex[0], ItemCount * SizeOf(TColumnIndex)); - UpdatePositions(True); - finally - EndUpdate; - end; - end; - - // Data introduced with header stream version 5 - if Version > 4 then - Stream.ReadBuffer(FDefaultWidth, SizeOf(FDefaultWidth)); + FCurrentHotNode := Value; end; + //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.PaintHeader(DC: HDC; R: TRect; HOffset: Integer); +procedure TBaseVirtualTree.SetFiltered(Node: PVirtualNode; Value: Boolean); -// Backward compatible header paint method. This method takes care of visually moving floating columns +// Sets the 'filtered' flag of the given node according to Value and updates all dependent states. var - VisibleFixedWidth: Integer; - RTLOffset: Integer; - - procedure PaintFixedArea; - - begin - if VisibleFixedWidth > 0 then - PaintHeader(FHeaderBitmap.Canvas, - Rect(0, 0, Min(R.Right, VisibleFixedWidth), R.Bottom - R.Top), - Point(R.Left, R.Top), RTLOffset); - end; + NeedUpdate: Boolean; begin - // Adjust size of the header bitmap - with TWithSafeRect(FHeader.Treeview.FHeaderRect) do - begin - FHeaderBitmap.SetSize(Max(Right, R.Right - R.Left), Bottom); - end; - - VisibleFixedWidth := GetVisibleFixedWidth; - - // Consider right-to-left directionality. - if FHeader.TreeView.UseRightToLeftAlignment then - RTLOffset := FHeader.Treeview.ComputeRTLOffset - else - RTLOffset := 0; - - if RTLOffset = 0 then - PaintFixedArea; - - // Paint the floating part of the header. - PaintHeader(FHeaderBitmap.Canvas, - Rect(VisibleFixedWidth - HOffset, 0, R.Right + VisibleFixedWidth - HOffset, R.Bottom - R.Top), - Point(R.Left + VisibleFixedWidth, R.Top), RTLOffset); - - // In case of right-to-left directionality we paint the fixed part last. - if RTLOffset <> 0 then - PaintFixedArea; - - // Blit the result to target. - with TWithSafeRect(R) do - BitBlt(DC, Left, Top, Right - Left, Bottom - Top, FHeaderBitmap.Canvas.Handle, Left, Top, SRCCOPY); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVirtualTreeColumns.PaintHeader(TargetCanvas: TCanvas; R: TRect; const Target: TPoint; - RTLOffset: Integer = 0); - -// Main paint method to draw the header. -// This procedure will paint the a slice (given in R) out of HeaderRect into TargetCanvas starting at position Target. -// This function does not offer the option to visually move floating columns due to scrolling. To accomplish this you -// need to call this method twice. - -var - Run: TColumnIndex; - RightBorderFlag, - NormalButtonStyle, - NormalButtonFlags, - PressedButtonStyle, - PressedButtonFlags, - RaisedButtonStyle, - RaisedButtonFlags: Cardinal; - Images: TCustomImageList; - OwnerDraw, - AdvancedOwnerDraw: Boolean; - PaintInfo: THeaderPaintInfo; - RequestedElements, - ActualElements: THeaderPaintElements; - - //--------------- local functions ------------------------------------------- - - procedure PrepareButtonStyles; - - // Prepare the button styles and flags for later usage. - - begin - RaisedButtonStyle := 0; - RaisedButtonFlags := 0; - case FHeader.Style of - hsThickButtons: - begin - NormalButtonStyle := BDR_RAISEDINNER or BDR_RAISEDOUTER; - NormalButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_MIDDLE or BF_SOFT or BF_ADJUST; - PressedButtonStyle := BDR_RAISEDINNER or BDR_RAISEDOUTER; - PressedButtonFlags := NormalButtonFlags or BF_RIGHT or BF_FLAT or BF_ADJUST; - end; - hsFlatButtons: - begin - NormalButtonStyle := BDR_RAISEDINNER; - NormalButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_MIDDLE or BF_ADJUST; - PressedButtonStyle := BDR_SUNKENOUTER; - PressedButtonFlags := BF_RECT or BF_MIDDLE or BF_ADJUST; - end; - else - // hsPlates or hsXPStyle, values are not used in the latter case - begin - NormalButtonStyle := BDR_RAISEDINNER; - NormalButtonFlags := BF_RECT or BF_MIDDLE or BF_SOFT or BF_ADJUST; - PressedButtonStyle := BDR_SUNKENOUTER; - PressedButtonFlags := BF_RECT or BF_MIDDLE or BF_ADJUST; - RaisedButtonStyle := BDR_RAISEDINNER; - RaisedButtonFlags := BF_LEFT or BF_TOP or BF_BOTTOM or BF_MIDDLE or BF_ADJUST; - end; - end; - end; - - //--------------------------------------------------------------------------- - - procedure DrawBackground; + Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); - // Draw the header background. + // Initialize the node if necessary as this might change the filtered state. + if not (vsInitialized in Node.States) then + InitNode(Node); - var - BackgroundRect: TRect; - Details: TThemedElementDetails; - Theme: HTheme; + if Value <> (vsFiltered in Node.States) then begin - BackgroundRect := Rect(Target.X, Target.Y, Target.X + R.Right - R.Left, Target.Y + FHeader.Height); - - with TargetCanvas do - begin - if hpeBackground in RequestedElements then - begin - PaintInfo.PaintRectangle := BackgroundRect; - FHeader.Treeview.DoAdvancedHeaderDraw(PaintInfo, [hpeBackground]); - end - else + InterruptValidation; + NeedUpdate := False; + if Value then + begin + Include(Node.States, vsFiltered); + if not (toShowFilteredNodes in FOptions.PaintOptions) then begin - if (FHeader.Treeview.VclStyleEnabled and (seClient in FHeader.Treeview.StyleElements)) then - begin - Details := StyleServices.GetElementDetails(thHeaderItemRightNormal); - StyleServices.DrawElement(Handle, Details, BackgroundRect, @BackgroundRect {$IF CompilerVersion >= 34}, FHeader.Treeview.FCurrentPPI{$IFEND}); - end - else - if tsUseThemes in FHeader.Treeview.FStates then - begin - Theme := OpenThemeData(FHeader.Treeview.Handle, 'HEADER'); - DrawThemeBackground(Theme, Handle, HP_HEADERITEM, HIS_NORMAL, BackgroundRect, nil); - CloseThemeData(THeme); - end + if (vsInitializing in Node.States) and not (vsHasChildren in Node.States) then + AdjustTotalHeight(Node, 0, False) else + AdjustTotalHeight(Node, -Integer(NodeHeight[Node]), True); + if FullyVisible[Node] then begin - Brush.Color := FHeader.Background; - FillRect(BackgroundRect); + Dec(FVisibleCount); + NeedUpdate := True; end; + if FocusedNode = Node then + FocusedNode := nil; end; - end; - end; - - //--------------------------------------------------------------------------- - - procedure PaintColumnHeader(AColumn: TColumnIndex; ATargetRect: TRect); - - // Draw a single column to TargetRect. The clipping rect needs to be set before - // this procedure is called. - - var - SavedDC: Integer; - ColCaptionText: string; - ColImageInfo: TVTImageInfo; - Glyph: TThemedHeader; - Details: TThemedElementDetails; - WrapCaption: Boolean; - DrawFormat: Cardinal; - Pos: TRect; - DrawHot: Boolean; - ImageWidth: Integer; - Theme: HTheme; - IdState: Integer; - begin - ColImageInfo.Ghosted := False; - PaintInfo.Column := Items[AColumn]; - with PaintInfo, Column do - begin - IsHoverIndex := (AColumn = FHoverIndex) and (hoHotTrack in FHeader.Options) and (coEnabled in FOptions); - IsDownIndex := (AColumn = FDownIndex) and not FCheckBoxHit; - - if (coShowDropMark in FOptions) and (AColumn = FDropTarget) and (AColumn <> FDragIndex) then - begin - if FDropBefore then - DropMark := dmmLeft - else - DropMark := dmmRight; - end - else - DropMark := dmmNone; - //Fix for issue 643 - //Do not show the left drop mark if the position to drop is just preceding the target which means - //the dragged column will stay where it is - if (DropMark = dmmLeft) and (Items[FDragIndex].Position = TColumnPosition(Max(Integer(Items[FDropTarget].Position) - 1, 0))) - then - DropMark := dmmNone + if FUpdateCount = 0 then + DetermineHiddenChildrenFlag(Node.Parent) else - //Do not show the right drop mark if the position to drop is just following the target which means - //the dragged column will stay where it is - if (DropMark = dmmRight) and (Items[FDragIndex].Position = Items[FDropTarget].Position + 1) - then - DropMark := dmmNone; - - IsEnabled := (coEnabled in FOptions) and (FHeader.Treeview.Enabled); - ShowHeaderGlyph := (hoShowImages in FHeader.Options) and ((Assigned(Images) and (FImageIndex > -1)) or FCheckBox); - ShowSortGlyph := (AColumn = FHeader.SortColumn) and (hoShowSortGlyphs in FHeader.Options); - WrapCaption := coWrapCaption in FOptions; - - PaintRectangle := ATargetRect; - - // This path for text columns or advanced owner draw. - if (Style = vsText) or not OwnerDraw or AdvancedOwnerDraw then + Include(FStates, tsUpdateHiddenChildrenNeeded); + end + else + begin + Exclude(Node.States, vsFiltered); + if not (toShowFilteredNodes in FOptions.PaintOptions) then begin - // See if the application wants to draw part of the header itself. - RequestedElements := []; - if AdvancedOwnerDraw then - begin - PaintInfo.Column := Items[AColumn]; - FHeader.Treeview.DoHeaderDrawQueryElements(PaintInfo, RequestedElements); - end; - - if ShowRightBorder or (AColumn < Count - 1) then - RightBorderFlag := BF_RIGHT - else - RightBorderFlag := 0; - - if hpeBackground in RequestedElements then - FHeader.Treeview.DoAdvancedHeaderDraw(PaintInfo, [hpeBackground]) - else - begin - if FHeader.Treeview.VclStyleEnabled and (seClient in FHeader.Treeview.StyleElements) then - begin - if IsDownIndex then - Details := StyleServices.GetElementDetails(thHeaderItemPressed) - else - if IsHoverIndex then - Details := StyleServices.GetElementDetails(thHeaderItemHot) - else - Details := StyleServices.GetElementDetails(thHeaderItemNormal); - StyleServices.DrawElement(TargetCanvas.Handle, Details, PaintRectangle, @PaintRectangle{$IF CompilerVersion >= 34}, FHeader.TreeView.FCurrentPPI{$IFEND}); - end - else - begin - if tsUseThemes in FHeader.Treeview.FStates then - begin - Theme := OpenThemeData(FHeader.Treeview.Handle, 'HEADER'); - if IsDownIndex then - IdState := HIS_PRESSED - else - if IsHoverIndex then - IdState := HIS_HOT - else - IdState := HIS_NORMAL; - DrawThemeBackground(Theme, TargetCanvas.Handle, HP_HEADERITEM, IdState, PaintRectangle, nil); - CloseThemeData(Theme); - end - else - if IsDownIndex then - DrawEdge(TargetCanvas.Handle, PaintRectangle, PressedButtonStyle, PressedButtonFlags) - else - // Plates have the special case of raising on mouse over. - if (FHeader.Style = hsPlates) and IsHoverIndex and - (coAllowClick in FOptions) and (coEnabled in FOptions) then - DrawEdge(TargetCanvas.Handle, PaintRectangle, RaisedButtonStyle, - RaisedButtonFlags or RightBorderFlag) - else - DrawEdge(TargetCanvas.Handle, PaintRectangle, NormalButtonStyle, - NormalButtonFlags or RightBorderFlag); - end; - end; - - PaintRectangle := ATargetRect; - - // calculate text and glyph position - InflateRect(PaintRectangle, -2, -2); - DrawFormat := DT_TOP or DT_NOPREFIX; - case CaptionAlignment of - taLeftJustify : DrawFormat := DrawFormat or DT_LEFT; - taRightJustify : DrawFormat := DrawFormat or DT_RIGHT; - taCenter : DrawFormat := DrawFormat or DT_CENTER; - end; - if UseRightToLeftReading then - DrawFormat := DrawFormat + DT_RTLREADING; - ComputeHeaderLayout(PaintInfo, DrawFormat); - - // Move glyph and text one pixel to the right and down to simulate a pressed button. - if IsDownIndex then - begin - OffsetRect(TextRectangle, 1, 1); - Inc(GlyphPos.X); - Inc(GlyphPos.Y); - Inc(SortGlyphPos.X); - Inc(SortGlyphPos.Y); - end; - - // Advanced owner draw allows to paint elements, which would normally not be painted (because of space - // limitations, empty captions etc.). - ActualElements := RequestedElements * [hpeHeaderGlyph, hpeSortGlyph, hpeDropMark, hpeText, hpeOverlay]; - - // main glyph - FHasImage := False; - if Assigned(Images) then - ImageWidth := Images.Width - else - ImageWidth := 0; - - if not (hpeHeaderGlyph in ActualElements) and ShowHeaderGlyph and - (not ShowSortGlyph or (FBiDiMode <> bdLeftToRight) or (GlyphPos.X + ImageWidth <= SortGlyphPos.X) ) then - begin - if not FCheckBox then - begin - ColImageInfo.Images := Images; - Images.Draw(TargetCanvas, GlyphPos.X, GlyphPos.Y, FImageIndex, IsEnabled); - end - else - begin - with Header.Treeview do - begin - ColImageInfo.Images := FCheckImages; - ColImageInfo.Index := GetCheckImage(nil, FCheckType, FCheckState, IsEnabled); - ColImageInfo.XPos := GlyphPos.X; - ColImageInfo.YPos := GlyphPos.Y; - PaintCheckImage(TargetCanvas, ColImageInfo, False); - end; - end; - - FHasImage := True; - with TWithSafeRect(FImageRect) do - begin - Left := GlyphPos.X; - Top := GlyphPos.Y; - Right := Left + ColImageInfo.Images.Width; - Bottom := Top + ColImageInfo.Images.Height; - end; - end; - - // caption - if WrapCaption then - ColCaptionText := FCaptionText - else - ColCaptionText := Text; - if IsHoverIndex and FHeader.Treeview.VclStyleEnabled then - DrawHot := True - else - DrawHot := (IsHoverIndex and (hoHotTrack in FHeader.Options) and not(tsUseThemes in FHeader.Treeview.FStates)); - if not(hpeText in ActualElements) and (Length(Text) > 0) then - DrawButtonText(TargetCanvas.Handle, ColCaptionText, TextRectangle, IsEnabled, DrawHot, DrawFormat, WrapCaption); - - // sort glyph - if not (hpeSortGlyph in ActualElements) and ShowSortGlyph then + AdjustTotalHeight(Node, Integer(NodeHeight[Node]), True); + if FullyVisible[Node] then begin - if tsUseExplorerTheme in FHeader.Treeview.FStates then - begin - Pos.TopLeft := SortGlyphPos; - Pos.Right := Pos.Left + SortGlyphSize.cx; - Pos.Bottom := Pos.Top + SortGlyphSize.cy; - if FHeader.SortDirection = sdAscending then - Glyph := thHeaderSortArrowSortedUp - else - Glyph := thHeaderSortArrowSortedDown; - Details := StyleServices.GetElementDetails(Glyph); - if not StyleServices.DrawElement(TargetCanvas.Handle, Details, Pos, @Pos {$IF CompilerVersion >= 34}, FHeader.TreeView.FCurrentPPI {$IFEND}) then - PaintInfo.DrawSortArrow(FHeader.SortDirection); - end - else - begin - PaintInfo.DrawSortArrow(FHeader.SortDirection); - end; + Inc(FVisibleCount); + NeedUpdate := True; end; + end; - // Show an indication if this column is the current drop target in a header drag operation. - if not (hpeDropMark in ActualElements) and (DropMark <> dmmNone) then - begin - PaintInfo.DrawDropMark(); - end; + if vsVisible in Node.States then + // Update the hidden children flag of the parent. + // Since this node is now visible we simply have to remove the flag. + Exclude(Node.Parent.States, vsAllChildrenHidden); + end; - if ActualElements <> [] then - begin - SavedDC := SaveDC(TargetCanvas.Handle); - FHeader.Treeview.DoAdvancedHeaderDraw(PaintInfo, ActualElements); - RestoreDC(TargetCanvas.Handle, SavedDC); - end; - end - else // Let application draw the header. - FHeader.Treeview.DoHeaderDraw(TargetCanvas, Items[AColumn], PaintRectangle, IsHoverIndex, IsDownIndex, - DropMark); + InvalidateCache; + if NeedUpdate and (FUpdateCount = 0) then + begin + ValidateCache; + UpdateScrollBars(True); + Invalidate; end; end; +end; - //--------------- end local functions --------------------------------------- - -var - TargetRect: TRect; - MaxX: Integer; +//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.SetImages(const Value: TCustomImageList); begin - if IsRectEmpty(R) then - Exit; - - // If both draw posibillities are specified then prefer the advanced way. - AdvancedOwnerDraw := (hoOwnerDraw in FHeader.Options) and Assigned(FHeader.Treeview.OnAdvancedHeaderDraw) and - Assigned(FHeader.Treeview.FOnHeaderDrawQueryElements) and not (csDesigning in FHeader.Treeview.ComponentState); - OwnerDraw := (hoOwnerDraw in FHeader.Options) and Assigned(FHeader.Treeview.OnHeaderDraw) and - not (csDesigning in FHeader.Treeview.ComponentState) and not AdvancedOwnerDraw; - - ZeroMemory(@PaintInfo, SizeOf(PaintInfo)); - PaintInfo.TargetCanvas := TargetCanvas; - - with PaintInfo, TargetCanvas do + if FImages <> Value then begin - // Use shortcuts for the images and the font. - Images := FHeader.Images; - Font := FHeader.Font; - - PrepareButtonStyles; - - // At first, query the application which parts of the header it wants to draw on its own. - RequestedElements := []; - if AdvancedOwnerDraw then + if Assigned(FImages) then begin - PaintRectangle := R; - Column := nil; - FHeader.Treeview.DoHeaderDrawQueryElements(PaintInfo, RequestedElements); + FImages.UnRegisterChanges(FImageChangeLink); + FImages.RemoveFreeNotification(Self); end; - - // Draw the background. - DrawBackground; - - // Now that we have drawn the background, we apply the header's dimensions to R. - R := Rect(Max(R.Left, 0), Max(R.Top, 0), Min(R.Right, TotalWidth), Min(R.Bottom, Header.Height)); - - // Determine where to stop. - MaxX := Target.X + R.Right - R.Left - //Fixes issues #544, #427 -- MaxX should also shift on BidiMode bdRightToLeft - + RTLOffset; //added for fix - - // Determine the start column. - Run := ColumnFromPosition(Point(R.Left + RTLOffset, 0), False); - if Run <= NoColumn then - Exit; - - TargetRect.Top := Target.Y; - TargetRect.Bottom := Target.Y + R.Bottom - R.Top; - TargetRect.Left := Target.X - R.Left + Items[Run].Left + RTLOffset; - // TargetRect.Right will be set in the loop - - ShowRightBorder := (FHeader.Style = hsThickButtons) or not (hoAutoResize in FHeader.Options) or - (FHeader.Treeview.BevelKind = bkNone); - - // Now go for each button. - while (Run > NoColumn) and (TargetRect.Left < MaxX) do + FImages := Value; + if Assigned(FImages) then begin - TargetRect.Right := TargetRect.Left + Items[Run].Width; - - // create a clipping rect to limit painting to button area - ClipCanvas(TargetCanvas, Rect(Max(TargetRect.Left, Target.X), Target.Y + R.Top, - Min(TargetRect.Right, MaxX), TargetRect.Bottom)); - - PaintColumnHeader(Run, TargetRect); - - SelectClipRgn(Handle, 0); - - TargetRect.Left := TargetRect.Right; - Run := GetNextVisibleColumn(Run); + FImages.RegisterChanges(FImageChangeLink); + FImages.FreeNotification(Self); end; + if not (csLoading in ComponentState) then + Invalidate; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVirtualTreeColumns.SaveToStream(const Stream: TStream); - -var - I: Integer; +procedure TBaseVirtualTree.SetIndent(Value: Cardinal); begin - I := Count; - Stream.WriteBuffer(I, SizeOf(I)); - if I > 0 then + if FIndent <> Value then begin - for I := 0 to Count - 1 do - TVirtualTreeColumn(Items[I]).SaveToStream(Stream); - - Stream.WriteBuffer(FPositionToIndex[0], Count * SizeOf(TColumnIndex)); - end; - - // Data introduced with header stream version 5. - Stream.WriteBuffer(DefaultWidth, SizeOf(DefaultWidth)); -end; - -//---------------------------------------------------------------------------------------------------------------------- + FIndent := Value; + if not (csLoading in ComponentState) and (FUpdateCount = 0) and HandleAllocated then + begin + UpdateScrollBars(True); + Invalidate; + end; + end; +end; -function TVirtualTreeColumns.TotalWidth: Integer; +//---------------------------------------------------------------------------------------------------------------------- -var - LastColumn: TColumnIndex; +procedure TBaseVirtualTree.SetLineMode(const Value: TVTLineMode); begin - Result := 0; - if (Count > 0) and (Length(FPositionToIndex) > 0) then + if FLineMode <> Value then begin - LastColumn := FPositionToIndex[Count - 1]; - if not (coVisible in Items[LastColumn].Options) then - LastColumn := GetPreviousVisibleColumn(LastColumn); - if LastColumn > NoColumn then - with Items[LastColumn] do - Result := FLeft + FWidth; + FLineMode := Value; + if HandleAllocated and not (csLoading in ComponentState) then + Invalidate; end; end; -//----------------- TVTFixedAreaConstraints ---------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- -constructor TVTFixedAreaConstraints.Create(AOwner: TVTHeader); +procedure TBaseVirtualTree.SetLineStyle(const Value: TVTLineStyle); begin - inherited Create; - FMaxWidthPercent := 95; - FHeader := AOwner; + if FLineStyle <> Value then + begin + FLineStyle := Value; + if not (csLoading in ComponentState) then + begin + PrepareBitmaps(False, True); + if HandleAllocated then + Invalidate; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTFixedAreaConstraints.SetConstraints(Index: Integer; Value: TVTConstraintPercent); +procedure TBaseVirtualTree.SetMargin(Value: Integer); begin - case Index of - 0: - if Value <> FMaxHeightPercent then - begin - FMaxHeightPercent := Value; - if (Value > 0) and (Value < FMinHeightPercent) then - FMinHeightPercent := Value; - Change; - end; - 1: - if Value <> FMaxWidthPercent then - begin - FMaxWidthPercent := Value; - if (Value > 0) and (Value < FMinWidthPercent) then - FMinWidthPercent := Value; - Change; - end; - 2: - if Value <> FMinHeightPercent then - begin - FMinHeightPercent := Value; - if (FMaxHeightPercent > 0) and (Value > FMaxHeightPercent) then - FMaxHeightPercent := Value; - Change; - end; - 3: - if Value <> FMinWidthPercent then - begin - FMinWidthPercent := Value; - if (FMaxWidthPercent > 0) and (Value > FMaxWidthPercent) then - FMaxWidthPercent := Value; - Change; - end; + if FMargin <> Value then + begin + FMargin := Value; + if HandleAllocated and not (csLoading in ComponentState) then + Invalidate; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTFixedAreaConstraints.Change; +procedure TBaseVirtualTree.SetMultiline(Node: PVirtualNode; const Value: Boolean); begin - if Assigned(FOnChange) then - FOnChange(Self); + if Assigned(Node) and (Node <> FRoot) then + if Value <> (vsMultiline in Node.States) then + begin + if Value then + Include(Node.States, vsMultiline) + else + Exclude(Node.States, vsMultiline); + + if FUpdateCount = 0 then + InvalidateNode(Node); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTFixedAreaConstraints.Assign(Source: TPersistent); +procedure TBaseVirtualTree.SetNodeAlignment(const Value: TVTNodeAlignment); begin - if Source is TVTFixedAreaConstraints then + if FNodeAlignment <> Value then begin - FMaxHeightPercent := TVTFixedAreaConstraints(Source).FMaxHeightPercent; - FMaxWidthPercent := TVTFixedAreaConstraints(Source).FMaxWidthPercent; - FMinHeightPercent := TVTFixedAreaConstraints(Source).FMinHeightPercent; - FMinWidthPercent := TVTFixedAreaConstraints(Source).FMinWidthPercent; - Change; - end - else - inherited; + FNodeAlignment := Value; + if HandleAllocated and not (csReading in ComponentState) then + Invalidate; + end; end; -//----------------- TVTHeader ----------------------------------------------------------------------------------------- - -constructor TVTHeader.Create(AOwner: TBaseVirtualTree); +//---------------------------------------------------------------------------------------------------------------------- -begin - inherited Create; - FOwner := AOwner; - FColumns := GetColumnsClass.Create(Self); - FHeight := 19; - FDefaultHeight := FHeight; - FMinHeight := 10; - FMaxHeight := 10000; - FFont := TFont.Create; - FFont.OnChange := FontChanged; - FParentFont := True; - FBackgroundColor := clBtnFace; - FOptions := [hoColumnResize, hoDrag, hoShowSortGlyphs]; +procedure TBaseVirtualTree.SetNodeData(pNode: PVirtualNode; pUserData: Pointer); - FImageChangeLink := TChangeLink.Create; - FImageChangeLink.OnChange := ImageListChange; + // Can be used to set user data of a PVirtualNode with the size of a pointer, useful for setting + // A pointer to a record or a reference to a class instance. - FSortColumn := NoColumn; - FSortDirection := sdAscending; - FMainColumn := NoColumn; +var + NodeData: PPointer; +begin + // Check if there is initial user data and there is also enough user data space allocated. + Assert(FNodeDataSize >= SizeOf(Pointer), Self.Classname + ': Cannot set initial user data because there is not enough user data space allocated.'); + NodeData := PPointer(@pNode.Data); + NodeData^ := pUserData; + Include(pNode.States, vsOnFreeNodeCallRequired); +end; - FDragImage := TVTDragImage.Create(AOwner); - with FDragImage do - begin - Fade := False; - PreBlendBias := -50; - Transparency := 140; - end; +procedure TBaseVirtualTree.SetNodeData(pNode: PVirtualNode; pUserData: T); - fSplitterHitTolerance := 8; - FFixedAreaConstraints := TVTFixedAreaConstraints.Create(Self); - FFixedAreaConstraints.OnChange := FixedAreaConstraintsChanged; + // Can be used to set user data of a PVirtualNode to a class instance. - FDoingAutoFitColumns := false; +begin + pNode.SetData(pUserData); end; -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.SetNodeData(pNode: PVirtualNode; const pUserData: IInterface); -destructor TVTHeader.Destroy; + // Can be used to set user data of a PVirtualNode to a class instance, + // will take care about reference counting. begin - FDragImage.Free; - FFixedAreaConstraints.Free; - FImageChangeLink.Free; - FFont.Free; - FColumns.Clear; // TCollection's Clear method is not virtual, so we have to call our own Clear method manually. - FColumns.Free; - inherited; + pNode.SetData(pUserData); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.FontChanged(Sender: TObject); -begin - inherited; - AutoScale(); -end; +procedure TBaseVirtualTree.SetNodeDataSize(Value: Integer); -procedure TVTHeader.AutoScale(); var - I: Integer; - lMaxHeight: Integer; + LastRootCount: Cardinal; + begin - if toAutoChangeScale in Treeview.TreeOptions.AutoOptions then + if Value < -1 then + Value := -1; + if FNodeDataSize <> Value then begin - // Ensure a minimum header size based on the font, so that all text is visible. - // First find the largest Columns[].Spacing - lMaxHeight := 0; - for I := 0 to Self.Columns.Count - 1 do - lMaxHeight := Max(lMaxHeight, Columns[I].Spacing); - // Calculate the required height based on the font, this is important as the user might just have increased the size of the system icon font. - with TBitmap.Create do - try - Canvas.Font.Assign(FFont); - lMaxHeight := lMaxHeight {top spacing} + (lMaxHeight div 2) {minimum bottom spacing} + Canvas.TextHeight('Q'); - finally - Free; - end; - // Get the maximum of the scaled original value and the minimum needed header height. - lMaxHeight := Max(lMaxHeight, FHeight); - // Set the calculated size - Self.SetHeight(lMaxHeight); + FNodeDataSize := Value; + if not (csLoading in ComponentState) and not (csDesigning in ComponentState) then + begin + LastRootCount := FRoot.ChildCount; + Clear; + SetRootNodeCount(LastRootCount); + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.GetMainColumn: TColumnIndex; +procedure TBaseVirtualTree.SetNodeHeight(Node: PVirtualNode; Value: Cardinal); + +var + Difference: Integer; begin - if FColumns.Count > 0 then - Result := FMainColumn - else - Result := NoColumn; -end; + Assert(Assigned(Node), 'SetNodeHeight() cannot be called with Node = nil'); + Assert((Node <> FRoot), 'SetNodeHeight() cannot be called for the root node FRoot'); + if (Node.NodeHeight <> Value) then + begin + Difference := Integer(Value) - Integer(Node.NodeHeight); + Node.NodeHeight := Value; -//---------------------------------------------------------------------------------------------------------------------- + // If the node is effectively filtered out, nothing else has to be done, as it is not visible anyway. + if not IsEffectivelyFiltered[Node] then + begin + AdjustTotalHeight(Node, Difference, True); -function TVTHeader.GetUseColumns: Boolean; + // If an edit operation is currently active then update the editors boundaries as well. + UpdateEditBounds; -begin - Result := FColumns.Count > 0; + InvalidateCache; + // Stay away from touching the node cache while it is being validated. + if not (tsValidating in FStates) and FullyVisible[Node] then + begin + if (FUpdateCount = 0) and ([tsPainting, tsSizing] * FStates = []) then + begin + ValidateCache; + InvalidateToBottom(Node); + UpdateScrollBars(True); + end; + end; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.IsFontStored: Boolean; +procedure TBaseVirtualTree.SetNodeParent(Node: PVirtualNode; const Value: PVirtualNode); begin - Result := not ParentFont; + if Assigned(Node) and Assigned(Value) and (Node.Parent <> Value) then + MoveTo(Node, Value, amAddChildLast, False); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetAutoSizeIndex(Value: TColumnIndex); +procedure TBaseVirtualTree.SetOffsetX(const Value: Integer); begin - if FAutoSizeIndex <> Value then - begin - FAutoSizeIndex := Value; - if hoAutoResize in FOptions then - Columns.AdjustAutoSize(InvalidColumn); - end; + DoSetOffsetXY(Point(Value, FOffsetY), DefaultScrollUpdateFlags); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetBackground(Value: TColor); +procedure TBaseVirtualTree.SetOffsetXY(const Value: TPoint); begin - if FBackgroundColor <> Value then - begin - FBackgroundColor := Value; - Invalidate(nil); - end; + DoSetOffsetXY(Value, DefaultScrollUpdateFlags); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetColumns(Value: TVirtualTreeColumns); +procedure TBaseVirtualTree.SetOffsetY(const Value: Integer); + +begin + DoSetOffsetXY(Point(FOffsetX, Value), DefaultScrollUpdateFlags); +end; +procedure TBaseVirtualTree.SetOnPrepareButtonImages(const Value: TVTPrepareButtonImagesEvent); begin - FColumns.Assign(Value); + FOnPrepareButtonImages := Value; + PrepareBitmaps(True, False); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetDefaultHeight(Value: Integer); +procedure TBaseVirtualTree.SetOptions(const Value: TCustomVirtualTreeOptions); begin - if Value < FMinHeight then - Value := FMinHeight; - if Value > FMaxHeight then - Value := FMaxHeight; - - if FHeight = FDefaultHeight then - SetHeight(Value); - FDefaultHeight := Value; + FOptions.Assign(Value); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetFont(const Value: TFont); - +procedure TBaseVirtualTree.SetRangeX(value: Cardinal); begin - FFont.Assign(Value); - FParentFont := False; + FRangeX := value; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetHeight(Value: Integer); - -var - RelativeMaxHeight, - RelativeMinHeight, - EffectiveMaxHeight, - EffectiveMinHeight: Integer; +procedure TBaseVirtualTree.SetRootNodeCount(Value: Cardinal); begin - if not TreeView.HandleAllocated then + // Don't set the root node count until all other properties (in particular the OnInitNode event) have been set. + if csLoading in ComponentState then begin - FHeight := Value; - Include(FStates, hsNeedScaling); + FRoot.ChildCount := Value; + DoStateChange([tsNeedRootCountUpdate]); end else - begin - with FFixedAreaConstraints do + if FRoot.ChildCount <> Value then begin - RelativeMaxHeight := ((Treeview.ClientHeight + FHeight) * FMaxHeightPercent) div 100; - RelativeMinHeight := ((Treeview.ClientHeight + FHeight) * FMinHeightPercent) div 100; + BeginUpdate; + InterruptValidation; + SetChildCount(FRoot, Value); + EndUpdate; + end; +end; - EffectiveMinHeight := IfThen(FMaxHeightPercent > 0, Min(RelativeMaxHeight, FMinHeight), FMinHeight); - EffectiveMaxHeight := IfThen(FMinHeightPercent > 0, Max(RelativeMinHeight, FMaxHeight), FMaxHeight); +//---------------------------------------------------------------------------------------------------------------------- - Value := Min(Max(Value, EffectiveMinHeight), EffectiveMaxHeight); - if FMinHeightPercent > 0 then - Value := Max(RelativeMinHeight, Value); - if FMaxHeightPercent > 0 then - Value := Min(RelativeMaxHeight, Value); - end; +procedure TBaseVirtualTree.SetScrollBarOptions(Value: TScrollBarOptions); - if FHeight <> Value then - begin - FHeight := Value; - if not (csLoading in Treeview.ComponentState) and not (hsScaling in FStates) then - RecalculateHeader; - Treeview.Invalidate; - end; - end; +begin + FScrollBarOptions.Assign(Value); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetImages(const Value: TCustomImageList); +procedure TBaseVirtualTree.SetSearchOption(const Value: TVTIncrementalSearch); begin - if FImages <> Value then + if FIncrementalSearch <> Value then begin - if Assigned(FImages) then - begin - FImages.UnRegisterChanges(FImageChangeLink); - FImages.RemoveFreeNotification(FOwner); - end; - FImages := Value; - if Assigned(FImages) then + FIncrementalSearch := Value; + if FIncrementalSearch = isNone then begin - FImages.RegisterChanges(FImageChangeLink); - FImages.FreeNotification(FOwner); + StopTimer(SearchTimer); + FSearchBuffer := ''; + FLastSearchNode := nil; end; - if not (csLoading in Treeview.ComponentState) then - Invalidate(nil); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetMainColumn(Value: TColumnIndex); +procedure TBaseVirtualTree.SetSelected(Node: PVirtualNode; Value: Boolean); begin - if csLoading in Treeview.ComponentState then - FMainColumn := Value - else + if not FSelectionLocked and Assigned(Node) and (Node <> FRoot) and (Value xor (vsSelected in Node.States)) then begin - if Value < 0 then - Value := 0; - if Value > FColumns.Count - 1 then - Value := FColumns.Count - 1; - if Value <> FMainColumn then + if Value then begin - FMainColumn := Value; - if not (csLoading in Treeview.ComponentState) then - begin - Treeview.MainColumnChanged; - if not (toExtendedFocus in Treeview.TreeOptions.SelectionOptions) then - Treeview.FocusedColumn := FMainColumn; - Treeview.Invalidate; + if FSelectionCount = 0 then + FRangeAnchor := Node + else begin + if not (toMultiSelect in FOptions.SelectionOptions) then + ClearSelection; + if FRangeAnchor = nil then + FRangeAnchor := Node; end; + + AddToSelection(Node, True); + + if not (toMultiSelect in FOptions.SelectionOptions) then + FocusedNode := GetFirstSelected; // if only one node can be selected, make sure the focused node changes with the selected node + // Make sure there is a valid column selected (if there are columns at all). + if ((FFocusedColumn < 0) or not (coVisible in FHeader.Columns[FFocusedColumn].Options)) and + (FHeader.MainColumn > NoColumn) then + if ([coVisible, coAllowFocus] * FHeader.Columns[FHeader.MainColumn].Options = [coVisible, coAllowFocus]) then + FFocusedColumn := FHeader.MainColumn + else + FFocusedColumn := FHeader.Columns.GetFirstVisibleColumn(True); + end + else + begin + RemoveFromSelection(Node); + if FSelectionCount = 0 then + ResetRangeAnchor; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetMaxHeight(Value: Integer); +procedure TBaseVirtualTree.SetSelectionCurveRadius(const Value: Cardinal); begin - if Value < FMinHeight then - Value := FMinHeight; - FMaxHeight := Value; - SetHeight(FHeight); + if FSelectionCurveRadius <> Value then + begin + FSelectionCurveRadius := Value; + if HandleAllocated and not (csLoading in ComponentState) then + Invalidate; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetMinHeight(Value: Integer); +procedure TBaseVirtualTree.SetStateImages(const Value: TCustomImageList); begin - if Value < 0 then - Value := 0; - if Value > FMaxHeight then - Value := FMaxHeight; - FMinHeight := Value; - SetHeight(FHeight); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTHeader.SetOptions(Value: TVTHeaderOptions); - -var - ToBeSet, - ToBeCleared: TVTHeaderOptions; - -begin - ToBeSet := Value - FOptions; - ToBeCleared := FOptions - Value; - FOptions := Value; - - if (hoAutoResize in (ToBeSet + ToBeCleared)) and (FColumns.Count > 0) then + if FStateImages <> Value then begin - FColumns.AdjustAutoSize(InvalidColumn); - if Treeview.HandleAllocated then + if Assigned(FStateImages) then begin - Treeview.UpdateHorizontalScrollBar(False); - if hoAutoResize in ToBeSet then - Treeview.Invalidate; + FStateImages.UnRegisterChanges(FStateChangeLink); + FStateImages.RemoveFreeNotification(Self); end; - end; - - if not (csLoading in Treeview.ComponentState) and Treeview.HandleAllocated then - begin - if hoVisible in (ToBeSet + ToBeCleared) then - RecalculateHeader; - Invalidate(nil); - Treeview.Invalidate; + FStateImages := Value; + if Assigned(FStateImages) then + begin + FStateImages.RegisterChanges(FStateChangeLink); + FStateImages.FreeNotification(Self); + end; + if HandleAllocated and not (csLoading in ComponentState) then + Invalidate; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetParentFont(Value: Boolean); +procedure TBaseVirtualTree.SetTextMargin(Value: Integer); begin - if FParentFont <> Value then + if FTextMargin <> Value then begin - FParentFont := Value; - if FParentFont then - FFont.Assign(FOwner.Font); + FTextMargin := Value; + if not (csLoading in ComponentState) then + Invalidate; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetSortColumn(Value: TColumnIndex); - -begin - if csLoading in Treeview.ComponentState then - FSortColumn := Value - else - DoSetSortColumn(Value, FSortDirection); -end; - -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.SetTopNode(Node: PVirtualNode); -procedure TVTHeader.SetSortDirection(const Value: TSortDirection); +var + R: TRect; + Run: PVirtualNode; begin - if Value <> FSortDirection then + if Assigned(Node) then begin - FSortDirection := Value; - Invalidate(nil); - if ((toAutoSort in Treeview.TreeOptions.AutoOptions) or (hoHeaderClickAutoSort in Options)) and (Treeview.UpdateCount = 0) then - Treeview.SortTree(FSortColumn, FSortDirection, True); + // make sure all parents of the node are expanded + Run := Node.Parent; + while Run <> FRoot do + begin + if not (vsExpanded in Run.States) then + ToggleNode(Run); + Run := Run.Parent; + end; + R := GetDisplayRect(Node, FHeader.MainColumn, True); + SetOffsetY(FOffsetY - R.Top); end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.CanSplitterResize(P: TPoint): Boolean; +procedure TBaseVirtualTree.SetUpdateState(Updating: Boolean); begin - Result := hoHeightResize in FOptions; - DoCanSplitterResize(P, Result); + // The check for visibility is necessary otherwise the tree is automatically shown when + // updating is allowed. As this happens internally the VCL does not get notified and + // still assumes the control is hidden. This results in weird "cannot focus invisible control" errors. + if Visible and HandleAllocated and (FUpdateCount = 0) then + SendMessage(Handle, WM_SETREDRAW, Ord(not Updating), 0); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SetStyle(Value: TVTHeaderStyle); +procedure TBaseVirtualTree.SetVerticalAlignment(Node: PVirtualNode; Value: Byte); begin - if FStyle <> Value then + if Value > 100 then + Value := 100; + if Node.Align <> Value then begin - FStyle := Value; - if not (csLoading in Treeview.ComponentState) then - Invalidate(nil); + Node.Align := Value; + if FullyVisible[Node] and not IsEffectivelyFiltered[Node] then + InvalidateNode(Node); end; end; -procedure TVTHeader.StyleChanged(); -begin - AutoScale(); // Elements may have chnaged in size -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVTHeader.CanWriteColumns: Boolean; - -// descendants may override this to optionally prevent column writing (e.g. if they are build dynamically). - -begin - Result := True; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTHeader.ChangeScale(M, D: Integer; isDpiChange: Boolean); -var - I: Integer; -begin - // This method is only executed if toAutoChangeScale is set - FMinHeight := MulDiv(FMinHeight, M, D); - FMaxHeight := MulDiv(FMaxHeight, M, D); - Self.Height := MulDiv(FHeight, M, D); - if not ParentFont then - Font.Height := MulDiv(Font.Height, M, D); - // Scale the columns widths too - for I := 0 to FColumns.Count - 1 do - Self.FColumns[I].ChangeScale(M, D, isDpiChange); - if not isDpiChange then - AutoScale(); -end; - //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.DetermineSplitterIndex(P: TPoint): Boolean; +procedure TBaseVirtualTree.SetVisible(Node: PVirtualNode; Value: Boolean); -// Tries to find the index of that column whose right border corresponds to P. -// Result is True if column border was hit (with -3..+5 pixels tolerance). -// For continuous resizing the current track index and the column's left/right border are set. -// Note: The hit test is checking from right to left (or left to right in RTL mode) to make enlarging of zero-sized -// columns possible. +// Sets the visibility style of the given node according to Value. var - VisibleFixedWidth: Integer; - SplitPoint: Integer; - - //--------------- local function -------------------------------------------- - - function IsNearBy(IsFixedCol: Boolean; LeftTolerance, RightTolerance: Integer): Boolean; - - begin - if IsFixedCol then - Result := (P.X < SplitPoint + Treeview.FEffectiveOffsetX + RightTolerance) and (P.X > SplitPoint + Treeview.FEffectiveOffsetX - LeftTolerance) - else - Result := (P.X > VisibleFixedWidth) and (P.X < SplitPoint + RightTolerance) and (P.X > SplitPoint - LeftTolerance); - end; - - //--------------- end local function ---------------------------------------- + NeedUpdate: Boolean; -var - I: Integer; - LeftTolerance: Integer; // The area left of the column divider which allows column resizing begin - Result := False; + Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); - if FColumns.Count > 0 then + if Value <> (vsVisible in Node.States) then begin - FColumns.TrackIndex := NoColumn; - VisibleFixedWidth := FColumns.GetVisibleFixedWidth; - LeftTolerance := Round(SplitterHitTolerance * 0.6); - if Treeview.UseRightToLeftAlignment then + InterruptValidation; + NeedUpdate := False; + if Value then begin - SplitPoint := -Treeview.FEffectiveOffsetX; - if FColumns.TotalWidth < Treeview.ClientWidth then - Inc(SplitPoint, Treeview.ClientWidth - FColumns.TotalWidth); - - for I := 0 to FColumns.Count - 1 do - with FColumns, Items[FPositionToIndex[I]] do - if coVisible in FOptions then - begin - if IsNearBy(coFixed in FOptions, LeftTolerance, SplitterHitTolerance - LeftTolerance) then - begin - if CanSplitterResize(P, FPositionToIndex[I]) then - begin - Result := True; - FTrackIndex := FPositionToIndex[I]; + Include(Node.States, vsVisible); + if vsExpanded in Node.Parent.States then + AdjustTotalHeight(Node.Parent, Node.TotalHeight, True); + if VisiblePath[Node] then + begin + Inc(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); + NeedUpdate := True; + end; - // Keep the right border of this column. This and the current mouse position - // directly determine the current column width. - FTrackPoint.X := SplitPoint + IfThen(coFixed in FOptions, Treeview.FEffectiveOffsetX) + FWidth; - FTrackPoint.Y := P.Y; - Break; - end; - end; - Inc(SplitPoint, FWidth); - end; + // Update the hidden children flag of the parent. + // Since this node is now visible we simply have to remove the flag. + if not IsEffectivelyFiltered[Node] then + Exclude(Node.Parent.States, vsAllChildrenHidden); end else begin - SplitPoint := -Treeview.FEffectiveOffsetX + FColumns.TotalWidth; + if vsExpanded in Node.Parent.States then + AdjustTotalHeight(Node.Parent, -Integer(Node.TotalHeight), True); + if VisiblePath[Node] then + begin + Dec(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); + NeedUpdate := True; + end; + Exclude(Node.States, vsVisible); - for I := FColumns.Count - 1 downto 0 do - with FColumns, Items[FPositionToIndex[I]] do - if coVisible in FOptions then - begin - if IsNearBy(coFixed in FOptions, SplitterHitTolerance - LeftTolerance, LeftTolerance) then - begin - if CanSplitterResize(P, FPositionToIndex[I]) then - begin - Result := True; - FTrackIndex := FPositionToIndex[I]; + if FUpdateCount = 0 then + DetermineHiddenChildrenFlag(Node.Parent) + else + Include(FStates, tsUpdateHiddenChildrenNeeded); + end; - // Keep the left border of this column. This and the current mouse position - // directly determine the current column width. - FTrackPoint.X := SplitPoint + IfThen(coFixed in FOptions, Treeview.FEffectiveOffsetX) - FWidth; - FTrackPoint.Y := P.Y; - Break; - end; - end; - Dec(SplitPoint, FWidth); - end; + InvalidateCache; + if NeedUpdate and (FUpdateCount = 0) then + begin + ValidateCache; + UpdateScrollBars(True); + Invalidate; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.DoAfterAutoFitColumn(Column: TColumnIndex); +procedure TBaseVirtualTree.SetVisiblePath(Node: PVirtualNode; Value: Boolean); + +// If Value is True then all parent nodes of Node are expanded. begin - if Assigned(TreeView.FOnAfterAutoFitColumn) then - TreeView.FOnAfterAutoFitColumn(Self, Column); -end; + Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); -//---------------------------------------------------------------------------------------------------------------------- + if Value then + begin + repeat + Node := Node.Parent; + if Node = FRoot then + Break; + if not (vsExpanded in Node.States) then + ToggleNode(Node); + until False; + end; +end; -procedure TVTHeader.DoAfterColumnWidthTracking(Column: TColumnIndex); +// ---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.PrepareBackGroundPicture(Source: TPicture; + DrawBitmap: TBitmap; DrawBitmapWidth: Integer; DrawBitMapHeight: Integer; ABkgcolor: TColor); +const + DST = $00AA0029; // Ternary Raster Operation - Destination unchanged -// Tell the application that a column width tracking operation has been finished. + // fill background will work for transparent images and + // will not disturb non-transparent ones + procedure FillDrawBitmapWithBackGroundColor; + begin + DrawBitmap.Canvas.Brush.Color := ABkgcolor; + DrawBitmap.Canvas.FillRect(Rect(0, 0, DrawBitmap.Width, DrawBitmap.Height)); + end; begin - if Assigned(TreeView.FOnAfterColumnWidthTracking) then - TreeView.FOnAfterColumnWidthTracking(Self, Column); + DrawBitmap.SetSize(DrawBitmapWidth, DrawBitMapHeight); + + if (Source.Graphic is TBitmap) and + (FBackGroundImageTransparent or Source.Bitmap.TRANSPARENT) + then + begin + FillDrawBitmapWithBackGroundColor; + MaskBlt(DrawBitmap.Canvas.Handle, 0, 0, Source.Width, Source.Height, + Source.Bitmap.Canvas.Handle, 0, 0, Source.Bitmap.MaskHandle, 0, 0, + MakeROP4(DST, SRCCOPY)); + end + else + begin + // Similar to TImage's Transparent property behavior, we don't want + // to draw transparent if the following flag is OFF. + if FBackGroundImageTransparent then + FillDrawBitmapWithBackGroundColor; + DrawBitmap.Canvas.Draw(0, 0, Source.Graphic); + end end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.DoAfterHeightTracking; +procedure TBaseVirtualTree.StaticBackground(Source: TPicture; Target: TCanvas; OffsetPosition: TPoint; R: TRect; aBkgColor: TColor); -// Tell the application that a height tracking operation has been finished. +// Draws the given source graphic so that it stays static in the given rectangle which is relative to the target bitmap. +// The graphic is aligned so that it always starts at the upper left corner of the target canvas. +// Offset gives the position of the target window as a possible superordinated surface. -begin - if Assigned(TreeView.FOnAfterHeaderHeightTracking) then - TreeView.FOnAfterHeaderHeightTracking(Self); -end; +const + DST = $00AA0029; // Ternary Raster Operation - Destination unchanged -//---------------------------------------------------------------------------------------------------------------------- +var + PicRect: TRect; + AreaRect: TRect; + DrawRect: TRect; + DrawBitmap: TBitmap; +begin + DrawBitmap := TBitmap.Create; + try + // clear background + Target.Brush.Color := aBkgColor; + Target.FillRect(R); -function TVTHeader.DoBeforeAutoFitColumn(Column: TColumnIndex; SmartAutoFitType: TSmartAutoFitType): Boolean; + // Picture rect in relation to client viewscreen. + PicRect := Rect(FBackgroundOffsetX, FBackgroundOffsetY, FBackgroundOffsetX + Source.Width, FBackgroundOffsetY + Source.Height); -// Query the application if we may autofit a column. + // Area to be draw in relation to client viewscreen. + AreaRect := Rect(OffsetPosition.X + R.Left, OffsetPosition.Y + R.Top, OffsetPosition.X + R.Right, OffsetPosition.Y + R.Bottom); -begin - Result := True; - if Assigned(TreeView.FOnBeforeAutoFitColumn) then - TreeView.FOnBeforeAutoFitColumn(Self, Column, SmartAutoFitType, Result); + // If picture falls in AreaRect, return intersection (DrawRect). + if IntersectRect(DrawRect, PicRect, AreaRect) then + begin + PrepareBackGroundPicture(Source, DrawBitmap, Source.Width, Source.Height, aBkgColor); + // copy image to destination + BitBlt(Target.Handle, DrawRect.Left - OffsetPosition.X, DrawRect.Top - OffsetPosition.Y, (DrawRect.Right - OffsetPosition.X) - (DrawRect.Left - OffsetPosition.X), + (DrawRect.Bottom - OffsetPosition.Y) - (DrawRect.Top - OffsetPosition.Y) + R.Top, DrawBitmap.Canvas.Handle, DrawRect.Left - PicRect.Left, DrawRect.Top - PicRect.Top, + SRCCOPY); + end; + finally + DrawBitmap.Free; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.DoBeforeColumnWidthTracking(Column: TColumnIndex; Shift: TShiftState); - -// Tell the a application that a column width tracking operation may begin. +procedure TBaseVirtualTree.StopTimer(ID: Integer); begin - if Assigned(TreeView.FOnBeforeColumnWidthTracking) then - TreeView.FOnBeforeColumnWidthTracking(Self, Column, Shift); + if HandleAllocated then + KillTimer(Handle, ID); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.DoBeforeHeightTracking(Shift: TShiftState); - -// Tell the application that a height tracking operation may begin. +procedure TBaseVirtualTree.SetWindowTheme(const Theme: string); begin - if Assigned(TreeView.FOnBeforeHeaderHeightTracking) then - TreeView.FOnBeforeHeaderHeightTracking(Self, Shift); + FChangingTheme := True; + Winapi.UxTheme.SetWindowTheme(Handle, PWideChar(Theme), nil); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.DoCanSplitterResize(P: TPoint; var Allowed: Boolean); +//used by TCustomVirtualTreeOptions +procedure TBaseVirtualTree.SetVisibleCount(value : Cardinal); begin - if Assigned(TreeView.FOnCanSplitterResizeHeader) then - TreeView.FOnCanSplitterResizeHeader(Self, P, Allowed); + FVisibleCount := value; end; -//---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.DoColumnWidthDblClickResize(Column: TColumnIndex; P: TPoint; Shift: TShiftState): Boolean; +procedure TBaseVirtualTree.TileBackground(Source: TPicture; Target: TCanvas; Offset: TPoint; R: TRect; aBkgColor: TColor); -// Queries the application whether a double click on the column splitter should resize the column. +// Draws the given source graphic so that it tiles into the given rectangle which is relative to the target bitmap. +// The graphic is aligned so that it always starts at the upper left corner of the target canvas. +// Offset gives the position of the target window in an possible superordinated surface. +var + SourceX, + SourceY, + TargetX, + DeltaY: Integer; + DrawBitmap: TBitmap; begin - Result := True; - if Assigned(TreeView.FOnColumnWidthDblClickResize) then - TreeView.FOnColumnWidthDblClickResize(Self, Column, Shift, P, Result); -end; + DrawBitmap := TBitmap.Create; + try + PrepareBackGroundPicture(Source, DrawBitmap, Source.Width, Source.Height, aBkgColor); + with Target do + begin + SourceY := (R.Top + Offset.Y + FBackgroundOffsetY) mod Source.Height; + // Always wrap the source coordinates into positive range. + if SourceY < 0 then + SourceY := Source.Height + SourceY; -//---------------------------------------------------------------------------------------------------------------------- + // Tile image vertically until target rect is filled. + while R.Top < R.Bottom do + begin + SourceX := (R.Left + Offset.X + FBackgroundOffsetX) mod Source.Width; + // always wrap the source coordinates into positive range + if SourceX < 0 then + SourceX := Source.Width + SourceX; -function TVTHeader.DoColumnWidthTracking(Column: TColumnIndex; Shift: TShiftState; var TrackPoint: TPoint; P: TPoint): Boolean; + TargetX := R.Left; + // height of strip to draw + DeltaY := Min(R.Bottom - R.Top, Source.Height - SourceY); -begin - Result := True; - if Assigned(TreeView.FOnColumnWidthTracking) then - TreeView.FOnColumnWidthTracking(Self, Column, Shift, TrackPoint, P, Result); + // tile the image horizontally + while TargetX < R.Right do + begin + BitBlt(Handle, TargetX, R.Top, Min(R.Right - TargetX, Source.Width - SourceX), DeltaY, + DrawBitmap.Canvas.Handle, SourceX, SourceY, SRCCOPY); + Inc(TargetX, Source.Width - SourceX); + SourceX := 0; + end; + Inc(R.Top, Source.Height - SourceY); + SourceY := 0; + end; + end; + finally + DrawBitmap.Free; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.DoGetPopupMenu(Column: TColumnIndex; Position: TPoint): TPopupMenu; - -// Queries the application whether there is a column specific header popup menu. +function TBaseVirtualTree.ToggleCallback(Step, StepSize: Integer; Data: Pointer): Boolean; var - AskParent: Boolean; - -begin - Result := PopupMenu; - if Assigned(TreeView.FOnGetPopupMenu) then - TreeView.FOnGetPopupMenu(TreeView, nil, Column, Position, AskParent, Result); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVTHeader.DoHeightTracking(var P: TPoint; Shift: TShiftState): Boolean; + Column: TColumnIndex; + Run: TRect; + SecondaryStepSize: Integer; -begin - Result := True; - if Assigned(TreeView.FOnHeaderHeightTracking) then - TreeView.FOnHeaderHeightTracking(Self, P, Shift, Result); -end; + //--------------- local functions ------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- + procedure EraseLine; -function TVTHeader.DoHeightDblClickResize(var P: TPoint; Shift: TShiftState): Boolean; + var + LocalBrush: HBRUSH; -begin - Result := True; - if Assigned(TreeView.FOnHeaderHeightDblClickResize) then - TreeView.FOnHeaderHeightDblClickResize(Self, P, Shift, Result); -end; + begin + with TToggleAnimationData(Data^), FHeader.Columns do + begin + // Iterate through all columns and erase background in their local color. + // LocalBrush is a brush in the color of the particular column. + Column := GetFirstVisibleColumn; + while (Column > InvalidColumn) and (Run.Left < ClientWidth) do + begin + GetColumnBounds(Column, Run.Left, Run.Right); + if coParentColor in Items[Column].Options then + FillRect(DC, Run, Brush) + else + begin + if VclStyleEnabled then + LocalBrush := CreateSolidBrush(ColorToRGB(FColors.BackGroundColor)) + else + LocalBrush := CreateSolidBrush(ColorToRGB(Items[Column].Color)); + FillRect(DC, Run, LocalBrush); + DeleteObject(LocalBrush); + end; + Column := GetNextVisibleColumn(Column); + end; + end; + end; -//---------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------- -procedure TVTHeader.DoSetSortColumn(Value: TColumnIndex; pSortDirection: TSortDirection); + procedure DoScrollUp(DC: HDC; Brush: HBRUSH; Area: TRect; Steps: Integer); -begin - if Value < NoColumn then - Value := NoColumn; - if Value > Columns.Count - 1 then - Value := Columns.Count - 1; - if FSortColumn <> Value then begin - if FSortColumn > NoColumn then - Invalidate(Columns[FSortColumn]); - FSortColumn := Value; - FSortDirection := pSortDirection; - if FSortColumn > NoColumn then - Invalidate(Columns[FSortColumn]); - if ((toAutoSort in Treeview.TreeOptions.AutoOptions) or (hoHeaderClickAutoSort in Options)) and (Treeview.UpdateCount = 0) then - Treeview.SortTree(FSortColumn, FSortDirection, True); + ScrollDC(DC, 0, -Steps, Area, Area, 0, nil); + + if Step = 0 then + if not FHeader.UseColumns then + FillRect(DC, Rect(Area.Left, Area.Bottom - Steps - 1, Area.Right, Area.Bottom), Brush) + else + begin + Run := Rect(Area.Left, Area.Bottom - Steps - 1, Area.Right, Area.Bottom); + EraseLine; + end; end; -end; -//---------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------- -procedure TVTHeader.DragTo(P: TPoint); + procedure DoScrollDown(DC: HDC; Brush: HBRUSH; Area: TRect; Steps: Integer); -// Moves the drag image to a new position, which is determined from the passed point P and the previous -// mouse position. + begin + ScrollDC(DC, 0, Steps, Area, Area, 0, nil); -var - I, - NewTarget: Integer; - // optimized drag image move support - ClientP: TPoint; - Left, - Right: Integer; - NeedRepaint: Boolean; // True if the screen needs an update (changed drop target or drop side) + if Step = 0 then + if not FHeader.UseColumns then + FillRect(DC, Rect(Area.Left, Area.Top, Area.Right, Area.Top + Steps + 1), Brush) + else + begin + Run := Rect(Area.Left, Area.Top, Area.Right, Area.Top + Steps + 1); + EraseLine; + end; + end; + + //--------------- end local functions --------------------------------------- begin - // Determine new drop target and which side of it is prefered. - ClientP := Treeview.ScreenToClient(P); - // Make coordinates relative to (0, 0) of the non-client area. - Inc(ClientP.Y, FHeight); - NewTarget := FColumns.ColumnFromPosition(ClientP); - NeedRepaint := (NewTarget <> InvalidColumn) and (NewTarget <> FColumns.DropTarget); - if NewTarget >= 0 then + Result := True; + if StepSize > 0 then begin - FColumns.GetColumnBounds(NewTarget, Left, Right); - if (ClientP.X < ((Left + Right) div 2)) <> FColumns.DropBefore then + SecondaryStepSize := 0; + with TToggleAnimationData(Data^) do begin - NeedRepaint := True; - FColumns.DropBefore := not FColumns.DropBefore; - end; - end; + if Mode1 <> tamNoScroll then + begin + if Mode1 = tamScrollUp then + DoScrollUp(DC, Brush, R1, StepSize) + else + DoScrollDown(DC, Brush, R1, StepSize); - if NeedRepaint then - begin - // Invalidate columns which need a repaint. - if FColumns.DropTarget > NoColumn then - begin - I := FColumns.DropTarget; - FColumns.DropTarget := NoColumn; - Invalidate(FColumns.Items[I]); - end; - if (NewTarget > NoColumn) and (NewTarget <> FColumns.DropTarget) then - begin - Invalidate(FColumns.Items[NewTarget]); - FColumns.DropTarget := NewTarget; - end; - end; + if (Mode2 <> tamNoScroll) and (ScaleFactor > 0) then + begin + // As this routine is able to scroll two independent areas at once, the missing StepSize is + // computed in that case. To ensure the maximal accuracy the rounding error is accumulated. + SecondaryStepSize := Round((StepSize + MissedSteps) * ScaleFactor); + MissedSteps := MissedSteps + StepSize * ScaleFactor - SecondaryStepSize; + end; + end + else + SecondaryStepSize := StepSize; - // Fix for various problems mentioned in issue 248. - if NeedRepaint then - begin - UpdateWindow(FOwner.Handle); - // The new routine recaptures the backup image after the updatewindow - // Note: We could have called this unconditionally but when called - // over the tree, doesn't capture the background image. Since our - // problems are in painting of the header, we call it only when the - // drag image is over the header. - if - // determine the case when the drag image is or was on the header area - (InHeader(FOwner.ScreenToClient(FDragImage.LastPosition)) - or InHeader(FOwner.ScreenToClient(FDragImage.ImagePosition)) - ) then - begin - GDIFlush; - FOwner.UpdateWindowAndDragImage(FOwner, FOwner.HeaderRect, True, true); + if Mode2 <> tamNoScroll then + if Mode2 = tamScrollUp then + DoScrollUp(DC, Brush, R2, SecondaryStepSize) + else + DoScrollDown(DC, Brush, R2, SecondaryStepSize); end; - // since we took care of UpdateWindow above, there is no need to do an - // update window again by sending NeedRepaint. So switch off the second parameter. - NeedRepaint := false; end; - - FDragImage.DragTo(P, NeedRepaint); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.FixedAreaConstraintsChanged(Sender: TObject); - -// This method gets called when FFixedAreaConstraints is changed. +procedure TBaseVirtualTree.CMColorChange(var Message: TMessage); begin - if Treeview.HandleAllocated then - RescaleHeader - else - Include(FStates, hsNeedScaling); + if not (csLoading in ComponentState) then + begin + PrepareBitmaps(True, False); + if HandleAllocated then + Invalidate; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.GetColumnsClass: TVirtualTreeColumnsClass; - -// Returns the class to be used for the actual column implementation. descendants may optionally override this and -// return their own class. +procedure TBaseVirtualTree.CMCtl3DChanged(var Message: TMessage); begin - Result := TVirtualTreeColumns; + inherited; + if FBorderStyle = bsSingle then + RecreateWnd; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.GetOwner: TPersistent; +procedure TBaseVirtualTree.CMBiDiModeChanged(var Message: TMessage); begin - Result := FOwner; -end; + inherited; -function TVTHeader.GetRestoreSelectionColumnIndex: Integer; -begin - if fRestoreSelectionColumnIndex >= 0 then - Result := fRestoreSelectionColumnIndex + if UseRightToLeftAlignment then + FEffectiveOffsetX := Integer(FRangeX) - ClientWidth + FOffsetX else - Result := MainColumn; + FEffectiveOffsetX := -FOffsetX; + if FEffectiveOffsetX < 0 then + FEffectiveOffsetX := 0; + + if toAutoBidiColumnOrdering in FOptions.AutoOptions then + TVirtualTreeColumnsCracker(FHeader.Columns).ReorderColumns(UseRightToLeftAlignment); + FHeader.Invalidate(nil); end; -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.CMBorderChanged(var Message: TMessage); +begin + inherited; + if VclStyleEnabled and (seBorder in StyleElements) then + RecreateWnd; +end; -function TVTHeader.GetShiftState: TShiftState; +procedure TBaseVirtualTree.CMParentDoubleBufferedChange(var Message: TMessage); +begin + // empty by intention, we do our own buffering +end; +procedure TBaseVirtualTree.CMStyleChanged(var Message: TMessage); begin - Result := []; - if GetKeyState(VK_SHIFT) < 0 then - Include(Result, ssShift); - if GetKeyState(VK_CONTROL) < 0 then - Include(Result, ssCtrl); - if GetKeyState(VK_MENU) < 0 then - Include(Result, ssAlt); + VclStyleChanged; + RecreateWnd; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.HandleHeaderMouseMove(var Message: TWMMouseMove): Boolean; +procedure TBaseVirtualTree.CMDenySubclassing(var Message: TMessage); -var - P: TPoint; - NextColumn, - I: TColumnIndex; - NewWidth: Integer; +// If a Windows XP Theme Manager component is used in the application it will try to subclass all controls which do not +// explicitly deny this. Virtual Treeview knows how to handle XP themes so it does not need subclassing. begin - Result := False; - with Message do - begin - P := Point(XPos, YPos); - if hsColumnWidthTrackPending in FStates then - begin - Treeview.StopTimer(HeaderTimer); - FStates := FStates - [hsColumnWidthTrackPending] + [hsColumnWidthTracking]; - HandleHeaderMouseMove := True; - Result := 0; - end - else - if hsHeightTrackPending in FStates then - begin - Treeview.StopTimer(HeaderTimer); - FStates := FStates - [hsHeightTrackPending] + [hsHeightTracking]; - HandleHeaderMouseMove := True; - Result := 0; - end - else - if hsColumnWidthTracking in FStates then - begin - if DoColumnWidthTracking(FColumns.TrackIndex, GetShiftState, FTrackPoint, P) then - begin - if Treeview.UseRightToLeftAlignment then - begin - NewWidth := FTrackPoint.X - XPos; - NextColumn := FColumns.GetPreviousVisibleColumn(FColumns.TrackIndex); - end - else - begin - NewWidth := XPos - FTrackPoint.X; - NextColumn := FColumns.GetNextVisibleColumn(FColumns.TrackIndex); - end; - - // The autosized column cannot be resized using the mouse normally. Instead we resize the next - // visible column, so it look as we directly resize the autosized column. - if (hoAutoResize in FOptions) and (FColumns.TrackIndex = FAutoSizeIndex) and - (NextColumn > NoColumn) and (coResizable in FColumns[NextColumn].Options) and - (FColumns[FColumns.TrackIndex].MinWidth < NewWidth) and - (FColumns[FColumns.TrackIndex].MaxWidth > NewWidth) then - FColumns[NextColumn].Width := FColumns[NextColumn].Width - NewWidth - + FColumns[FColumns.TrackIndex].Width - else - FColumns[FColumns.TrackIndex].Width := NewWidth; // 1 EListError seen here (List index out of bounds (-1)) since 10/2013 - end; - HandleHeaderMouseMove := True; - Result := 0; - end - else - if hsHeightTracking in FStates then - begin - if DoHeightTracking(P, GetShiftState) then - SetHeight(Integer(FHeight) + P.Y); - HandleHeaderMouseMove := True; - Result := 0; - end - else - begin - if hsDragPending in FStates then - begin - P := Treeview.ClientToScreen(P); - // start actual dragging if allowed - if (hoDrag in FOptions) and Treeview.DoHeaderDragging(FColumns.DownIndex) then - begin - if ((Abs(FDragStart.X - P.X) > Mouse.DragThreshold) or - (Abs(FDragStart.Y - P.Y) > Mouse.DragThreshold)) then - begin - Treeview.StopTimer(HeaderTimer); - I := FColumns.DownIndex; - FColumns.DownIndex := NoColumn; - FColumns.HoverIndex := NoColumn; - if I > NoColumn then - Invalidate(FColumns[I]); - PrepareDrag(P, FDragStart); - FStates := FStates - [hsDragPending] + [hsDragging]; - HandleHeaderMouseMove := True; - Result := 0; - end; - end; - end - else - if hsDragging in FStates then - begin - DragTo(Treeview.ClientToScreen(Point(XPos, YPos))); - HandleHeaderMouseMove := True; - Result := 0; - end; - end; - end; + Message.Result := 1; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.HandleMessage(var Message: TMessage): Boolean; - -// The header gets here the opportunity to handle certain messages before they reach the tree. This is important -// because the tree needs to handle various non-client area messages for the header as well as some dragging/tracking -// events. -// By returning True the message will not be handled further, otherwise the message is then dispatched -// to the proper message handlers. +procedure TBaseVirtualTree.CMDrag(var Message: TCMDrag); var + S: TObject; + ShiftState: Integer; P: TPoint; - R: TRect; - I: TColumnIndex; - OldPosition: Integer; - HitIndex: TColumnIndex; - NewCursor: HCURSOR; - Button: TMouseButton; - IsInHeader, - IsHSplitterHit, - IsVSplitterHit: Boolean; - - //--------------- local function -------------------------------------------- - - function HSplitterHit: Boolean; - - var - NextCol: TColumnIndex; + Formats: TFormatArray; + Effect: Integer; +begin + with Message, DragRec^ do begin - Result := (hoColumnResize in FOptions) and DetermineSplitterIndex(P); - if Result and not InHeader(P) then - begin - NextCol := FColumns.GetNextVisibleColumn(FColumns.TrackIndex); - if not (coFixed in FColumns[FColumns.TrackIndex].Options) or (NextCol <= NoColumn) or - (coFixed in FColumns[NextCol].Options) or (P.Y > Integer(Treeview.FRangeY)) then - Result := False; - end; - end; - - //--------------- end local function ---------------------------------------- - -begin - Result := False; - case Message.Msg of - WM_SIZE: - begin - if not (tsWindowCreating in FOwner.TreeStates) then - if (hoAutoResize in FOptions) and not (hsAutoSizing in FStates) then - begin - FColumns.AdjustAutoSize(InvalidColumn); - Invalidate(nil); - end - else - if not (hsScaling in FStates) then - begin - RescaleHeader; - Invalidate(nil); - end; - end; - CM_PARENTFONTCHANGED: - if FParentFont then - FFont.Assign(FOwner.Font); - CM_BIDIMODECHANGED: - for I := 0 to FColumns.Count - 1 do - if coParentBiDiMode in FColumns[I].Options then - FColumns[I].ParentBiDiModeChanged; - WM_NCMBUTTONDOWN: - begin - with TWMNCMButtonDown(Message) do - P := Treeview.ScreenToClient(Point(XCursor, YCursor)); - if InHeader(P) then - FOwner.DoHeaderMouseDown(mbMiddle, GetShiftState, P.X, P.Y + Integer(FHeight)); - end; - WM_NCMBUTTONUP: - begin - with TWMNCMButtonUp(Message) do - P := FOwner.ScreenToClient(Point(XCursor, YCursor)); - if InHeader(P) then - begin - FColumns.HandleClick(P, mbMiddle, True, False); - FOwner.DoHeaderMouseUp(mbMiddle, GetShiftState, P.X, P.Y + Integer(FHeight)); - FColumns.DownIndex := NoColumn; - FColumns.CheckBoxHit := False; - end; - end; - WM_LBUTTONDBLCLK, - WM_NCLBUTTONDBLCLK, - WM_NCMBUTTONDBLCLK, - WM_NCRBUTTONDBLCLK: - begin - if Message.Msg <> WM_LBUTTONDBLCLK then - with TWMNCLButtonDblClk(Message) do - P := FOwner.ScreenToClient(Point(XCursor, YCursor)) - else - with TWMLButtonDblClk(Message) do - P := Point(XPos, YPos); - - if (hoHeightDblClickResize in FOptions) and InHeaderSplitterArea(P) and (FDefaultHeight > 0) then - begin - if DoHeightDblClickResize(P, GetShiftState) and (FDefaultHeight > 0) then - SetHeight(FMinHeight); - Result := True; - end - else - if HSplitterHit and ((Message.Msg = WM_NCLBUTTONDBLCLK) or (Message.Msg = WM_LBUTTONDBLCLK)) and - (hoDblClickResize in FOptions) and (FColumns.TrackIndex > NoColumn) then - begin - // If the click was on a splitter then resize column to smallest width. - if DoColumnWidthDblClickResize(FColumns.TrackIndex, P, GetShiftState) then - AutoFitColumns(True, smaUseColumnOption, FColumns[FColumns.TrackIndex].Position, - FColumns[FColumns.TrackIndex].Position); - Message.Result := 0; - Result := True; - end - else - if InHeader(P) and (Message.Msg <> WM_LBUTTONDBLCLK) then - begin - case Message.Msg of - WM_NCMBUTTONDBLCLK: - Button := mbMiddle; - WM_NCRBUTTONDBLCLK: - Button := mbRight; - else - // WM_NCLBUTTONDBLCLK - Button := mbLeft; - end; - if Button = mbLeft then - Columns.AdjustDownColumn(P); - FColumns.HandleClick(P, Button, True, True); - end; - end; - // The "hot" area of the headers horizontal splitter is partly within the client area of the the tree, so we need - // to handle WM_LBUTTONDOWN here, too. - WM_LBUTTONDOWN, - WM_NCLBUTTONDOWN: - begin - - Application.CancelHint; - - if not (csDesigning in Treeview.ComponentState) then - begin - // make sure no auto scrolling is active... - Treeview.StopTimer(ScrollTimer); - Treeview.DoStateChange([], [tsScrollPending, tsScrolling]); - // ... pending editing is cancelled (actual editing remains active) - Treeview.StopTimer(EditTimer); - Treeview.DoStateChange([], [tsEditPending]); - end; + S := Source; + Formats := nil; - if Message.Msg = WM_LBUTTONDOWN then - // Coordinates are already client area based. - with TWMLButtonDown(Message) do - begin - P := Point(XPos, YPos); - // #909 - FDragStart := Treeview.ClientToScreen(p); - end - else - with TWMNCLButtonDown(Message) do + // Let the ancestor handle dock operations. + if S is TDragDockObject then + inherited + else + begin + // We need an extra check for the control drag object as there might be other objects not derived from + // this class (e.g. TActionDragObject). + if not (tsUserDragObject in FStates) and (S is TBaseDragControlObject) then + S := (S as TBaseDragControlObject).Control; + case DragMessage of + dmDragEnter, dmDragLeave, dmDragMove: begin - // want the drag start point in screen coordinates - FDragStart := Point(XCursor, YCursor); - P := Treeview.ScreenToClient(FDragStart); - end; + if DragMessage = dmDragEnter then + DoStateChange([tsVCLDragging]); + if DragMessage = dmDragLeave then + DoStateChange([tsVCLDragFinished], [tsVCLDragging]); - IsInHeader := InHeader(P); - // in design-time header columns are always resizable - if (csDesigning in Treeview.ComponentState) then - IsVSplitterHit := InHeaderSplitterArea(P) - else - IsVSplitterHit := InHeaderSplitterArea(P) and CanSplitterResize(P); - IsHSplitterHit := HSplitterHit; + if DragMessage = dmDragMove then + with ScreenToClient(Pos) do + DoAutoScroll(X, Y); - if IsVSplitterHit or IsHSplitterHit then - begin - FTrackStart := P; - FColumns.HoverIndex := NoColumn; - if IsVSplitterHit then - begin - if not (csDesigning in Treeview.ComponentState) then - DoBeforeHeightTracking(GetShiftState); - Include(FStates, hsHeightTrackPending); - end - else - begin - if not (csDesigning in Treeview.ComponentState) then - DoBeforeColumnWidthTracking(FColumns.TrackIndex, GetShiftState); - Include(FStates, hsColumnWidthTrackPending); - end; + ShiftState := 0; + // Alt key will be queried by the KeysToShiftState function in DragOver. + if GetKeyState(VK_SHIFT) < 0 then + ShiftState := ShiftState or MK_SHIFT; + if GetKeyState(VK_CONTROL) < 0 then + ShiftState := ShiftState or MK_CONTROL; - SetCapture(Treeview.Handle); - Result := True; - Message.Result := 0; - end - else - if IsInHeader then - begin - HitIndex := Columns.AdjustDownColumn(P); - // in design-time header columns are always draggable - if ((csDesigning in Treeview.ComponentState) and (HitIndex > NoColumn)) or - ((hoDrag in FOptions) and (HitIndex > NoColumn) and (coDraggable in FColumns[HitIndex].Options)) then + // Allowed drop effects are simulated for VCL dd. + Effect := DROPEFFECT_MOVE or DROPEFFECT_COPY; + DragOver(S, ShiftState, TDragState(DragMessage), Pos, Effect); + FLastVCLDragTarget := FDropTargetNode; + FVCLDragEffect := Effect; + if (DragMessage = dmDragLeave) and Assigned(FDropTargetNode) then begin - // Show potential drag operation. - // Disabled columns do not start a drag operation because they can't be clicked. - Include(FStates, hsDragPending); - SetCapture(Treeview.Handle); - Result := True; - Message.Result := 0; + InvalidateNode(FDropTargetNode); + FDropTargetNode := nil; end; + Result := LRESULT(Effect); end; - - // This is a good opportunity to notify the application. - if not (csDesigning in Treeview.ComponentState) and IsInHeader then - FOwner.DoHeaderMouseDown(mbLeft, GetShiftState, P.X, P.Y + Integer(FHeight)); - end; - WM_NCRBUTTONDOWN: - begin - with TWMNCRButtonDown(Message) do - P := FOwner.ScreenToClient(Point(XCursor, YCursor)); - if InHeader(P) then - FOwner.DoHeaderMouseDown(mbRight, GetShiftState, P.X, P.Y + Integer(FHeight)); - end; - WM_NCRBUTTONUP: - if not (csDesigning in FOwner.ComponentState) then - with TWMNCRButtonUp(Message) do - begin - Application.CancelHint; - P := FOwner.ScreenToClient(Point(XCursor, YCursor)); - if InHeader(P) then + dmDragDrop: begin - HandleMessage := FColumns.HandleClick(P, mbRight, True, False); - FOwner.DoHeaderMouseUp(mbRight, GetShiftState, P.X, P.Y + Integer(FHeight)); - end; - end; - // When the tree window has an active mouse capture then we only get "client-area" messages. - WM_LBUTTONUP, - WM_NCLBUTTONUP: - begin - Application.CancelHint; + ShiftState := 0; + // Alt key will be queried by the KeysToShiftState function in DragOver + if GetKeyState(VK_SHIFT) < 0 then + ShiftState := ShiftState or MK_SHIFT; + if GetKeyState(VK_CONTROL) < 0 then + ShiftState := ShiftState or MK_CONTROL; - if FStates <> [] then - begin - ReleaseCapture; - if hsDragging in FStates then - begin - // successfull dragging moves columns - with TWMLButtonUp(Message) do - P := Treeview.ClientToScreen(Point(XPos, YPos)); - GetWindowRect(Treeview.Handle, R); - with FColumns do - begin - FDragImage.EndDrag; - - //Problem fixed: - //Column Header does not paint correctly after a drop in certain conditions - //** The conditions are, drag is across header, mouse is not moved after - //the drop and the graphics hardware is slow in certain operations (encountered - //on Windows 10). - //Fix for the problem on certain systems where the dropped column header - //does not appear in the new position if the mouse is not moved after - //the drop. The reason is that the restore backup image operation (BitBlt) - //in the above EndDrag is slower than the header repaint in the code below - //and overlaps the new changed header with the older image. - //This happens because BitBlt seems to operate in its own thread in the - //graphics hardware and finishes later than the following code. - // - //To solve this problem, we introduce a small delay here so that the - //changed header in the following code is correctly repainted after - //the delayed BitBlt above has finished operation to restore the old - //backup image. - sleep(50); - - if (FDropTarget > -1) and (FDropTarget <> FDragIndex) and PtInRect(R, P) then + // allowed drop effects are simulated for VCL dd, + // replace target node with cached node from other VCL dd messages + if Assigned(FDropTargetNode) then + InvalidateNode(FDropTargetNode); + FDropTargetNode := FLastVCLDragTarget; + P := Point(Pos.X, Pos.Y); + P := ScreenToClient(P); + try + DoDragDrop(S, nil, Formats, KeysToShiftState(ShiftState), P, FVCLDragEffect, FLastDropMode); + finally + if Assigned(FDropTargetNode) then begin - OldPosition := FColumns[FDragIndex].Position; - if FColumns.DropBefore then - begin - if FColumns[FDragIndex].Position < FColumns[FDropTarget].Position then - FColumns[FDragIndex].Position := Max(0, FColumns[FDropTarget].Position - 1) - else - FColumns[FDragIndex].Position := FColumns[FDropTarget].Position; - end - else - begin - if FColumns[FDragIndex].Position < FColumns[FDropTarget].Position then - FColumns[FDragIndex].Position := FColumns[FDropTarget].Position - else - FColumns[FDragIndex].Position := FColumns[FDropTarget].Position + 1; - end; - Treeview.DoHeaderDragged(FDragIndex, OldPosition); - end - else - Treeview.DoHeaderDraggedOut(FDragIndex, P); - FDropTarget := NoColumn; - end; - Invalidate(nil); - end; - Result := True; - Message.Result := 0; - end; - - case Message.Msg of - WM_LBUTTONUP: - with TWMLButtonUp(Message) do - begin - if FColumns.DownIndex > NoColumn then - FColumns.HandleClick(Point(XPos, YPos), mbLeft, False, False); - if FStates <> [] then - FOwner.DoHeaderMouseUp(mbLeft, KeysToShiftState(Keys), XPos, YPos); - end; - WM_NCLBUTTONUP: - with TWMNCLButtonUp(Message) do - begin - P := FOwner.ScreenToClient(Point(XCursor, YCursor)); - FColumns.HandleClick(P, mbLeft, False, False); - FOwner.DoHeaderMouseUp(mbLeft, GetShiftState, P.X, P.Y + Integer(FHeight)); + InvalidateNode(FDropTargetNode); + FDropTargetNode := nil; + end; end; - end; - - if FColumns.TrackIndex > NoColumn then - begin - if hsColumnWidthTracking in FStates then - DoAfterColumnWidthTracking(FColumns.TrackIndex); - Invalidate(Columns[FColumns.TrackIndex]); - FColumns.TrackIndex := NoColumn; - end; - if FColumns.DownIndex > NoColumn then - begin - Invalidate(Columns[FColumns.DownIndex]); - FColumns.DownIndex := NoColumn; - end; - if hsHeightTracking in FStates then - DoAfterHeightTracking; - - FStates := FStates - [hsDragging, hsDragPending, - hsColumnWidthTracking, hsColumnWidthTrackPending, - hsHeightTracking, hsHeightTrackPending]; - end;// WM_NCLBUTTONUP - // hovering, mouse leave detection - WM_NCMOUSEMOVE: - with TWMNCMouseMove(Message), FColumns do - begin - P := Treeview.ScreenToClient(Point(XCursor, YCursor)); - Treeview.DoHeaderMouseMove(GetShiftState, P.X, P.Y + Integer(FHeight)); - if InHeader(P) and ((AdjustHoverColumn(P)) or ((FDownIndex >= 0) and (FHoverIndex <> FDownIndex))) then - begin - // We need a mouse leave detection from here for the non client area. - // TODO: The best solution available would be the TrackMouseEvent API. - // With the drop of the support of Win95 totally and WinNT4 we should replace the timer. - Treeview.StopTimer(HeaderTimer); - SetTimer(Treeview.Handle, HeaderTimer, 50, nil); - // use Delphi's internal hint handling for header hints too - if hoShowHint in FOptions then - begin - // client coordinates! - XCursor := P.X; - YCursor := P.Y + Integer(FHeight); - Application.HintMouseMessage(Treeview, Message); end; - end; - end; - WM_TIMER: - if TWMTimer(Message).TimerID = HeaderTimer then - begin - // determine current mouse position to check if it left the window - GetCursorPos(P); - P := Treeview.ScreenToClient(P); - with FColumns do - begin - if not InHeader(P) or ((FDownIndex > NoColumn) and (FHoverIndex <> FDownIndex)) then + dmFindTarget: begin - Treeview.StopTimer(HeaderTimer); - FHoverIndex := NoColumn; - FClickIndex := NoColumn; - FDownIndex := NoColumn; - FCheckBoxHit := False; - Result := True; - Message.Result := 0; - Invalidate(nil); - end; - end; - end; - WM_MOUSEMOVE: // mouse capture and general message redirection - Result := HandleHeaderMouseMove(TWMMouseMove(Message)); - WM_SETCURSOR: - // Feature: design-time header - if (FStates = []) then - begin - // Retrieve last cursor position (GetMessagePos does not work here, I don't know why). - GetCursorPos(P); - - // Is the mouse in the header rectangle and near the splitters? - P := Treeview.ScreenToClient(P); - IsHSplitterHit := HSplitterHit; - // in design-time header columns are always resizable - if (csDesigning in Treeview.ComponentState) then - IsVSplitterHit := InHeaderSplitterArea(P) - else - IsVSplitterHit := InHeaderSplitterArea(P) and CanSplitterResize(P); - - if IsVSplitterHit or IsHSplitterHit then - begin - NewCursor := Screen.Cursors[Treeview.Cursor]; - if IsVSplitterHit and ((hoHeightResize in FOptions) or (csDesigning in Treeview.ComponentState)) then - NewCursor := Screen.Cursors[crVertSplit] - else - if IsHSplitterHit then - NewCursor := Screen.Cursors[crHeaderSplit]; + Result := LRESULT(ControlAtPos(ScreenToClient(Pos), False)); + if Result = 0 then + Result := LRESULT(Self); - if not (csDesigning in Treeview.ComponentState) then - Treeview.DoGetHeaderCursor(NewCursor); - Result := NewCursor <> Screen.Cursors[crDefault]; - if Result then - begin - Winapi.Windows.SetCursor(NewCursor); - Message.Result := 1; - end; - end; - end - else - begin - Message.Result := 1; - Result := True; - end; - WM_KEYDOWN, - WM_KILLFOCUS: - if (Message.Msg = WM_KILLFOCUS) or - (TWMKeyDown(Message).CharCode = VK_ESCAPE) then - begin - if hsDragging in FStates then - begin - ReleaseCapture; - FDragImage.EndDrag; - Exclude(FStates, hsDragging); - FColumns.DropTarget := NoColumn; - Invalidate(nil); - Result := True; - Message.Result := 0; - end - else - begin - if [hsColumnWidthTracking, hsHeightTracking] * FStates <> [] then - begin - ReleaseCapture; - if hsColumnWidthTracking in FStates then - DoAfterColumnWidthTracking(FColumns.TrackIndex); - if hsHeightTracking in FStates then - DoAfterHeightTracking; - Result := True; - Message.Result := 0; + // This is a reliable place to check whether VCL drag has + // really begun. + if tsVCLDragPending in FStates then + DoStateChange([tsVCLDragging], [tsVCLDragPending, tsEditPending, tsClearPending]); end; - - FStates := FStates - [hsColumnWidthTracking, hsColumnWidthTrackPending, - hsHeightTracking, hsHeightTrackPending]; - end; end; + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.ImageListChange(Sender: TObject); +procedure TBaseVirtualTree.CMEnabledChanged(var Message: TMessage); begin - if not (csDestroying in Treeview.ComponentState) then - Invalidate(nil); + inherited; + + // Need to invalidate the non-client area as well, since the header must be redrawn too. + if csDesigning in ComponentState then + RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.PrepareDrag(P, Start: TPoint); - -// Initializes dragging of the header, P is the current mouse postion and Start the initial mouse position. +procedure TBaseVirtualTree.CMFontChanged(var Message: TMessage); var - Image: TBitmap; - ImagePos: TPoint; - DragColumn: TVirtualTreeColumn; - RTLOffset: Integer; + HeaderMessage: TMessage; begin - // Determine initial position of drag image (screen coordinates). - FColumns.DropTarget := NoColumn; - Start := Treeview.ScreenToClient(Start); - Inc(Start.Y, FHeight); - FColumns.DragIndex := FColumns.ColumnFromPosition(Start); - DragColumn := FColumns[FColumns.DragIndex]; - - Image := TBitmap.Create; - with Image do - try - PixelFormat := pf32Bit; - SetSize(DragColumn.Width, FHeight); - - // Erase the entire image with the color key value, for the case not everything - // in the image is covered by the header image. - Canvas.Brush.Color := clBtnFace; - Canvas.FillRect(Rect(0, 0, Width, Height)); - - if TreeView.UseRightToLeftAlignment then - RTLOffset := Treeview.ComputeRTLOffset - else - RTLOffset := 0; - with DragColumn do - FColumns.PaintHeader(Canvas, Rect(FLeft, 0, FLeft + Width, Height), Point(-RTLOffset, 0), RTLOffset); - - if Treeview.UseRightToLeftAlignment then - ImagePos := Treeview.ClientToScreen(Point(DragColumn.Left + Treeview.ComputeRTLOffset(True), 0)) - else - ImagePos := Treeview.ClientToScreen(Point(DragColumn.Left, 0)); - // Column rectangles are given in local window coordinates not client coordinates. - Dec(ImagePos.Y, FHeight); + inherited; - if hoRestrictDrag in FOptions then - FDragImage.MoveRestriction := dmrHorizontalOnly - else - FDragImage.MoveRestriction := dmrNone; - FDragImage.PrepareDrag(Image, ImagePos, P, nil); - FDragImage.ShowDragImage; - finally - Image.Free; + if not (csLoading in ComponentState) then + begin + if HandleAllocated then begin + AutoScale(False); + Invalidate; + end end; -end; - -//---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.ReadColumns(Reader: TReader); - -begin - Include(FStates, hsLoading); - Columns.Clear; - Reader.ReadValue; - Reader.ReadCollection(Columns); - Exclude(FStates, hsLoading); + HeaderMessage.Msg := CM_PARENTFONTCHANGED; + HeaderMessage.WParam := 0; + HeaderMessage.LParam := 0; + HeaderMessage.Result := 0; + TVTHeaderCracker(FHeader).HandleMessage(HeaderMessage); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.RecalculateHeader; +procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow); -// Initiate a recalculation of the non-client area of the owner tree. +// Determines hint message (tooltip) and out-of-hint rect. +// Note: A special handling is needed here because we cannot pass wide strings back to the caller. +// I had to introduce the hint data record anyway so we can use this to pass the hint string. +// We still need to set a dummy hint string in the message to make the VCL showing the hint window. +var + NodeRect: TRect; + SpanColumn, + Dummy, + ColLeft, + ColRight: Integer; + HitInfo: THitInfo; + ShowOwnHint: Boolean; + IsFocusedOrEditing: Boolean; + ParentForm: TCustomForm; + BottomRightCellContentMargin: TPoint; + HintKind: TVTHintKind; begin - if Treeview.HandleAllocated then + with Message do begin - Treeview.UpdateHeaderRect; - SetWindowPos(Treeview.Handle, 0, 0, 0, 0, 0, SWP_FRAMECHANGED or SWP_NOMOVE or SWP_NOACTIVATE or SWP_NOOWNERZORDER or - SWP_NOSENDCHANGING or SWP_NOSIZE or SWP_NOZORDER); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTHeader.RescaleHeader; - -// Rescale the fixed elements (fixed columns, header itself) to FixedAreaConstraints. - -var - FixedWidth, - MaxFixedWidth, - MinFixedWidth: Integer; + Result := 1; - //--------------- local function -------------------------------------------- + if PtInRect(FLastHintRect, HintInfo.CursorPos) then + Exit; - procedure ComputeConstraints; + // Determine node for which to show hint/tooltip. + with HintInfo^ do + GetHitTestInfoAt(CursorPos.X, CursorPos.Y, True, HitInfo); - var - I: TColumnIndex; - - begin - with FColumns do + // Make sure a hint is only shown if the tree or at least its parent form is active. + // Active editing is ok too as long as we don't want the hint for the current edit node. + if IsEditing then + IsFocusedOrEditing := HitInfo.HitNode <> FFocusedNode + else begin - I := GetFirstVisibleColumn; - while I > NoColumn do - begin - if (coFixed in FColumns[I].Options) and (FColumns[I].Width < FColumns[I].MinWidth) then - FColumns[I].InternalSetWidth(FColumns[I].MinWidth); //SetWidth has side effects and this bypasses them - I := GetNextVisibleColumn(I); - end; - FixedWidth := GetVisibleFixedWidth; + IsFocusedOrEditing := Focused; + ParentForm := GetParentForm(Self); + if Assigned(ParentForm) then + IsFocusedOrEditing := ParentForm.Focused or Application.Active; end; - with FFixedAreaConstraints do + if (GetCapture = 0) and ShowHint and not (Dragging or IsMouseSelecting) and ([tsScrolling] * FStates = []) and + (FHeader.States = []) and IsFocusedOrEditing then begin - MinFixedWidth := (TreeView.ClientWidth * FMinWidthPercent) div 100; - MaxFixedWidth := (TreeView.ClientWidth * FMaxWidthPercent) div 100; - end; - end; - - //----------- end local function -------------------------------------------- - -begin - if ([csLoading, csReading, csWriting, csDestroying] * Treeview.ComponentState = []) and not - (hsLoading in FStates) and Treeview.HandleAllocated then - begin - Include(FStates, hsScaling); - - SetHeight(FHeight); - RecalculateHeader; - - with FFixedAreaConstraints do - if (FMaxWidthPercent > 0) or (FMinWidthPercent > 0) or (FMinHeightPercent > 0) or (FMaxHeightPercent > 0) then + with HintInfo^ do begin - ComputeConstraints; + Result := 0; + ShowOwnHint := False; - with FColumns do - if (FMaxWidthPercent > 0) and (FixedWidth > MaxFixedWidth) then - ResizeColumns(MaxFixedWidth - FixedWidth, 0, Count - 1, [coVisible, coFixed]) + //workaround for issue #291 + //it duplicates parts of the following code and code in TVirtualTreeHintWindow + HintStr := ''; + if FHeader.UseColumns and (hoShowHint in FHeader.Options) and FHeader.InHeader(CursorPos) then + begin + CursorRect := FHeaderRect; + // Convert the cursor rectangle into real client coordinates. + OffsetRect(CursorRect, 0, -Integer(FHeader.Height)); + HitInfo.HitColumn := TVirtualTreeColumnsCracker(FHeader.Columns).GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right); + if (HitInfo.HitColumn > NoColumn) and not (csLButtonDown in ControlState) and + (FHeader.Columns[HitInfo.HitColumn].Hint <> '') then + HintStr := FHeader.Columns[HitInfo.HitColumn].Hint; + end + else + if HintMode = hmDefault then + HintStr := GetShortHint(Hint) + else + if Assigned(HitInfo.HitNode) and (HitInfo.HitColumn > InvalidColumn) then + begin + if HintMode = hmToolTip then + HintStr := DoGetNodeToolTip(HitInfo.HitNode, HitInfo.HitColumn, fHintData.LineBreakStyle) else - if (FMinWidthPercent > 0) and (FixedWidth < MinFixedWidth) then - ResizeColumns(MinFixedWidth - FixedWidth, 0, Count - 1, [coVisible, coFixed]); - - FColumns.UpdatePositions; - end; - - Exclude(FStates, hsScaling); - Exclude(FStates, hsNeedScaling); - end; -end; + HintStr := DoGetNodeHint(HitInfo.HitNode, HitInfo.HitColumn, fHintData.LineBreakStyle); + end; -//---------------------------------------------------------------------------------------------------------------------- + // First check whether there is a header hint to show. + if FHeader.UseColumns and (hoShowHint in FHeader.Options) and FHeader.InHeader(CursorPos) then + begin + CursorRect := FHeaderRect; + // Convert the cursor rectangle into real client coordinates. + OffsetRect(CursorRect, 0, -Integer(FHeader.Height)); + HitInfo.HitColumn := TVirtualTreeColumnsCracker(FHeader.Columns).GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right); + // align the vertical hint position on the bottom bound of the header, but + // avoid overlapping of mouse cursor and hint + HintPos.Y := Max(HintPos.Y, ClientToScreen(Point(0, CursorRect.Bottom)).Y); + // Note: the test for the left mouse button in ControlState might cause problems whenever the VCL does not + // realize when the button is released. This, for instance, happens when doing OLE drag'n drop and + // cancel this with ESC. + if (HitInfo.HitColumn > NoColumn) and not (csLButtonDown in ControlState) then + begin + HintStr := FHeader.Columns[HitInfo.HitColumn].Hint; + if HintStr = '' then + with FHeader.Columns[HitInfo.HitColumn] do + begin + if (2 * FMargin + CaptionWidth + 1) >= Width then + HintStr := CaptionText; + end; + if HintStr <> '' then + ShowOwnHint := True + else + Result := 1; + end + else + Result := 1; + end + else + begin + // Default mode is handled as would the tree be a usual VCL control (no own hint window necessary). + if FHintMode = hmDefault then + HintStr := GetShortHint(Hint) + else + begin + if Assigned(HitInfo.HitNode) and (HitInfo.HitColumn > InvalidColumn) then + begin + // An owner-draw tree should only display a hint when at least + // its OnGetHintSize event handler is assigned. + DoGetHintKind(HitInfo.HitNode, HitInfo.HitColumn, HintKind); + FHintData.HintRect := Rect(0, 0, 0, 0); + if (HintKind = vhkOwnerDraw) then + begin + DoGetHintSize(HitInfo.HitNode, HitInfo.HitColumn, FHintData.HintRect); + ShowOwnHint := not IsRectEmpty(FHintData.HintRect); + end + else + // For trees displaying text hints, a decision about showing the hint or not is based + // on the hint string (if it is empty then no hint is shown). + ShowOwnHint := True; -procedure TVTHeader.UpdateMainColumn(); + if ShowOwnHint then + begin + if HitInfo.HitColumn > NoColumn then + begin + FHeader.Columns.GetColumnBounds(HitInfo.HitColumn, ColLeft, ColRight); + // The right column border might be extended if column spanning is enabled. + if toAutoSpanColumns in FOptions.AutoOptions then + begin + SpanColumn := HitInfo.HitColumn; + repeat + Dummy := FHeader.Columns.GetNextVisibleColumn(SpanColumn); + if (Dummy = InvalidColumn) or not ColumnIsEmpty(HitInfo.HitNode, Dummy) then + Break; + SpanColumn := Dummy; + until False; + if SpanColumn <> HitInfo.HitColumn then + FHeader.Columns.GetColumnBounds(SpanColumn, Dummy, ColRight); + end; + end + else + begin + ColLeft := 0; + ColRight := ClientWidth; + end; -// Called once the load process of the owner tree is done. + if FHintMode <> hmTooltip then + begin + // Node specific hint text. + CursorRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, False); + CursorRect.Left := ColLeft; + CursorRect.Right := ColRight; + // Align the vertical hint position on the bottom bound of the node, but + // avoid overlapping of mouse cursor and hint. + HintPos.Y := Max(HintPos.Y, ClientToScreen(CursorRect.BottomRight).Y) + ScaledPixels(2); + end + else + begin + // Tool tip to show. This means the full caption of the node must be displayed. + if vsMultiline in HitInfo.HitNode.States then + begin + if hiOnItemLabel in HitInfo.HitPositions then + begin + ShowOwnHint := True; + NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, True, False); + end + else + ShowOwnHint := False; + end + else + begin + NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, True, True, True); + BottomRightCellContentMargin := DoGetCellContentMargin(HitInfo.HitNode, HitInfo.HitColumn, ccmtBottomRightOnly); -begin - if FMainColumn < 0 then - MainColumn := 0; - if FMainColumn > FColumns.Count - 1 then - MainColumn := FColumns.Count - 1; - if (FMainColumn >= 0) and not (coVisible in Self.Columns[FMainColumn].Options) then - begin - // Issue #946: Choose new MainColumn if current one ist not visible - MainColumn := Self.Columns.GetFirstVisibleColumn(); - end -end; + ShowOwnHint := (HitInfo.HitColumn > InvalidColumn) and PtInRect(NodeRect, CursorPos) and + (CursorPos.X <= ColRight) and (CursorPos.X >= ColLeft) and + ( + // Show hint also if the node text is partially out of the client area. + // "ColRight - 1", since the right column border is not part of this cell. + ( (NodeRect.Right + BottomRightCellContentMargin.X) > Min(ColRight - 1, ClientWidth) ) or + (NodeRect.Left < Max(ColLeft, 0)) or + ( (NodeRect.Bottom + BottomRightCellContentMargin.Y) > ClientHeight ) or + (NodeRect.Top < 0) + ); + end; -//---------------------------------------------------------------------------------------------------------------------- + if ShowOwnHint then + begin + // Node specific hint text given will be retrieved when needed. + HintPos := ClientToScreen(Point(NodeRect.Left, NodeRect.Top)); + CursorRect := NodeRect; + end + else + // nothing to show + Result := 1; + end; + end + else + Result := 1; // Avoid hint if this is a draw tree returning an empty hint rectangle. + end + else + begin + // No node so fall back to control's hint (if indicated) or show nothing. + if FHintMode = hmHintAndDefault then + begin + HintStr := GetShortHint(Hint); -procedure TVTHeader.UpdateSpringColumns; + // Fix for the problem: Default Hint once shown stayed even when + // node hint was to be displayed. The reason was that CursorRect + // was for the full client area. Now reducing it to remove the + // columns from it. + if BidiMode = bdLeftToRight then + CursorRect.Left := Header.Columns.TotalWidth + else + CursorRect.right := CursorRect.right - Header.Columns.TotalWidth; -var - I: TColumnIndex; - SpringCount: Integer; - Sign: Integer; - ChangeBy: Single; - Difference: Single; - NewAccumulator: Single; + if Length(HintStr) = 0 then + Result := 1 + else + ShowOwnHint := True; + end + else + Result := 1; + end; + end; + end; -begin - with TreeView do - ChangeBy := FHeaderRect.Right - FHeaderRect.Left - FLastWidth; - if (hoAutoSpring in FOptions) and (FLastWidth <> 0) and (ChangeBy <> 0) then - begin - // Stay positive if downsizing the control. - if ChangeBy < 0 then - Sign := -1 - else - Sign := 1; - ChangeBy := Abs(ChangeBy); - // Count how many columns have spring enabled. - SpringCount := 0; - for I := 0 to FColumns.Count-1 do - if [coVisible, coAutoSpring] * FColumns[I].Options = [coVisible, coAutoSpring] then - Inc(SpringCount); - if SpringCount > 0 then - begin - // Calculate the size to add/sub to each columns. - Difference := ChangeBy / SpringCount; - // Adjust the column's size accumulators and resize if the result is >= 1. - for I := 0 to FColumns.Count - 1 do - if [coVisible, coAutoSpring] * FColumns[I].Options = [coVisible, coAutoSpring] then + // Set our own hint window class and prepare structure to be passed to the hint window. + if ShowOwnHint and (Result = 0) then begin - // Sum up rest changes from previous runs and the amount from this one and store it in the - // column. If there is at least one pixel difference then do a resize and reset the accumulator. - NewAccumulator := FColumns[I].SpringRest + Difference; - // Set new width if at least one pixel size difference is reached. - if NewAccumulator >= 1 then - FColumns[I].SetWidth(FColumns[I].Width + (Trunc(NewAccumulator) * Sign)); - FColumns[I].SpringRest := Frac(NewAccumulator); - - // Keep track of the size count. - ChangeBy := ChangeBy - Difference; - // Exit loop if resize count drops below freezing point. - if ChangeBy < 0 then - Break; - end; + HintWindowClass := GetHintWindowClass; + FHintData.HintText := HintStr; + FHintData.Tree := Self; + FHintData.Column := HitInfo.HitColumn; + FHintData.Node := HitInfo.HitNode; + FLastHintRect := CursorRect; + HintData := @FHintData; + end + else + FLastHintRect := Rect(0, 0, 0, 0); + end; + + // Remind that a hint is about to show. + if Result = 0 then + DoStateChange([tsHint]) + else + DoStateChange([], [tsHint]); end; end; - with TreeView do - FLastWidth := FHeaderRect.Right - FHeaderRect.Left; end; //---------------------------------------------------------------------------------------------------------------------- -type - // --- HACK WARNING! - // This type cast is a partial rewrite of the private section of TWriter. The purpose is to have access to - // the FPropPath member, which is otherwise not accessible. The reason why this access is needed is that - // with nested components this member contains unneeded property path information. These information prevent - // successful load of the stored properties later. - // In System.Classes.pas you can see that FPropPath is reset several times to '' to prevent this case for certain properies. - // Unfortunately, there is no clean way for us here to do the same. - {$hints off} - TWriterHack = class(TFiler) - private - FRootAncestor: TComponent; - FPropPath: string; - end; - {$hints on} - -procedure TVTHeader.WriteColumns(Writer: TWriter); - -// Write out the columns but take care for the case VT is a nested component. +procedure TBaseVirtualTree.CMHintShowPause(var Message: TCMHintShowPause); -var - LastPropPath: string; +// Tells the application that the tree (and only the tree) does not want a delayed tool tip. +// Normal hints / header hints use the default delay (except for the first time). -begin - // Save last property path for restoration. - LastPropPath := TWriterHack(Writer).FPropPath; - try - // If VT is a nested component then this path contains the name of the parent component at this time - // (otherwise it is already empty). This path is then combined with the property name under which the tree - // is defined in the parent component. Unfortunately, the load code in System.Classes.pas does not consider this case - // is then unable to load this property. - TWriterHack(Writer).FPropPath := ''; - Writer.WriteCollection(Columns); - finally - TWriterHack(Writer).FPropPath := LastPropPath; - end; + begin + if ShowHint and (FHintMode = hmToolTip) then + Message.Pause^ := 0; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.AllowFocus(ColumnIndex: TColumnIndex): Boolean; +procedure TBaseVirtualTree.CMMouseEnter(var Message: TMessage); begin - Result := False; - if not FColumns.IsValidColumn(ColumnIndex) then - Exit; // Just in case. - - Result := (coAllowFocus in FColumns[ColumnIndex].Options); + DoMouseEnter(); + inherited; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.Assign(Source: TPersistent); +procedure TBaseVirtualTree.CMMouseLeave(var Message: TMessage); + +var + LeaveStates: TVirtualTreeStates; begin - if Source is TVTHeader then + // Reset the last used hint rectangle in case the mouse enters the window within the bounds + if Assigned(FHintData.Tree) then + FHintData.Tree.FLastHintRect := Rect(0, 0, 0, 0); + + LeaveStates := [tsHint]; + if [tsWheelPanning, tsWheelScrolling] * FStates = [] then + begin + StopTimer(ScrollTimer); + LeaveStates := LeaveStates + [tsScrollPending, tsScrolling]; + end; + DoStateChange([], LeaveStates); + if Assigned(FCurrentHotNode) then begin - AutoSizeIndex := TVTHeader(Source).AutoSizeIndex; - Background := TVTHeader(Source).Background; - Columns := TVTHeader(Source).Columns; - Font := TVTHeader(Source).Font; - FixedAreaConstraints.Assign(TVTHeader(Source).FixedAreaConstraints); - Height := TVTHeader(Source).Height; - Images := TVTHeader(Source).Images; - MainColumn := TVTHeader(Source).MainColumn; - Options := TVTHeader(Source).Options; - ParentFont := TVTHeader(Source).ParentFont; - PopupMenu := TVTHeader(Source).PopupMenu; - SortColumn := TVTHeader(Source).SortColumn; - SortDirection := TVTHeader(Source).SortDirection; - Style := TVTHeader(Source).Style; + DoHotChange(FCurrentHotNode, nil); + if (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.MiscOptions) then + InvalidateNode(FCurrentHotNode); + FCurrentHotNode := nil; + end; - RescaleHeader; - end - else - inherited; + if Assigned(Header) then + begin + with TVirtualTreeColumnsCracker(Header.Columns) do + begin + DownIndex := NoColumn; + HoverIndex := NoColumn; + CheckBoxHit := False; + end; + end; + DoMouseLeave(); + inherited; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.AutoFitColumns(Animated: Boolean = True; SmartAutoFitType: TSmartAutoFitType = smaUseColumnOption; - RangeStartCol: Integer = NoColumn; RangeEndCol: Integer = NoColumn); - - //--------------- local functions ------------------------------------------- - - function GetUseSmartColumnWidth(ColumnIndex: TColumnIndex): Boolean; +procedure TBaseVirtualTree.CMMouseWheel(var Message: TCMMouseWheel); - begin - case SmartAutoFitType of - smaAllColumns: - Result := True; - smaUseColumnOption: - Result := coSmartResize in FColumns.Items[ColumnIndex].Options; - else - Result := False; - end; - end; +var + ScrollAmount: Integer; + ScrollLines: DWORD; + RTLFactor: Integer; + WheelFactor: Double; - //---------------------------------------------------------------------------- +begin + StopWheelPanning; - procedure DoAutoFitColumn(Column: TColumnIndex); + inherited; + if Message.Result = 0 then begin - with FColumns do - if ([coResizable, coVisible] * Items[FPositionToIndex[Column]].Options = [coResizable, coVisible]) and - DoBeforeAutoFitColumn(FPositionToIndex[Column], SmartAutoFitType) and not TreeView.OperationCanceled then + with Message do + begin + Result := 1; + WheelFactor := WheelDelta / WHEEL_DELTA; + if (FRangeY > Cardinal(ClientHeight)) and (not (ssShift in ShiftState)) then + begin + // Scroll vertically if there's something to scroll... + if ssCtrl in ShiftState then + ScrollAmount := Trunc(WheelFactor * ClientHeight) + else + begin + SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, @ScrollLines, 0); + if ScrollLines = WHEEL_PAGESCROLL then + ScrollAmount := Trunc(WheelFactor * ClientHeight) + else + ScrollAmount := Integer(Trunc(WheelFactor * ScrollLines * FDefaultNodeHeight)); + end; + SetOffsetY(FOffsetY + ScrollAmount); + end + else begin - if Animated then - AnimatedResize(FPositionToIndex[Column], Treeview.GetMaxColumnWidth(FPositionToIndex[Column], - GetUseSmartColumnWidth(FPositionToIndex[Column]))) + // ...else scroll horizontally if there's something to scroll. + if UseRightToLeftAlignment then + RTLFactor := -1 else - FColumns[FPositionToIndex[Column]].Width := Treeview.GetMaxColumnWidth(FPositionToIndex[Column], - GetUseSmartColumnWidth(FPositionToIndex[Column])); + RTLFactor := 1; - DoAfterAutoFitColumn(FPositionToIndex[Column]); + if ssCtrl in ShiftState then + ScrollAmount := Trunc(WheelFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth)) + else + begin + SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, @ScrollLines, 0); + ScrollAmount := Trunc(WheelFactor * ScrollLines * FHeader.Columns.GetScrollWidth); + end; + SetOffsetX(FOffsetX + RTLFactor * ScrollAmount); end; + end; + end; - //--------------- end local functions ---------------------------------------- +end; -var - I: Integer; - StartCol, - EndCol: Integer; +//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.CMSysColorChange(var Message: TMessage); begin - StartCol := Max(NoColumn + 1, RangeStartCol); + inherited; + Message.Msg := WM_SYSCOLORCHANGE; + DefaultHandler(Message); +end; - if RangeEndCol <= NoColumn then - EndCol := FColumns.Count - 1 - else - EndCol := Min(RangeEndCol, FColumns.Count - 1); +//---------------------------------------------------------------------------------------------------------------------- - if StartCol > EndCol then - Exit; // nothing to do +procedure TBaseVirtualTree.TVMGetItem(var Message: TMessage); - TreeView.StartOperation(okAutoFitColumns); - FDoingAutoFitColumns := true; - try - if Assigned(TreeView.FOnBeforeAutoFitColumns) then - TreeView.FOnBeforeAutoFitColumns(Self, SmartAutoFitType); +// Screen reader support function. The method returns information about a particular node. + +const + StateMask = TVIS_STATEIMAGEMASK or TVIS_OVERLAYMASK or TVIS_EXPANDED or TVIS_DROPHILITED or TVIS_CUT or + TVIS_SELECTED or TVIS_FOCUSED; - for I := StartCol to EndCol do - DoAutoFitColumn(I); +var + Item: PTVItemEx; + Node: PVirtualNode; + Ghosted: Boolean; + ImageIndex: TImageIndex; + R: TRect; + Text: string; +begin + // We can only return valid data if a nodes reference is given. + Item := Pointer(Message.LParam); + Message.Result := Ord(((Item.mask and TVIF_HANDLE) <> 0) and Assigned(Item.hItem)); + if Message.Result = 1 then + begin + Node := Pointer(Item.hItem); + // Child count requested? + if (Item.mask and TVIF_CHILDREN) <> 0 then + Item.cChildren := Node.ChildCount; + // Index for normal image requested? + if (Item.mask and TVIF_IMAGE) <> 0 then + begin + ImageIndex := -1; + DoGetImageIndex(Node, ikNormal, -1, Ghosted, ImageIndex); + Item.iImage := ImageIndex; + end; + // Index for selected image requested? + if (Item.mask and TVIF_SELECTEDIMAGE) <> 0 then + begin + ImageIndex := -1; + DoGetImageIndex(Node, ikSelected, -1, Ghosted, ImageIndex); + Item.iSelectedImage := ImageIndex; + end; + // State info requested? + if (Item.mask and TVIF_STATE) <> 0 then + begin + // Everything, which is possible is returned. + Item.stateMask := StateMask; + Item.state := 0; + if Node = FFocusedNode then + Item.state := Item.state or TVIS_FOCUSED; + if vsSelected in Node.States then + Item.state := Item.state or TVIS_SELECTED; + if vsCutOrCopy in Node.States then + Item.state := Item.state or TVIS_CUT; + if Node = FDropTargetNode then + Item.state := Item.state or TVIS_DROPHILITED; + if vsExpanded in Node.States then + Item.state := Item.state or TVIS_EXPANDED; - if Assigned(TreeView.FOnAfterAutoFitColumns) then - TreeView.FOnAfterAutoFitColumns(Self); + // Construct state image and overlay image indices. They are one based, btw. + // and zero means there is no image. + ImageIndex := -1; + DoGetImageIndex(Node, ikState, -1, Ghosted, ImageIndex); + Item.state := Item.state or Byte(IndexToStateImageMask(ImageIndex + 1)); + ImageIndex := -1; + DoGetImageIndex(Node, ikOverlay, -1, Ghosted, ImageIndex); + Item.state := Item.state or Byte(IndexToOverlayMask(ImageIndex + 1)); + end; + // Node caption requested? + if (Item.mask and TVIF_TEXT) <> 0 then + begin + GetTextInfo(Node, -1, Font, R, Text); - finally - Treeview.EndOperation(okAutoFitColumns); - TreeView.Invalidate(); - FDoingAutoFitColumns := false; + StrLCopy(Item.pszText, PWideChar(Text), Item.cchTextMax - 1); + Item.pszText[Length(Text)] := #0; + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.InHeader(P: TPoint): Boolean; +procedure TBaseVirtualTree.TVMGetItemRect(var Message: TMessage); -// Determines whether the given point (client coordinates!) is within the header rectangle (non-client coordinates). +// Screen read support function. This method returns a node's display rectangle. var - R, RW: TRect; + TextOnly: Boolean; + Node: PVirtualNode; begin - R := Treeview.FHeaderRect; - - // Current position of the owner in screen coordinates. - GetWindowRect(Treeview.Handle, RW); - - // Convert to client coordinates. - MapWindowPoints(0, Treeview.Handle, RW, 2); - - // Consider the header within this rectangle. - OffsetRect(R, RW.Left, RW.Top); - Result := PtInRect(R, P); + // The lparam member is used two-way. On enter it contains a pointer to the item (node). + // On exit it is to be considered as pointer to a rectangle structure. + Node := Pointer(Pointer(Message.LParam)^); + Message.Result := Ord(IsVisible[Node]); + if Message.Result <> 0 then + begin + TextOnly := Message.WParam <> 0; + PRect(Message.LParam)^ := GetDisplayRect(Node, NoColumn, TextOnly); + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTHeader.InHeaderSplitterArea(P: TPoint): Boolean; +procedure TBaseVirtualTree.TVMGetNextItem(var Message: TMessage); -// Determines whether the given point (client coordinates!) hits the horizontal splitter area of the header. +// Screen read support function. This method returns a node depending on the requested case. var - R, RW: TRect; + Node: PVirtualNode; begin - if (P.Y > 2) or (P.Y < -2) or not (hoVisible in FOptions) then - Result := False - else - begin - R := Treeview.FHeaderRect; - Inc(R.Bottom, 2); - - // Current position of the owner in screen coordinates. - GetWindowRect(Treeview.Handle, RW); - - // Convert to client coordinates. - MapWindowPoints(0, Treeview.Handle, RW, 2); - - // Consider the header within this rectangle. - OffsetRect(R, RW.Left, RW.Top); - Result := PtInRect(R, P); + // Start with a nil result. + Message.Result := 0; + Node := Pointer(Message.LParam); + case Message.WParam of + TVGN_CARET: + Message.Result := LRESULT(FFocusedNode); + TVGN_CHILD: + if Assigned(Node) then + Message.Result := LRESULT(GetFirstChild(Node)); + TVGN_DROPHILITE: + Message.Result := LRESULT(FDropTargetNode); + TVGN_FIRSTVISIBLE: + Message.Result := LRESULT(GetFirstVisible(nil, True)); + TVGN_LASTVISIBLE: + Message.Result := LRESULT(GetLastVisible(nil, True)); + TVGN_NEXT: + if Assigned(Node) then + Message.Result := LRESULT(GetNextSibling(Node)); + TVGN_NEXTVISIBLE: + if Assigned(Node) then + Message.Result := LRESULT(GetNextVisible(Node, True)); + TVGN_PARENT: + if Assigned(Node) and (Node <> FRoot) and (Node.Parent <> FRoot) then + Message.Result := LRESULT(Node.Parent); + TVGN_PREVIOUS: + if Assigned(Node) then + Message.Result := LRESULT(GetPreviousSibling(Node)); + TVGN_PREVIOUSVISIBLE: + if Assigned(Node) then + Message.Result := LRESULT(GetPreviousVisible(Node, True)); + TVGN_ROOT: + Message.Result := LRESULT(GetFirst); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.Invalidate(Column: TVirtualTreeColumn; ExpandToBorder: Boolean = False; UpdateNowFlag : Boolean = False); - -// Because the header is in the non-client area of the tree it needs some special handling in order to initiate its -// repainting. -// If ExpandToBorder is True then not only the given column but everything or (depending on hoFullRepaintOnResize) just -// everything to its right (or left, in RTL mode) will be invalidated (useful for resizing). This makes only sense when -// a column is given. - -var - R, RW: TRect; - Flags: Cardinal; +procedure TBaseVirtualTree.WMCancelMode(var Message: TWMCancelMode); begin - if (hoVisible in FOptions) and Treeview.HandleAllocated then - with Treeview do - begin - if Column = nil then - R := FHeaderRect - else - begin - R := Column.GetRect; - if not (coFixed in Column.Options) then - OffsetRect(R, -FEffectiveOffsetX, 0); - if UseRightToLeftAlignment then - OffsetRect(R, ComputeRTLOffset, 0); - if ExpandToBorder then - begin - if (hoFullRepaintOnResize in FHeader.Options) then - begin - R.Left := FHeaderRect.Left; - R.Right := FHeaderRect.Right; - end - else - begin - if UseRightToLeftAlignment then - R.Left := FHeaderRect.Left - else - R.Right := FHeaderRect.Right; - end; - end; - end; - R.Bottom := Treeview.ClientHeight; // We want to repaint the entire column to bottom, not just the header - - // Current position of the owner in screen coordinates. - GetWindowRect(Handle, RW); + // Clear any transient state. + StopTimer(ExpandTimer); + StopTimer(EditTimer); + StopTimer(HeaderTimer); + StopTimer(ScrollTimer); + StopTimer(SearchTimer); + StopTimer(ThemeChangedTimer); + FSearchBuffer := ''; + FLastSearchNode := nil; - // Consider the header within this rectangle. - OffsetRect(R, RW.Left, RW.Top); + DoStateChange([], [tsClearPending, tsEditPending, tsOLEDragPending, tsVCLDragPending, tsDrawSelecting, + tsDrawSelPending, tsIncrementalSearching]); - // Expressed in client coordinates (because RedrawWindow wants them so, they will actually become negative). - MapWindowPoints(0, Handle, R, 2); - Flags := RDW_FRAME or RDW_INVALIDATE or RDW_VALIDATE or RDW_NOINTERNALPAINT or RDW_NOERASE or RDW_NOCHILDREN; - if UpdateNowFlag then - Flags := Flags or RDW_UPDATENOW; - RedrawWindow(Handle, @R, 0, Flags); - end; + inherited; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.LoadFromStream(const Stream: TStream); - -// restore the state of the header from the given stream - -var - Dummy, - Version: Integer; - S: AnsiString; - OldOptions: TVTHeaderOptions; +procedure TBaseVirtualTree.WMChar(var Message: TWMChar); begin - Include(FStates, hsLoading); - with Stream do - try - // Switch off all options which could influence loading the columns (they will be later set again). - OldOptions := FOptions; - FOptions := []; - - // Determine whether the stream contains data without a version number. - ReadBuffer(Dummy, SizeOf(Dummy)); - if Dummy > -1 then - begin - // Seek back to undo the read operation if this is an old stream format. - Seek(-SizeOf(Dummy), soFromCurrent); - Version := -1; - end - else // Read version number if this is a "versionized" format. - ReadBuffer(Version, SizeOf(Version)); - Columns.LoadFromStream(Stream, Version); - - ReadBuffer(Dummy, SizeOf(Dummy)); - AutoSizeIndex := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - Background := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - Height := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - FOptions := OldOptions; - Options := TVTHeaderOptions(Dummy); - // PopupMenu is neither saved nor restored - ReadBuffer(Dummy, SizeOf(Dummy)); - Style := TVTHeaderStyle(Dummy); - // TFont has no own save routine so we do it manually - with Font do - begin - ReadBuffer(Dummy, SizeOf(Dummy)); - Color := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - Height := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - SetLength(S, Dummy); - ReadBuffer(PAnsiChar(S)^, Dummy); - Name := UTF8ToString(S); - ReadBuffer(Dummy, SizeOf(Dummy)); - Pitch := TFontPitch(Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - Style := TFontStyles(Byte(Dummy)); - end; - - // Read data introduced by stream version 1+. - if Version > 0 then - begin - ReadBuffer(Dummy, SizeOf(Dummy)); - MainColumn := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - SortColumn := Dummy; - ReadBuffer(Dummy, SizeOf(Dummy)); - SortDirection := TSortDirection(Byte(Dummy)); - end; - - // Read data introduced by stream version 5+. - if Version > 4 then - begin - ReadBuffer(Dummy, SizeOf(Dummy)); - ParentFont := Boolean(Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - FMaxHeight := Integer(Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - FMinHeight := Integer(Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - FDefaultHeight := Integer(Dummy); - with FFixedAreaConstraints do - begin - ReadBuffer(Dummy, SizeOf(Dummy)); - FMaxHeightPercent := TVTConstraintPercent(Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - FMaxWidthPercent := TVTConstraintPercent(Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - FMinHeightPercent := TVTConstraintPercent(Dummy); - ReadBuffer(Dummy, SizeOf(Dummy)); - FMinWidthPercent := TVTConstraintPercent(Dummy); - end; - end; - finally - Exclude(FStates, hsLoading); - RecalculateHeader(); - Treeview.DoColumnResize(NoColumn); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TVTHeader.ResizeColumns(ChangeBy: Integer; RangeStartCol: TColumnIndex; RangeEndCol: TColumnIndex; - Options: TVTColumnOptions = [coVisible]): Integer; - -// Distribute the given width change to a range of columns. A 'fair' way is used to distribute ChangeBy to the columns, -// while ensuring that everything that can be distributed will be distributed. - -var - Start, - I: TColumnIndex; - ColCount, - ToGo, - Sign, - Rest, - MaxDelta, - Difference: Integer; - Constraints, - Widths: array of Integer; - BonusPixel: Boolean; - - //--------------- local functions ------------------------------------------- - - function IsResizable (Column: TColumnIndex): Boolean; - - begin - if BonusPixel then - Result := Widths[Column - RangeStartCol] < Constraints[Column - RangeStartCol] - else - Result := Widths[Column - RangeStartCol] > Constraints[Column - RangeStartCol]; - end; - - //--------------------------------------------------------------------------- - - procedure IncDelta(Column: TColumnIndex); - - begin - if BonusPixel then - Inc(MaxDelta, FColumns[Column].MaxWidth - Widths[Column - RangeStartCol]) - else - Inc(MaxDelta, Widths[Column - RangeStartCol] - Constraints[Column - RangeStartCol]); - end; - - //--------------------------------------------------------------------------- - - function ChangeWidth(Column: TColumnIndex; Delta: Integer): Integer; - + if tsIncrementalSearchPending in FStates then begin - if Delta > 0 then - Delta := Min(Delta, Constraints[Column - RangeStartCol] - Widths[Column - RangeStartCol]) - else - Delta := Max(Delta, Constraints[Column - RangeStartCol] - Widths[Column - RangeStartCol]); - - Inc(Widths[Column - RangeStartCol], Delta); - Dec(ToGo, Abs(Delta)); - Result := Abs(Delta); + HandleIncrementalSearch(Message.CharCode); + DoStateChange([], [tsIncrementalSearchPending]); end; - //--------------------------------------------------------------------------- - - function ReduceConstraints: Boolean; - - var - MaxWidth, - MaxReserveCol, - Column: TColumnIndex; - - begin - Result := True; - if not (hsScaling in FStates) or BonusPixel then - Exit; + inherited; +end; - MaxWidth := 0; - MaxReserveCol := NoColumn; - for Column := RangeStartCol to RangeEndCol do - if (Options * FColumns[Column].Options = Options) and - (FColumns[Column].Width > MaxWidth) then - begin - MaxWidth := Widths[Column - RangeStartCol]; - MaxReserveCol := Column; - end; +//---------------------------------------------------------------------------------------------------------------------- - if (MaxReserveCol <= NoColumn) or (Constraints[MaxReserveCol - RangeStartCol] <= 10) then - Result := False - else - Dec(Constraints[MaxReserveCol - RangeStartCol], - Constraints[MaxReserveCol - RangeStartCol] div 10); - end; +procedure TBaseVirtualTree.WMContextMenu(var Message: TWMContextMenu); - //----------- end local functions ------------------------------------------- +// This method is called when a popup menu is about to be displayed. +// We have to cancel some pending states here to avoid interferences. begin - Result := 0; - if ChangeBy <> 0 then - begin - // Do some initialization here - BonusPixel := ChangeBy > 0; - Sign := IfThen(BonusPixel, 1, -1); - Start := IfThen(BonusPixel, RangeStartCol, RangeEndCol); - ToGo := Abs(ChangeBy); - SetLength(Widths, RangeEndCol - RangeStartCol + 1); - SetLength(Constraints, RangeEndCol - RangeStartCol + 1); - for I := RangeStartCol to RangeEndCol do - begin - Widths[I - RangeStartCol] := FColumns[I].Width; - Constraints[I - RangeStartCol] := IfThen(BonusPixel, FColumns[I].MaxWidth, FColumns[I].MinWidth); - end; - - repeat - repeat - MaxDelta := 0; - ColCount := 0; - for I := RangeStartCol to RangeEndCol do - if (Options * FColumns[I].Options = Options) and IsResizable(I) then - begin - Inc(ColCount); - IncDelta(I); - end; - if MaxDelta < Abs(ChangeBy) then - if not ReduceConstraints then - Break; - until (MaxDelta >= Abs(ChangeBy)) or not (hsScaling in FStates); - - if ColCount = 0 then - Break; - - ToGo := Min(ToGo, MaxDelta); - Difference := ToGo div ColCount; - Rest := ToGo mod ColCount; - - if Difference > 0 then - for I := RangeStartCol to RangeEndCol do - if (Options * FColumns[I].Options = Options) and IsResizable(I) then - ChangeWidth(I, Difference * Sign); - - // Now distribute Rest. - I := Start; - while Rest > 0 do - begin - if (Options * FColumns[I].Options = Options) and IsResizable(I) then - if FColumns[I].BonusPixel <> BonusPixel then - begin - Dec(Rest, ChangeWidth(I, Sign)); - FColumns[I].BonusPixel := BonusPixel; - end; - Inc(I, Sign); - if (BonusPixel and (I > RangeEndCol)) or (not BonusPixel and (I < RangeStartCol)) then - begin - for I := RangeStartCol to RangeEndCol do - if Options * FColumns[I].Options = Options then - FColumns[I].BonusPixel := not FColumns[I].BonusPixel; - I := Start; - end; - end; - until ToGo <= 0; + DoStateChange([], [tsClearPending, tsEditPending, tsOLEDragPending, tsVCLDragPending]); - // Now set the computed widths. We also compute the result here. - Include(FStates, hsResizing); - for I := RangeStartCol to RangeEndCol do - if (Options * FColumns[I].Options = Options) then - begin - Inc(Result, Widths[I - RangeStartCol] - FColumns[I].Width); - FColumns[I].SetWidth(Widths[I - RangeStartCol]); - end; - Exclude(FStates, hsResizing); - end; + if not (tsPopupMenuShown in FStates) then + inherited; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.RestoreColumns; - -// Restores all columns to their width which they had before they have been auto fitted. - -var - I: TColumnIndex; +procedure TBaseVirtualTree.WMCopy(var Message: TWMCopy); begin - with FColumns do - for I := Count - 1 downto 0 do - if [coResizable, coVisible] * Items[FPositionToIndex[I]].Options = [coResizable, coVisible] then - Items[I].RestoreLastWidth; + CopyToClipboard; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTHeader.SaveToStream(const Stream: TStream); - -// Saves the complete state of the header into the provided stream. - -var - Dummy: Integer; - Tmp: AnsiString; +procedure TBaseVirtualTree.WMCut(var Message: TWMCut); begin - with Stream do - begin - // In previous version of VT was no header stream version defined. - // For feature enhancements it is necessary, however, to know which stream - // format we are trying to load. - // In order to distict from non-version streams an indicator is inserted. - Dummy := -1; - WriteBuffer(Dummy, SizeOf(Dummy)); - // Write current stream version number, nothing more is required at the time being. - Dummy := VTHeaderStreamVersion; - WriteBuffer(Dummy, SizeOf(Dummy)); - - // Save columns in case they depend on certain options (like auto size). - Columns.SaveToStream(Stream); - - Dummy := FAutoSizeIndex; - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := FBackgroundColor; - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := FHeight; - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Integer(FOptions); - WriteBuffer(Dummy, SizeOf(Dummy)); - // PopupMenu is neither saved nor restored - Dummy := Ord(FStyle); - WriteBuffer(Dummy, SizeOf(Dummy)); - // TFont has no own save routine so we do it manually - with Font do - begin - Dummy := Color; - WriteBuffer(Dummy, SizeOf(Dummy)); - - // Need only to write one: size or height, I decided to write height. - Dummy := Height; - WriteBuffer(Dummy, SizeOf(Dummy)); - Tmp := UTF8Encode(Name); - Dummy := Length(Tmp); - WriteBuffer(Dummy, SizeOf(Dummy)); - WriteBuffer(PAnsiChar(Tmp)^, Dummy); - Dummy := Ord(Pitch); - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Byte(Style); - WriteBuffer(Dummy, SizeOf(Dummy)); - end; - - // Data introduced by stream version 1. - Dummy := FMainColumn; - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := FSortColumn; - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Byte(FSortDirection); - WriteBuffer(Dummy, SizeOf(Dummy)); - - // Data introduced by stream version 5. - Dummy := Integer(ParentFont); - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Integer(FMaxHeight); - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Integer(FMinHeight); - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Integer(FDefaultHeight); - WriteBuffer(Dummy, SizeOf(Dummy)); - with FFixedAreaConstraints do - begin - Dummy := Integer(FMaxHeightPercent); - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Integer(FMaxWidthPercent); - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Integer(FMinHeightPercent); - WriteBuffer(Dummy, SizeOf(Dummy)); - Dummy := Integer(FMinWidthPercent); - WriteBuffer(Dummy, SizeOf(Dummy)); - end; - end; + CutToClipboard; end; -//----------------- TScrollBarOptions ---------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- -constructor TScrollBarOptions.Create(AOwner: TBaseVirtualTree); +procedure TBaseVirtualTree.WMEnable(var Message: TWMEnable); begin - inherited Create; - - FOwner := AOwner; - FAlwaysVisible := False; - FScrollBarStyle := sbmRegular; - FScrollBars := ssBoth; - FIncrementX := 20; - FIncrementY := 20; + inherited; + RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TScrollBarOptions.SetAlwaysVisible(Value: Boolean); +procedure TBaseVirtualTree.WMEraseBkgnd(var Message: TWMEraseBkgnd); begin - if FAlwaysVisible <> Value then - begin - FAlwaysVisible := Value; - if not (csLoading in FOwner.ComponentState) and FOwner.HandleAllocated then - FOwner.RecreateWnd; - end; + Message.Result := 1; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TScrollBarOptions.SetScrollBars(Value: TScrollStyle); +procedure TBaseVirtualTree.WMGetDlgCode(var Message: TWMGetDlgCode); begin - if FScrollBars <> Value then - begin - FScrollBars := Value; - if not (csLoading in FOwner.ComponentState) and FOwner.HandleAllocated then - FOwner.RecreateWnd; - end; + Message.Result := DLGC_WANTCHARS or DLGC_WANTARROWS; + if FWantTabs then + Message.Result := Message.Result or DLGC_WANTTAB; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TScrollBarOptions.SetScrollBarStyle(Value: TScrollBarStyle); +procedure TBaseVirtualTree.WMGetObject(var Message: TMessage); begin - if FScrollBarStyle <> Value then + if TVTAccessibilityFactory.GetAccessibilityFactory <> nil then begin - FScrollBarStyle := Value; + // Create the IAccessibles for the tree view and tree view items, if necessary. + if FAccessible = nil then + FAccessible := TVTAccessibilityFactory.GetAccessibilityFactory.CreateIAccessible(Self); + if FAccessibleItem = nil then + FAccessibleItem := TVTAccessibilityFactory.GetAccessibilityFactory.CreateIAccessible(Self); + if Cardinal(Message.LParam) = OBJID_CLIENT then + if Assigned(Accessible) then + Message.Result := LresultFromObject(IID_IAccessible, Message.WParam, FAccessible) + else + Message.Result := 0; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TScrollBarOptions.GetOwner: TPersistent; +procedure TBaseVirtualTree.WMHScroll(var Message: TWMHScroll); -begin - Result := FOwner; -end; + //--------------- local functions ------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- + function GetRealScrollPosition: Integer; -procedure TScrollBarOptions.Assign(Source: TPersistent); + var + SI: TScrollInfo; + Code: Integer; -begin - if Source is TScrollBarOptions then begin - AlwaysVisible := TScrollBarOptions(Source).AlwaysVisible; - HorizontalIncrement := TScrollBarOptions(Source).HorizontalIncrement; - ScrollBars := TScrollBarOptions(Source).ScrollBars; - ScrollBarStyle := TScrollBarOptions(Source).ScrollBarStyle; - VerticalIncrement := TScrollBarOptions(Source).VerticalIncrement; - end - else - inherited; -end; + SI.cbSize := SizeOf(TScrollInfo); + SI.fMask := SIF_TRACKPOS; + Code := SB_HORZ; + GetScrollInfo(Handle, Code, SI); + Result := SI.nTrackPos; + end; -//----------------- TVTColors ------------------------------------------------------------------------------------------ + //--------------- end local functions --------------------------------------- -constructor TVTColors.Create(AOwner: TBaseVirtualTree); var - CE : TVTColorEnum; -begin - FOwner := AOwner; - for CE := Low(TVTColorEnum) to High(TVTColorEnum) do - FColors[CE] := cDefaultColors[CE]; -end; - -//---------------------------------------------------------------------------------------------------------------------- + RTLFactor: Integer; -function TVTColors.GetBackgroundColor: TColor; begin -// XE2 VCL Style - if FOwner.VclStyleEnabled and (seClient in FOwner.StyleElements) then - Result := StyleServices.GetStyleColor(scTreeView) + if UseRightToLeftAlignment then + RTLFactor := -1 else - Result := FOwner.Color; -end; - -//---------------------------------------------------------------------------------------------------------------------- + RTLFactor := 1; -function TVTColors.GetColor(const Index: TVTColorEnum): TColor; -begin - // Only try to fetch the color via StyleServices if theses are enabled - // Return default/user defined color otherwise - if FOwner.VclStyleEnabled then - begin - // If the ElementDetails are not defined, fall back to the SystemColor - case Index of - cDisabledColor: - if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemDisabled), ecTextColor, Result) then - Result := StyleServices.GetSystemColor(FColors[Index]); - cTreeLineColor: - if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttBranch), ecBorderColor, Result) then - Result := StyleServices.GetSystemColor(FColors[Index]); - cBorderColor: - if (seBorder in FOwner.StyleElements) then - Result := StyleServices.GetSystemColor(FColors[Index]) - else - Result := FColors[Index]; - cHotColor: - if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemHot), ecTextColor, Result) then - Result := StyleServices.GetSystemColor(FColors[Index]); - cHeaderHotColor: - if not StyleServices.GetElementColor(StyleServices.GetElementDetails(thHeaderItemHot), ecTextColor, Result) then - Result := StyleServices.GetSystemColor(FColors[Index]); - cSelectionTextColor: - if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemSelected), ecTextColor, Result) then - Result := StyleServices.GetSystemColor(clHighlightText); - cUnfocusedColor: - if not StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemSelectedNotFocus), ecTextColor, Result) then - Result := StyleServices.GetSystemColor(FColors[Index]); + case Message.ScrollCode of + SB_BOTTOM: + SetOffsetX(-Integer(FRangeX)); + SB_ENDSCROLL: + begin + DoStateChange([], [tsThumbTracking]); + // avoiding to adjust the vertical scroll position while tracking makes it much smoother + // but we need to adjust the final position here then + UpdateHorizontalScrollBar(False); + end; + SB_LINELEFT: + SetOffsetX(FOffsetX + RTLFactor * FScrollBarOptions.HorizontalIncrement); + SB_LINERIGHT: + SetOffsetX(FOffsetX - RTLFactor * FScrollBarOptions.VerticalIncrement); + SB_PAGELEFT: + SetOffsetX(FOffsetX + RTLFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth)); + SB_PAGERIGHT: + SetOffsetX(FOffsetX - RTLFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth)); + SB_THUMBPOSITION, + SB_THUMBTRACK: + begin + DoStateChange([tsThumbTracking]); + if UseRightToLeftAlignment then + SetOffsetX(-Integer(FRangeX) + ClientWidth + GetRealScrollPosition) else - Result := StyleServices.GetSystemColor(FColors[Index]); + SetOffsetX(-GetRealScrollPosition); end; - end - else - Result := FColors[Index]; -end; - -//---------------------------------------------------------------------------------------------------------------------- + SB_TOP: + SetOffsetX(0); + end; -function TVTColors.GetHeaderFontColor: TColor; -begin -// XE2+ VCL Style - if FOwner.VclStyleEnabled and (seFont in FOwner.StyleElements) then - StyleServices.GetElementColor(StyleServices.GetElementDetails(thHeaderItemNormal), ecTextColor, Result) - else - Result := FOwner.Header.Font.Color; + Message.Result := 0; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTColors.GetNodeFontColor: TColor; -begin - if FOwner.VclStyleEnabled and (seFont in FOwner.StyleElements) then - StyleServices.GetElementColor(StyleServices.GetElementDetails(ttItemNormal), ecTextColor, Result) - else - Result := FOwner.Font.Color; -end; +procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); -//---------------------------------------------------------------------------------------------------------------------- +// Keyboard event handling for node focus, selection, node specific popup menus and help invokation. +// For a detailed description of every action done here read the help. -function TVTColors.GetSelectedNodeFontColor(Focused: boolean): TColor; -begin - if Focused then begin - if (tsUseExplorerTheme in FOwner.TreeStates) and not IsHighContrastEnabled then begin - Result := NodeFontColor - end - else - Result := SelectionTextColor - end// if Focused - else - Result := UnfocusedColor; -end; +var + Shift: TShiftState; + Node, Temp, + LastFocused: PVirtualNode; + Offset: Integer; + ClearPending, + NeedInvalidate, + DoRangeSelect, + PerformMultiSelect: Boolean; + Context: Integer; + ParentControl: TWinControl; + R: TRect; + NewCheckState: TCheckState; + TempColumn, + NewColumn: TColumnIndex; + ActAsGrid: Boolean; + ForceSelection: Boolean; + NewWidth, + NewHeight: Integer; + RTLFactor: Integer; -//---------------------------------------------------------------------------------------------------------------------- + // for tabulator handling + GetStartColumn: function(ConsiderAllowFocus: Boolean = False): TColumnIndex of object; + GetNextColumn: function(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex of object; + GetNextNode: TGetNextNodeProc; -procedure TVTColors.SetColor(const Index: TVTColorEnum; const Value: TColor); + KeyState: TKeyboardState; + Buffer: array[0..1] of AnsiChar; -begin - if FColors[Index] <> Value then + //--------------- local functions ------------------------------------------- + function getPreviousVisibleAutoSpanColumn(acolumn: TColumnIndex; anode: PVirtualNode): TColumnIndex; + var + PrevColumn: Integer; begin - FColors[Index] := Value; - if not (csLoading in FOwner.ComponentState) and FOwner.HandleAllocated then - begin - // Cause helper bitmap rebuild if the button color changed. - case Index of - cTreeLineColor: - begin - FOwner.PrepareBitmaps(True, False); - FOwner.Invalidate; - end; - cBorderColor: - RedrawWindow(FOwner.Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN) - else - FOwner.Invalidate; - end; + if (not assigned(anode)) + or (not FHeader.UseColumns) + or (not (toAutoSpanColumns in FOptions.AutoOptions)) + or (acolumn = FHeader.MainColumn) then + begin + //previously existing logic + result := FHeader.Columns.GetPreviousVisibleColumn(acolumn, True); + exit; + end; + //consider auto spanning + with FHeader.Columns do //standard loop for auto span + begin + PrevColumn := acolumn; + repeat + result := FHeader.Columns.GetPreviousVisibleColumn(PrevColumn); + if (result = InvalidColumn) or + (not ColumnIsEmpty(anode, result)) + //Any other BidiMode is not supported as already + //documented by original developer + or (Items[result].BidiMode <> bdLeftToRight) then + Break; + PrevColumn := result; + until False; end; end; -end; - -function TVTColors.StyleServices(AControl: TControl): TCustomStyleServices; -begin - if AControl = nil then - AControl := fOwner; - Result := VTStyleServices(AControl); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTColors.Assign(Source: TPersistent); -begin - if Source is TVTColors then + //--------------------------------------------------------------------------- + function getNextVisibleAutoSpanColumn(acolumn: TColumnIndex; anode: PVirtualNode): TColumnIndex; + var + NextColumn: Integer; begin - FColors := TVTColors(Source).FColors; - if FOwner.UpdateCount = 0 then - FOwner.Invalidate; - end - else - inherited; -end; - -//----------------- TClipboardFormats ---------------------------------------------------------------------------------- - -constructor TClipboardFormats.Create(AOwner: TBaseVirtualTree); - -begin - FOwner := AOwner; - Sorted := True; - Duplicates := dupIgnore; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TClipboardFormats.Add(const S: string): Integer; - -// Restrict additions to the clipbard formats to only those which are registered with the owner tree or one of its -// ancestors. - -var - Format: Word; - RegisteredClass: TVirtualTreeClass; - -begin - RegisteredClass := TClipboardFormatList.FindFormat(S, Format); - if Assigned(RegisteredClass) and FOwner.ClassType.InheritsFrom(RegisteredClass) then - Result := inherited Add(S) - else - Result := -1; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TClipboardFormats.Insert(Index: Integer; const S: string); - -// Restrict additions to the clipbard formats to only those which are registered with the owner tree or one of its -// ancestors. - -var - Format: Word; - RegisteredClass: TVirtualTreeClass; + if (not assigned(anode)) + or (not FHeader.UseColumns) + or (not (toAutoSpanColumns in FOptions.AutoOptions)) + or (acolumn = FHeader.MainColumn) then + begin + //previously existing logic + result := FHeader.Columns.GetNextVisibleColumn(acolumn, True); + exit; + end; + //consider auto spanning + with FHeader.Columns do //standard loop for auto span + begin + NextColumn := acolumn; + repeat + result := FHeader.Columns.GetNextVisibleColumn(NextColumn); + if (result = InvalidColumn) or + not ColumnIsEmpty(anode, result) + //Any other BidiMode is not supported as already + //documented by original developer + or (Items[result].BidiMode <> bdLeftToRight) then + Break; + NextColumn := result; + until False; + end; + end; -begin - RegisteredClass := TClipboardFormatList.FindFormat(S, Format); - if Assigned(RegisteredClass) and FOwner.ClassType.InheritsFrom(RegisteredClass) then - inherited Insert(Index, S); -end; + //--------------------------------------------------------------------------- + function isEmptyAutoSpanColumn(acolumn: TColumnIndex; anode: PVirtualNode): boolean; + var + previousColumn: Integer; + begin + result := false; + if (not assigned(anode)) + or (not FHeader.UseColumns) + or (not (toAutoSpanColumns in FOptions.AutoOptions)) + or (acolumn = FHeader.MainColumn) then + exit; + with FHeader.Columns do + begin + previousColumn := FHeader.Columns.GetPreviousVisibleColumn(acolumn); + if (previousColumn = InvalidColumn) //there is no previous column + //Any other BidiMode is not supported as already + //documented by original developer + or (Items[acolumn].BidiMode <> bdLeftToRight) then + exit; //returning false + result := ColumnIsEmpty(anode, acolumn); + end; + end; -//----------------- TBaseVirtualTree ----------------------------------------------------------------------------------- -constructor TBaseVirtualTree.Create(AOwner: TComponent); + //--------------- end local functions --------------------------------------- begin - InitializeGlobalStructures(); - + // Make form key preview work and let application modify the key if it wants this. inherited; - ControlStyle := ControlStyle - [csSetCaption] + [csCaptureMouse, csOpaque, csReplicatable, csDisplayDragImage, - csReflector]; - FTotalInternalDataSize := 0; - FNodeDataSize := -1; - Width := 200; - Height := 100; - TabStop := True; - ParentColor := False; - FDefaultNodeHeight := 18; - FDragOperations := [doCopy, doMove]; - FHotCursor := crDefault; - FScrollBarOptions := TScrollBarOptions.Create(Self); - FFocusedColumn := NoColumn; - FDragImageKind := diComplete; - FLastSelectionLevel := -1; - FSelectionBlendFactor := 128; - - FIndent := 18; - - FPlusBM := TBitmap.Create; - FHotPlusBM := TBitmap.Create; - FMinusBM := TBitmap.Create; - FHotMinusBM := TBitmap.Create; - FSelectedHotPlusBM := TBitmap.Create; - FSelectedHotMinusBM := TBitmap.Create; - - FBorderStyle := bsSingle; - FButtonStyle := bsRectangle; - FButtonFillMode := fmTreeColor; - - FHeader := GetHeaderClass.Create(Self); - - // we have an own double buffer handling - inherited DoubleBuffered := False; - - FCheckImageKind := ckSystemDefault; + with Message do + begin + Shift := KeyDataToShiftState(KeyData); + // Ask the application if the default key handling is desired. + if DoKeyAction(CharCode, Shift) then + begin + if (CharCode in [VK_HOME, VK_END, VK_PRIOR, VK_NEXT, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_BACK, VK_TAB]) and (RootNode.FirstChild <> nil) then + begin + PerformMultiSelect := (ssShift in Shift) and (toMultiSelect in FOptions.SelectionOptions) and not IsEditing; - FImageChangeLink := TChangeLink.Create; - FImageChangeLink.OnChange := ImageListChange; - FStateChangeLink := TChangeLink.Create; - FStateChangeLink.OnChange := ImageListChange; - FCustomCheckChangeLink := TChangeLink.Create; - FCustomCheckChangeLink.OnChange := ImageListChange; + // Flag to avoid range selection in case of single node advance. + DoRangeSelect := (CharCode in [VK_HOME, VK_END, VK_PRIOR, VK_NEXT]) and PerformMultiSelect and not IsEditing; - FAutoExpandDelay := 1000; - FAutoScrollDelay := 1000; - FAutoScrollInterval := 1; + NeedInvalidate := DoRangeSelect or (FSelectionCount > 1); + ActAsGrid := toGridExtensions in FOptions.MiscOptions; + ClearPending := (Shift = []) or (ActAsGrid and not (ssShift in Shift)) or + not (toMultiSelect in FOptions.SelectionOptions) or (CharCode in [VK_TAB, VK_BACK]); - FBackground := TPicture.Create; - // Similar to the Transparent property of TImage, - // this flag is Off by default. - FBackGroundImageTransparent := False; + // Keep old focused node for range selection. Use a default node if none was focused until now. + LastFocused := FFocusedNode; + if (LastFocused = nil) and (Shift <> []) then + LastFocused := GetFirstVisible(nil, True); - FDefaultPasteMode := amAddChildLast; - FMargin := 4; - FTextMargin := 4; - FImagesMargin := 2; - FLastDragEffect := DROPEFFECT_NONE; - FDragType := dtOLE; - FDragHeight := 350; - FDragWidth := 200; + // Set an initial range anchor if there is not yet one. + if FRangeAnchor = nil then + FRangeAnchor := GetFirstSelected; + if FRangeAnchor = nil then + FRangeAnchor := GetFirst; - FColors := TVTColors.Create(Self); - FEditDelay := 1000; + if UseRightToLeftAlignment then + RTLFactor := -1 + else + RTLFactor := 1; - FDragImage := TVTDragImage.Create(Self); - with FDragImage do - begin - Fade := True; - PreBlendBias := 0; - Transparency := 200; - end; + // Determine new focused node. + case CharCode of + VK_HOME, VK_END: + begin + if (CharCode = VK_END) xor UseRightToLeftAlignment then + begin + GetStartColumn := FHeader.Columns.GetLastVisibleColumn; + GetNextColumn := FHeader.Columns.GetPreviousVisibleColumn; + GetNextNode := GetPreviousVisible; + Node := GetLastVisible(nil, True); + end + else + begin + GetStartColumn := FHeader.Columns.GetFirstVisibleColumn; + GetNextColumn := FHeader.Columns.GetNextVisibleColumn; + GetNextNode := GetNextVisible; + Node := GetFirstVisible(nil, True); + end; - FAnimationDuration := 200; - FSearchTimeout := 1000; - FSearchStart := ssFocusedNode; - FNodeAlignment := naProportional; - FLineStyle := lsDotted; - FIncrementalSearch := isNone; - FClipboardFormats := TClipboardFormats.Create(Self); - FOptions := GetOptionsClass.Create(Self); - - Touch.InteractiveGestures := [igPan, igPressAndTap]; - Touch.InteractiveGestureOptions := [igoPanInertia, - igoPanSingleFingerHorizontal, igoPanSingleFingerVertical, - igoPanGutter, igoParentPassthrough]; - - if not (csDesigning in ComponentState) then //Don't create worker thread in IDE, there is no use for it - TWorkerThread.AddThreadReference(); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -destructor TBaseVirtualTree.Destroy; -var - WasValidating: Boolean; -begin - // Disconnect all remote MSAA connections - if Assigned(FAccessibleItem) then begin - CoDisconnectObject(FAccessibleItem, 0); - FAccessibleItem := nil; - end; - if Assigned(fAccessible) then begin - CoDisconnectObject(fAccessible, 0); - fAccessible := nil; - end; - - WasValidating := (tsValidating in FStates); - InterruptValidation(True); - if WasValidating then - begin - // Make sure we dequeue the two synchronized calls from ChangeTreeStatesAsync(), fixes mem leak and AV reported in issue #1001, but is more a workaround. - CheckSynchronize(); - CheckSynchronize(); - end;// if - FOptions.InternalSetMiscOptions(FOptions.MiscOptions - [toReadOnly]); //SetMiscOptions has side effects - // Make sure there is no reference remaining to the releasing tree. - TWorkerThread.ReleaseThreadReference(); - StopWheelPanning; - CancelEditNode; - - // Just in case it didn't happen already release the edit link. - FEditLink := nil; - FClipboardFormats.Free; - // Clear will also free the drag manager if it is still alive. - Clear; - FDragImage.Free; - FColors.Free; - FBackground.Free; - - if CheckImageKind = ckSystemDefault then - FCheckImages.Free; - FScrollBarOptions.Free; - - // The window handle must be destroyed before the header is freed because it is needed in WM_NCDESTROY. - if HandleAllocated then - DestroyWindowHandle; - - // Release FDottedBrush in case WM_NCDESTROY hasn't been triggered. - if FDottedBrush <> 0 then - DeleteObject(FDottedBrush); - FDottedBrush := 0; - - FHeader.Free; - FHeader := nil; // Do not use FreeAndNil() before checking issue #497 - FreeAndNil(FOptions); // WM_NCDESTROY accesses FOptions - - FreeMem(FRoot); - - FPlusBM.Free; - FHotPlusBM.Free; - FMinusBM.Free; - FHotMinusBM.Free; - FSelectedHotPlusBM.Free; - FSelectedHotMinusBM.Free; - - // Fixes issue #1002 - Images := nil; - StateImages := nil; - CustomCheckImages := nil; - - FImageChangeLink.Free; - FStateChangeLink.Free; - FCustomCheckChangeLink.Free; - - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.AdjustTotalCount(Node: PVirtualNode; Value: Integer; Relative: Boolean = False); - -// Sets a node's total count to the given value and recursively adjusts the parent's total count -// (actually, the adjustment is done iteratively to avoid function call overheads). - -var - Difference: Integer; - Run: PVirtualNode; - -begin - if Relative then - Difference := Value - else - Difference := Value - Integer(Node.TotalCount); - if Difference <> 0 then - begin - Run := Node; - // Root node has as parent the tree view. - while Assigned(Run) and (Run <> Pointer(Self)) do - begin - Inc(Integer(Run.TotalCount), Difference); - Run := Run.Parent; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.AdjustTotalHeight(Node: PVirtualNode; Value: Integer; Relative: Boolean = False); - -// Sets a node's total height to the given value and recursively adjusts the parent's total height. - -var - Difference: Integer; - Run: PVirtualNode; - -begin - if Relative then - Difference := Value - else - Difference := Value - Integer(Node.TotalHeight); - if Difference <> 0 then - begin - Run := Node; - repeat - Inc(Integer(Run.TotalHeight), Difference); - // If the node is not visible or the parent node is not expanded or we are already at the top - // then nothing more remains to do. - if not (vsVisible in Run.States) or (Run = FRoot) or - (Run.Parent = nil) or not (vsExpanded in Run.Parent.States) then - Break; - - Run := Run.Parent; - until False; - end; - - UpdateVerticalRange; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CalculateCacheEntryCount: Integer; - -// Calculates the size of the position cache. - -begin - if FVisibleCount > 1 then - Result := Ceil(FVisibleCount / CacheThreshold) - else - Result := 0; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CalculateVerticalAlignments(var PaintInfo: TVTPaintInfo; var VButtonAlign: Integer); - -// Calculates the vertical alignment of the given node and its associated expand/collapse button during -// a node paint cycle depending on the required node alignment style. - -begin - With PaintInfo do begin - // For absolute alignment the calculation is trivial. - case FNodeAlignment of - naFromTop: - VAlign := Node.Align; - naFromBottom: - VAlign := Integer(NodeHeight[Node]) - Node.Align; - else // naProportional - // Consider button and line alignment, but make sure neither the image nor the button (whichever is taller) - // go out of the entire node height (100% means bottom alignment to the node's bounds). - if (ImageInfo[iiNormal].Index >= 0) or (ImageInfo[iiState].Index >= 0) then - begin - if (ImageInfo[iiNormal].Index >= 0) then - VAlign := ImageInfo[iiNormal].Images.Height - else - VAlign := ImageInfo[iiState].Images.Height; - VAlign := MulDiv((Integer(NodeHeight[Node]) - VAlign), Node.Align, 100) + VAlign div 2; - end - else - if toShowButtons in FOptions.PaintOptions then - VAlign := MulDiv((Integer(NodeHeight[Node]) - FPlusBM.Height), Node.Align, 100) + FPlusBM.Height div 2 - else - VAlign := MulDiv(Integer(Node.NodeHeight), Node.Align, 100); - end; - - VButtonAlign := VAlign - FPlusBM.Height div 2 - (FPlusBM.Height and 1); - end;// With PaintInfo -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.ChangeCheckState(Node: PVirtualNode; Value: TCheckState): Boolean; - -// Sets the check state of the node according to the given value and the node's check type. -// If the check state must be propagated to the parent nodes and one of them refuses to change then -// nothing happens and False is returned, otherwise True. - -var - Run: PVirtualNode; - UncheckedCount, - MixedCheckCount, - CheckedCount: Cardinal; - -begin - Result := not (vsChecking in Node.States); - with Node^ do - if Result then - begin - Include(States, vsChecking); - try - if not (vsInitialized in States) then - InitNode(Node) - else if CheckState = Value then - begin - // Value didn't change and node was initialized, so nothing to do - Result := False; - Exit; - end;//if - - // Indicate that we are going to propagate check states up and down the hierarchy. - if FCheckPropagationCount = 0 then begin - // Do not enter tsCheckPropagation more than once - DoStateChange([tsCheckPropagation]); - BeginUpdate(); - end; - Inc(FCheckPropagationCount); - try - // Do actions which are associated with the given check state. - case CheckType of - // Check state change with additional consequences for check states of the children. - ctTriStateCheckBox: - begin - // Propagate state down to the children. - if toAutoTristateTracking in FOptions.AutoOptions then - case Value of - csUncheckedNormal: - if Node.ChildCount > 0 then - begin - Run := FirstChild; - CheckedCount := 0; - MixedCheckCount := 0; - UncheckedCount := 0; - while Assigned(Run) do - begin - if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then - begin - if not Self.GetCheckState(Run).IsDisabled() then - SetCheckState(Run, csUncheckedNormal); - // Check if the new child state was set successfully, otherwise we have to adjust the - // node's new check state accordingly. - case Self.GetCheckState(Run) of - csCheckedNormal, csCheckedDisabled: - Inc(CheckedCount); - csMixedNormal: - Inc(MixedCheckCount); - csUncheckedNormal, csUncheckedDisabled: - Inc(UncheckedCount); - end; - end; - Run := Run.NextSibling; - end; - - // If there is still a mixed state child node checkbox then this node must be mixed checked too. - if MixedCheckCount > 0 then - Value := csMixedNormal - else - // If nodes are normally checked child nodes then the unchecked count determines what - // to set for the node itself. - if CheckedCount > 0 then - if UncheckedCount > 0 then - Value := csMixedNormal - else - Value := csCheckedNormal; - end; - csCheckedNormal: - if Node.ChildCount > 0 then - begin - Run := FirstChild; - CheckedCount := 0; - MixedCheckCount := 0; - UncheckedCount := 0; - while Assigned(Run) do - begin - if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then - begin - if not Self.GetCheckState(Run).IsDisabled() then - SetCheckState(Run, csCheckedNormal); - // Check if the new child state was set successfully, otherwise we have to adjust the - // node's new check state accordingly. - case Self.GetCheckState(Run) of - csCheckedNormal: - Inc(CheckedCount); - csMixedNormal: - Inc(MixedCheckCount); - csUncheckedNormal: - Inc(UncheckedCount); - end; - end; - Run := Run.NextSibling; - end; - - // If there is still a mixed state child node checkbox then this node must be mixed checked too. - if MixedCheckCount > 0 then - Value := csMixedNormal - else - // If nodes are normally checked child nodes then the unchecked count determines what - // to set for the node itself. - if CheckedCount > 0 then - if UncheckedCount > 0 then - Value := csMixedNormal - else - Value := csCheckedNormal; - end; - end; - end; - // radio button check state change - ctRadioButton: - if Value = csCheckedNormal then - begin - Value := csCheckedNormal; - // Make sure only this node is checked. - Run := Parent.FirstChild; - while Assigned(Run) do - begin - if Run.CheckType = ctRadioButton then - Run.CheckState := csUncheckedNormal; - Run := Run.NextSibling; - end; - Invalidate; - end; - end; - - if Result then - CheckState := Value // Set new check state - else - CheckState := Self.GetCheckState(Node).GetUnpressed(); // Reset dynamic check state. - - // Propagate state up to the parent. - if not (vsInitialized in Parent.States) then - InitNode(Parent); - if (toAutoTristateTracking in FOptions.AutoOptions) and ([vsChecking, vsDisabled] * Parent.States = []) and - (CheckType in [ctCheckBox, ctTriStateCheckBox]) and (Parent <> FRoot) and - (Parent.CheckType = ctTriStateCheckBox) then - Result := CheckParentCheckState(Node, Value) - else - Result := True; - - InvalidateNode(Node); - finally - Dec(FCheckPropagationCount); // WL, 05.02.2004 - if FCheckPropagationCount = 0 then begin - // Allow state change event after all check operations finished - DoStateChange([], [tsCheckPropagation]); - EndUpdate(); - end; - end; - finally - Exclude(States, vsChecking); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRight: Integer; Alignment: TAlignment; - OldRect, NewRect: TRect): Boolean; - -// Helper routine used when a draw selection takes place. This version handles left-to-right directionality. -// In the process of adding or removing nodes the current selection is modified which requires to pack it after -// the function returns. Another side effect of this method is that a temporary list of nodes will be created -// (see also InternalCacheNode) which must be inserted into the current selection by the caller. - -var - Run, - NextNode: PVirtualNode; - TextRight, - TextLeft, - CurrentTop, - CurrentRight, - NextTop, - NextColumn, - NodeWidth, - Dummy: Integer; - MinY, MaxY: Integer; - LabelOffset: Integer; - IsInOldRect, - IsInNewRect: Boolean; - - // quick check variables for various parameters - DoSwitch, - AutoSpan: Boolean; - SimpleSelection: Boolean; - -begin - // A priori nothing changes. - Result := False; - - // Determine minimum and maximum vertical coordinates to limit iteration to. - MinY := Min(OldRect.Top, NewRect.Top); - MaxY := Max(OldRect.Bottom, NewRect.Bottom); - - // Initialize short hand variables to speed up tests below. - DoSwitch := ssCtrl in FDrawSelShiftState; - AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.AutoOptions); - SimpleSelection := toSimpleDrawSelection in FOptions.SelectionOptions; - // This is the node to start with. - Run := GetNodeAt(0, MinY, False, CurrentTop); - - if Assigned(Run) then - begin - LabelOffset := GetOffset(TVTElement.ofsLabel, Run); - - // ----- main loop - // Change selection depending on the node's rectangle being in the selection rectangle or not, but - // touch only those nodes which overlap either the old selection rectangle or the new one but not both. - repeat - // Collect offsets for check, normal and state images. - TextLeft := NodeLeft + LabelOffset; - NextTop := CurrentTop + Integer(NodeHeight[Run]); - - // Simple selection allows to draw the selection rectangle anywhere. No intersection with node captions is - // required. Only top and bottom bounds of the rectangle matter. - if SimpleSelection or (toFullRowSelect in FOptions.SelectionOptions) then - begin - IsInOldRect := (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom) and - ((FHeader.Columns.Count = 0) or (FHeader.Columns.TotalWidth > OldRect.Left)) and ((NodeLeft + LabelOffset) < OldRect.Right); - IsInNewRect := (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom) and - ((FHeader.Columns.Count = 0) or (FHeader.Columns.TotalWidth > NewRect.Left)) and ((NodeLeft + LabelOffset) < NewRect.Right); - end - else - begin - // The right column border might be extended if column spanning is enabled. - if AutoSpan then - begin - with FHeader.Columns do - begin - NextColumn := MainColumn; - repeat - Dummy := GetNextVisibleColumn(NextColumn); - if (Dummy = InvalidColumn) or not ColumnIsEmpty(Run, Dummy) or - (Items[Dummy].BidiMode <> bdLeftToRight) then - Break; - NextColumn := Dummy; - until False; - if NextColumn = MainColumn then - CurrentRight := NodeRight - else - GetColumnBounds(NextColumn, Dummy, CurrentRight); - end; - end - else - CurrentRight := NodeRight; - // Check if we need the node's width. This is the case when the node is not left aligned or the - // left border of the selection rectangle is to the right of the left node border. - if (TextLeft < OldRect.Left) or (TextLeft < NewRect.Left) or (Alignment <> taLeftJustify) then - begin - NodeWidth := DoGetNodeWidth(Run, MainColumn); - if NodeWidth >= (CurrentRight - TextLeft) then - TextRight := CurrentRight - else - case Alignment of - taLeftJustify: - TextRight := TextLeft + NodeWidth; - taCenter: - begin - TextLeft := (TextLeft + CurrentRight - NodeWidth) div 2; - TextRight := TextLeft + NodeWidth; - end; - else - // taRightJustify - TextRight := CurrentRight; - TextLeft := TextRight - NodeWidth; - end; - end - else - TextRight := CurrentRight; - - // Now determine whether we need to change the state. - IsInOldRect := (OldRect.Left <= TextRight) and (OldRect.Right >= TextLeft) and - (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom); - IsInNewRect := (NewRect.Left <= TextRight) and (NewRect.Right >= TextLeft) and - (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom); - end; - - if IsInOldRect xor IsInNewRect then - begin - Result := True; - if DoSwitch then - begin - if vsSelected in Run.States then - InternalRemoveFromSelection(Run) - else - InternalCacheNode(Run); - end - else - begin - if IsInNewRect then - InternalCacheNode(Run) - else - InternalRemoveFromSelection(Run); - end; - end; - CurrentTop := NextTop; - // Get next visible node and update left node position. - NextNode := GetNextVisibleNoInit(Run, True); - if NextNode = nil then - Break; - Inc(NodeLeft, CountLevelDifference(Run, NextNode) * Integer(FIndent)); - Run := NextNode; - until CurrentTop > MaxY; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CollectSelectedNodesRTL(MainColumn, NodeLeft, NodeRight: Integer; Alignment: TAlignment; - OldRect, NewRect: TRect): Boolean; - -// Helper routine used when a draw selection takes place. This version handles right-to-left directionality. -// See also comments in CollectSelectedNodesLTR. - -var - Run, - NextNode: PVirtualNode; - TextRight, - TextLeft, - CheckOffset, - CurrentTop, - CurrentLeft, - NextTop, - NextColumn, - NodeWidth, - Dummy: Integer; - MinY, MaxY: Integer; - IsInOldRect, - IsInNewRect: Boolean; - - // quick check variables for various parameters - WithCheck, - WithStateImages, - DoSwitch, - AutoSpan: Boolean; - SimpleSelection: Boolean; - -begin - // A priori nothing changes. - Result := False; - // Switch the alignment to the opposite value in RTL context. - ChangeBiDiModeAlignment(Alignment); - - // Determine minimum and maximum vertical coordinates to limit iteration to. - MinY := Min(OldRect.Top, NewRect.Top); - MaxY := Max(OldRect.Bottom, NewRect.Bottom); - - // Initialize short hand variables to speed up tests below. - DoSwitch := ssCtrl in FDrawSelShiftState; - WithCheck := (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages); - // Don't check the events here as descendant trees might have overriden the DoGetImageIndex method. - WithStateImages := Assigned(FStateImages) or Assigned(OnGetImageIndexEx); - if WithCheck then - CheckOffset := FCheckImages.Width + FImagesMargin - else - CheckOffset := 0; - AutoSpan := FHeader.UseColumns and (toAutoSpanColumns in FOptions.AutoOptions); - SimpleSelection := toSimpleDrawSelection in FOptions.SelectionOptions; - // This is the node to start with. - Run := GetNodeAt(0, MinY, False, CurrentTop); - - if Assigned(Run) then - begin - // The initial minimal left border is determined by the identation level of the node and is dynamically adjusted. - if toShowRoot in FOptions.PaintOptions then - Dec(NodeRight, Integer((GetNodeLevel(Run) + 1) * FIndent) + FMargin) - else - Dec(NodeRight, Integer(GetNodeLevel(Run) * FIndent) + FMargin); - - // ----- main loop - // Change selection depending on the node's rectangle being in the selection rectangle or not, but - // touch only those nodes which overlap either the old selection rectangle or the new one but not both. - repeat - // Collect offsets for check, normal and state images. - TextRight := NodeRight; - if WithCheck and (Run.CheckType <> ctNone) then - Dec(TextRight, CheckOffset); - Dec(TextRight, GetImageSize(Run, ikNormal, MainColumn).cx); - if WithStateImages then - Dec(TextRight, GetImageSize(Run, ikState, MainColumn).cx); - NextTop := CurrentTop + Integer(NodeHeight[Run]); - - // Simple selection allows to draw the selection rectangle anywhere. No intersection with node captions is - // required. Only top and bottom bounds of the rectangle matter. - if SimpleSelection then - begin - IsInOldRect := (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom); - IsInNewRect := (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom); - end - else - begin // The left column border might be extended if column spanning is enabled. - if AutoSpan then - begin - NextColumn := MainColumn; - repeat - Dummy := FHeader.Columns.GetPreviousVisibleColumn(NextColumn); - if (Dummy = InvalidColumn) or not ColumnIsEmpty(Run, Dummy) or - (FHeader.Columns[Dummy].BiDiMode = bdLeftToRight) then - Break; - NextColumn := Dummy; - until False; - if NextColumn = MainColumn then - CurrentLeft := NodeLeft - else - FHeader.Columns.GetColumnBounds(NextColumn, CurrentLeft, Dummy); - end - else - CurrentLeft := NodeLeft; - // Check if we need the node's width. This is the case when the node is not left aligned (in RTL context this // means actually right aligned) or the right border of the selection rectangle is to the left - // of the right node border. - if (TextRight > OldRect.Right) or (TextRight > NewRect.Right) or (Alignment <> taRightJustify) then - begin - NodeWidth := DoGetNodeWidth(Run, MainColumn); - if NodeWidth >= (TextRight - CurrentLeft) then - TextLeft := CurrentLeft - else - case Alignment of - taLeftJustify: - begin - TextLeft := CurrentLeft; - TextRight := TextLeft + NodeWidth; - end; - taCenter: - begin - TextLeft := (TextRight + CurrentLeft - NodeWidth) div 2; - TextRight := TextLeft + NodeWidth; - end; - else - // taRightJustify - TextLeft := TextRight - NodeWidth; - end; - end - else - TextLeft := CurrentLeft; - - // Now determine whether we need to change the state. - IsInOldRect := (OldRect.Right >= TextLeft) and (OldRect.Left <= TextRight) and - (NextTop > OldRect.Top) and (CurrentTop < OldRect.Bottom); - IsInNewRect := (NewRect.Right >= TextLeft) and (NewRect.Left <= TextRight) and - (NextTop > NewRect.Top) and (CurrentTop < NewRect.Bottom); - end; - - if IsInOldRect xor IsInNewRect then - begin - Result := True; - if DoSwitch then - begin - if vsSelected in Run.States then - InternalRemoveFromSelection(Run) - else - InternalCacheNode(Run); - end - else - begin - if IsInNewRect then - InternalCacheNode(Run) - else - InternalRemoveFromSelection(Run); - end; - end; - CurrentTop := NextTop; - // Get next visible node and update left node position. - NextNode := GetNextVisibleNoInit(Run, True); - if NextNode = nil then - Break; - Dec(NodeRight, CountLevelDifference(Run, NextNode) * Integer(FIndent)); - Run := NextNode; - until CurrentTop > MaxY; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.ClearNodeBackground(const PaintInfo: TVTPaintInfo; UseBackground, Floating: Boolean; - R: TRect); - -// Erases a node's background depending on what the application decides to do. -// UseBackground determines whether or not to use the background picture, while Floating indicates -// that R is given in coordinates of the small node bitmap or the superordinated target bitmap used in PaintTree. - -var - BackColor: TColor; - EraseAction: TItemEraseAction; - Offset: TPoint; - -begin - BackColor := FColors.BackGroundColor; - with PaintInfo do - begin - EraseAction := eaDefault; - - if Floating then - begin - Offset := Point(-FEffectiveOffsetX, R.Top); - OffsetRect(R, 0, -Offset.Y); - end - else - Offset := Point(0, 0); - - DoBeforeItemErase(Canvas, Node, R, BackColor, EraseAction); - - with Canvas do - begin - case EraseAction of - eaNone: - ; - eaColor: - begin - // User has given a new background color. - Brush.Color := BackColor; - FillRect(R); - end; - else // eaDefault - if UseBackground then - begin - if toStaticBackground in TreeOptions.PaintOptions then - StaticBackground(FBackground, Canvas, Offset, R, FColors.BackGroundColor) - else - TileBackground(FBackground, Canvas, Offset, R, FColors.BackGroundColor); - end - else - begin - if (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.SelectionOptions) and - (vsSelected in Node.States) and not (toUseBlendedSelection in FOptions.PaintOptions) and not - (tsUseExplorerTheme in FStates) then - begin - if toShowHorzGridLines in FOptions.PaintOptions then - begin - Brush.Color := BackColor; - FillRect(Rect(R.Left, R.Bottom - 1, R.Right, R.Bottom)); - Dec(R.Bottom); - end; - if Focused or (toPopupMode in FOptions.PaintOptions) then - begin - Brush.Color := FColors.FocusedSelectionColor; - Pen.Color := FColors.FocusedSelectionBorderColor; - end - else - begin - Brush.Color := FColors.UnfocusedSelectionColor; - Pen.Color := FColors.UnfocusedSelectionBorderColor; - end; - - with TWithSafeRect(R) do - RoundRect(Left, Top, Right, Bottom, FSelectionCurveRadius, FSelectionCurveRadius); - end - else - begin - Brush.Color := BackColor; - FillRect(R); - end; - end; - end; - DoAfterItemErase(Canvas, Node, R); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CompareNodePositions(Node1, Node2: PVirtualNode; ConsiderChildrenAbove: Boolean = False): Integer; - -// Tries hard and smart to quickly determine whether Node1's structural position is before Node2's position. -// If ConsiderChildrenAbove is True, the nodes will be compared with their visual order in mind. -// Returns 0 if Node1 = Node2, < 0 if Node1 is located before Node2 else > 0. - -var - Run1, - Run2: PVirtualNode; - Level1, - Level2: Cardinal; - -begin - Assert(Assigned(Node1) and Assigned(Node2), 'Nodes must never be nil.'); - - if Node1 = Node2 then - Result := 0 - else - begin - if HasAsParent(Node1, Node2) then - Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions), -1, 1) - else - if HasAsParent(Node2, Node1) then - Result := IfThen(ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions), 1, -1) - else - begin - // the given nodes are neither equal nor are they parents of each other, so go up to FRoot - // for each node and compare the child indices of the top level parents - // Note: neither Node1 nor Node2 can be FRoot at this point as this (a bit strange) circumstance would - // be caught by the previous code. - - // start lookup at the same level - Level1 := GetNodeLevel(Node1); - Level2 := GetNodeLevel(Node2); - Run1 := Node1; - while Level1 > Level2 do - begin - Run1 := Run1.Parent; - Dec(Level1); - end; - Run2 := Node2; - while Level2 > Level1 do - begin - Run2 := Run2.Parent; - Dec(Level2); - end; - - // now go up until we find a common parent node (loop will safely stop at FRoot if the nodes - // don't share a common parent) - while Run1.Parent <> Run2.Parent do - begin - Run1 := Run1.Parent; - Run2 := Run2.Parent; - end; - Result := Integer(Run1.Index) - Integer(Run2.Index); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.DrawLineImage(const PaintInfo: TVTPaintInfo; X, Y, H, VAlign: Integer; Style: TVTLineType; - Reverse: Boolean); - -// Draws (depending on Style) one of the 5 line types of the tree. -// If Reverse is True then a right-to-left column is being drawn, hence horizontal lines must be mirrored. -// X and Y describe the left upper corner of the line image rectangle, while H denotes its height (and width). - -var - HalfWidth, - TargetX: Integer; - -begin - HalfWidth := (FIndent div 2); - if Reverse then - TargetX := 0 - else - TargetX := Integer(FIndent) + ScaledPixels(FImagesMargin); - - with PaintInfo.Canvas do - begin - case Style of - ltBottomRight: - begin - DrawDottedVLine(PaintInfo, Y + VAlign, Y + H, X + HalfWidth); - DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign); - end; - ltTopDown: - DrawDottedVLine(PaintInfo, Y, Y + H, X + HalfWidth); - ltTopDownRight: - begin - DrawDottedVLine(PaintInfo, Y, Y + H, X + HalfWidth); - DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign); - end; - ltRight: - DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign); - ltTopRight: - begin - DrawDottedVLine(PaintInfo, Y, Y + VAlign, X + HalfWidth); - DrawDottedHLine(PaintInfo, X + HalfWidth, X + TargetX, Y + VAlign); - end; - ltLeft: // left can also mean right for RTL context - if Reverse then - DrawDottedVLine(PaintInfo, Y, Y + H, X + Integer(FIndent)) - else - DrawDottedVLine(PaintInfo, Y, Y + H, X); - ltLeftBottom: - if Reverse then - begin - DrawDottedVLine(PaintInfo, Y, Y + H, X + Integer(FIndent)); - DrawDottedHLine(PaintInfo, X, X + Integer(FIndent), Y + H); - end - else - begin - DrawDottedVLine(PaintInfo, Y, Y + H, X); - DrawDottedHLine(PaintInfo, X, X + Integer(FIndent), Y + H); - end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.FindInPositionCache(Node: PVirtualNode; var CurrentPos: Cardinal): PVirtualNode; - -// Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal -// to the position of the given node. - -var - L, H, I: Integer; - -begin - L := 0; - H := High(FPositionCache); - while L <= H do - begin - I := (L + H) shr 1; - if CompareNodePositions(FPositionCache[I].Node, Node) <= 0 then - L := I + 1 - else - H := I - 1; - end; - if L = 0 then // High(FPositionCache) = -1 - begin - Result := nil; - CurrentPos := 0; - end - else - begin - Result := FPositionCache[L - 1].Node; - CurrentPos := FPositionCache[L - 1].AbsoluteTop; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.FindInPositionCache(Position: Cardinal; var CurrentPos: Cardinal): PVirtualNode; - -// Looks through the position cache and returns the node whose top position is the largest one which is smaller or equal -// to the given vertical position. -// The returned node does not necessarily occupy the given position but is the nearest one to start -// iterating from to approach the real node for a given position. CurrentPos receives the actual position of the found -// node which is needed for further iteration. - -var - L, H, I: Integer; - -begin - L := 0; - H := High(FPositionCache); - while L <= H do - begin - I := (L + H) shr 1; - if FPositionCache[I].AbsoluteTop <= Position then - L := I + 1 - else - H := I - 1; - end; - if L = 0 then // High(FPositionCache) = -1 - begin - Result := nil; - CurrentPos := 0; - end - else - begin - Result := FPositionCache[L - 1].Node; - CurrentPos := FPositionCache[L - 1].AbsoluteTop; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.FixupTotalCount(Node: PVirtualNode); - -// Called after loading a subtree from stream. The child count in each node is already set but not -// their total count. - -var - Child: PVirtualNode; - -begin - // Initial total count is set to one on node creation. - Child := Node.FirstChild; - while Assigned(Child) do - begin - FixupTotalCount(Child); - Inc(Node.TotalCount, Child.TotalCount); - Child := Child.NextSibling; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.FixupTotalHeight(Node: PVirtualNode); - -// Called after loading a subtree from stream. The individual height of each node is set already, -// but their total height needs an adjustment depending on their visibility state. - -var - Child: PVirtualNode; - -begin - // Initial total height is set to the node height on load. - Child := Node.FirstChild; - - if vsExpanded in Node.States then - begin - while Assigned(Child) do - begin - FixupTotalHeight(Child); - if vsVisible in Child.States then - Inc(Node.TotalHeight, Child.TotalHeight); - Child := Child.NextSibling; - end; - end - else - begin - // The node is collapsed, so just update the total height of its child nodes. - while Assigned(Child) do - begin - FixupTotalHeight(Child); - Child := Child.NextSibling; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetBottomNode: PVirtualNode; - -begin - Result := GetNodeAt(0, ClientHeight - 1); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetCheckedCount: Integer; - -var - Node: PVirtualNode; - -begin - Result := 0; - Node := GetFirstChecked; - while Assigned(Node) do - begin - Inc(Result); - Node := GetNextChecked(Node); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetCheckState(Node: PVirtualNode): TCheckState; - -begin - if Assigned(FOnBeforeGetCheckState) then - FOnBeforeGetCheckState(Self, Node); - - Result := Node.CheckState; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetCheckType(Node: PVirtualNode): TCheckType; - -begin - Result := Node.CheckType; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetChildCount(Node: PVirtualNode): Cardinal; -begin - if (Node = nil) or (Node = FRoot) then - Exit(FRoot.ChildCount); - if not GetChildrenInitialized(Node) then - InitChildren(Node); - Exit(Node.ChildCount); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetChildrenInitialized(Node: PVirtualNode): Boolean; - -begin - Result := not (vsHasChildren in Node.States) or (Node.ChildCount > 0); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetCutCopyCount: Integer; - -var - Node: PVirtualNode; - -begin - Result := 0; - Node := GetFirstCutCopy; - while Assigned(Node) do - begin - Inc(Result); - Node := GetNextCutCopy(Node); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetDisabled(Node: PVirtualNode): Boolean; - -begin - Result := Assigned(Node) and (vsDisabled in Node.States); -end; - -//---------------------------------------------------------------------------------------------------------------------- -// whether the sync of checkbox with selection is allowed for this node -function TBaseVirtualTree.GetSyncCheckstateWithSelection(Node: PVirtualNode): Boolean; - -begin - Result := (toSyncCheckboxesWithSelection in FOptions.SelectionOptions) - and (toCheckSupport in FOptions.MiscOptions) - and Assigned(FCheckImages) - and (Node.CheckType = ctCheckBox); ; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetDragManager: IVTDragManager; - -// Returns the internal drag manager interface. If this does not yet exist then it is created here. - -begin - if FDragManager = nil then - begin - FDragManager := DoCreateDragManager; - if FDragManager = nil then - FDragManager := TVTDragManager.Create(Self); - end; - - Result := FDragManager; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetExpanded(Node: PVirtualNode): Boolean; - -begin - if Assigned(Node) then - Result := vsExpanded in Node.States - else - Result := False; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetFiltered(Node: PVirtualNode): Boolean; - -begin - Result := vsFiltered in Node.States; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetFullyVisible(Node: PVirtualNode): Boolean; - -// Determines whether the given node has the visibility flag set as well as all its parents are expanded. - -begin - Assert(Assigned(Node), 'Invalid parameter.'); - Result := vsVisible in Node.States; - if Result and (Node <> FRoot) then - Result := VisiblePath[Node]; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetHasChildren(Node: PVirtualNode): Boolean; - -begin - if Assigned(Node) then - Result := vsHasChildren in Node.States - else - Result := vsHasChildren in FRoot.States; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetMultiline(Node: PVirtualNode): Boolean; - -begin - Result := Assigned(Node) and (Node <> FRoot) and (vsMultiline in Node.States); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetNodeHeight(Node: PVirtualNode): Cardinal; - -begin - if Assigned(Node) and (Node <> FRoot) then - begin - if (toVariableNodeHeight in FOptions.MiscOptions) and not (vsDeleting in Node.States) then - begin - if not (vsInitialized in Node.States) then - InitNode(Node); - - // Ensure the node's height is determined. - MeasureItemHeight(Self.Canvas, Node); - end; - Result := Node.NodeHeight; - end - else - Result := 0; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetNodeParent(Node: PVirtualNode): PVirtualNode; - -begin - if Assigned(Node) and (Node.Parent <> FRoot) then - Result := Node.Parent - else - Result := nil; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetOffset(pElement: TVTElement; pNode: PVirtualNode): integer; -// Calculates the offset of the given element -var - lOffsets: TVTOffsets; -begin - GetOffsets(pNode, lOffsets, pElement); - Exit(lOffsets[pElement]); -end; - -procedure TBaseVirtualTree.GetOffsets(pNode: PVirtualNode; out pOffsets: TVTOffsets; pElement: TVTElement = TVTElement.ofsEndOfClientArea; pColumn: Integer = NoColumn); -// Calculates the offset up to the given element and supplies them in an array. -var - lNodeLevel: Integer; -begin - // If no specific column was given, assume the main column - if pColumn = -1 then - pColumn := Header.MainColumn; - - // Left Margin - pOffsets[TVTElement.ofsMargin] := FMargin; - if pElement = ofsMargin then - exit; - pOffsets[TVTElement.ofsCheckBox] := FMargin + fImagesMargin; - if (pColumn = Header.MainColumn) then - begin - if not (toFixedIndent in TreeOptions.PaintOptions) then begin - // plus Indent - lNodeLevel := GetNodeLevel(pNode); - if toShowRoot in FOptions.PaintOptions then - Inc(lNodeLevel); - end - else - lNodeLevel := 1; - Inc(pOffsets[TVTElement.ofsCheckBox], lNodeLevel * Integer(FIndent)); - // toggle buttons - pOffsets[TVTElement.ofsToggleButton] := pOffsets[TVTElement.ofsCheckBox] - fImagesMargin - ((Integer(FIndent) - FPlusBM.Width) div 2) + 1 - FPlusBM.Width; //Compare PaintTree() relative line 107 - end;//if MainColumn - - // The area in which the toggle buttons are painted must have exactly the size of one indent level - if pElement <= TVTElement.ofsCheckBox then - exit; - - // right of checkbox, left of state image - if (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages) and (pNode.CheckType <> ctNone) and (pColumn = Header.MainColumn) then - pOffsets[TVTElement.ofsStateImage] := pOffsets[TVTElement.ofsCheckBox] + FCheckImages.Width + fImagesMargin - else - pOffsets[TVTElement.ofsStateImage] := pOffsets[TVTElement.ofsCheckBox]; - if pElement = TVTElement.ofsStateImage then - exit; - // right of left image, left of normal image - pOffsets[TVTElement.ofsImage] := pOffsets[TVTElement.ofsStateImage] + GetImageSize(pNode, TVTImageKind.ikState, pColumn).cx; - if pElement = TVTElement.ofsImage then - exit; - // label - pOffsets[TVTElement.ofsLabel] := pOffsets[TVTElement.ofsImage] + GetImageSize(pNode, TVTImageKind.ikNormal, pColumn).cx; - pOffsets[TVTElement.ofsText] := pOffsets[TVTElement.ofsLabel] + FTextMargin; - Dec(pOffsets[TVTElement.ofsText]); //TODO: This should no longer be necessary once issue #369 is resolved. - if pElement <= TVTElement.ofsText then - exit; - - // End of text - pOffsets[TVTElement.ofsRightOfText] := pOffsets[TVTElement.ofsText] + DoGetNodeWidth(pNode, pColumn) + DoGetNodeExtraWidth(pNode, pColumn); - - // end of client area - pOffsets[TVTElement.ofsEndOfClientArea] := Max(FRangeX, ClientWidth) - FTextMargin; -end; - -function TBaseVirtualTree.GetOffsetXY: TPoint; - -begin - Result := Point(FOffsetX, FOffsetY); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetRangeX: Cardinal; -begin - Result := Max(0, FRangeX); -end; - -function TBaseVirtualTree.GetRootNodeCount: Cardinal; - -begin - Result := FRoot.ChildCount; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetSelected(Node: PVirtualNode): Boolean; - -begin - Result := Assigned(Node) and (vsSelected in Node.States); -end; - -function TBaseVirtualTree.GetSelectedData: TArray; -var - lItem: PVirtualNode; - i: Integer; -begin - SetLEngth(Result, Self.SelectedCount); - i := 0; - lItem := Self.GetFirstSelected; - while Assigned(lItem) do - begin - Result[i] := Self.GetNodeData(lItem); - lItem := Self.GetNextSelected(lItem); - Inc(i); - end; - SetLength(Result, i); // See issue #927, SelectedCount may not yet be updated. -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetTopNode: PVirtualNode; - -var - Dummy: Integer; - -begin - Result := GetNodeAt(0, 0, True, Dummy); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetTotalCount(): Cardinal; - -begin - Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be used in UI thread.'); // FUpdateCount is not thread-safe! So do not write it in non-UI threads. - Inc(FUpdateCount); - try - ValidateNode(FRoot, True); - finally - Dec(FUpdateCount); - end; - // The root node itself doesn't count as node. - Result := FRoot.TotalCount - 1; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetVclStyleEnabled: Boolean; -begin - Exit(FVclStyleEnabled); -end; - -function TBaseVirtualTree.GetVerticalAlignment(Node: PVirtualNode): Byte; - -begin - Result := Node.Align; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetVisible(Node: PVirtualNode): Boolean; - -// Determines if the given node is marked as being visible. - -begin - if Node = nil then - Node := FRoot; - - if not (vsInitialized in Node.States) then - InitNode(Node); - - Result := vsVisible in Node.States; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetVisiblePath(Node: PVirtualNode): Boolean; - -// Determines if all parents of the given node are expanded and have the visibility flag set. - -begin - Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameters.'); - - // FRoot is always expanded - repeat - Node := Node.Parent; - until (Node = FRoot) or not (vsExpanded in Node.States) or not (vsVisible in Node.States); - - Result := Node = FRoot; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.HandleClickSelection(LastFocused, NewNode: PVirtualNode; Shift: TShiftState; - DragPending: Boolean); - -// Handles multi-selection with mouse click. - -begin - // Ctrl key down - if ssCtrl in Shift then - begin - if ssShift in Shift then - begin - SelectNodes(FRangeAnchor, NewNode, True); - end - else - begin - if not (toSiblingSelectConstraint in FOptions.SelectionOptions) then - FRangeAnchor := NewNode; - // Delay selection change if a drag operation is pending. - // Otherwise switch selection state here. - if DragPending then - DoStateChange([tsToggleFocusedSelection]) - else - if vsSelected in NewNode.States then - RemoveFromSelection(NewNode) - else - AddToSelection(NewNode, True); - end; - end - else - // Shift key down - if ssShift in Shift then - begin - if FRangeAnchor = nil then - FRangeAnchor := FRoot.FirstChild; - - // select node range - if Assigned(FRangeAnchor) then - begin - SelectNodes(FRangeAnchor, NewNode, False); - Invalidate; - end; - end - else - begin - // any other case - if not (vsSelected in NewNode.States) then - AddToSelection(NewNode, True); - // assign new reference item - FRangeAnchor := NewNode; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.HandleDrawSelection(X, Y: Integer): Boolean; - -// Handles multi-selection with a focus rectangle. -// Result is True if something changed in selection. - -var - OldRect, - NewRect: TRect; - MainColumn: TColumnIndex; - MaxValue: Integer; - - // limits of a node and its text - NodeLeft, - NodeRight: Integer; - - // alignment and directionality - CurrentBidiMode: TBidiMode; - CurrentAlignment: TAlignment; - -begin - Result := False; - - // Selection changes are only done if the user drew a selection rectangle large - // enough to exceed the threshold. - if (FRoot.TotalCount > 1) and (tsDrawSelecting in FStates) then - begin - // Effective handling of node selection is done by using two rectangles stored in FSelectRec. - OldRect := OrderRect(FLastSelRect); - NewRect := OrderRect(FNewSelRect); - ClearTempCache; - - MainColumn := FHeader.MainColumn; - - // Alignment and bidi mode determine where the node text is located within a node. - if MainColumn <= NoColumn then - begin - CurrentBidiMode := BidiMode; - CurrentAlignment := Alignment; - end - else - begin - CurrentBidiMode := FHeader.Columns[MainColumn].BidiMode; - CurrentAlignment := FHeader.Columns[MainColumn].Alignment; - end; - - // Determine initial left border of first node (take column reordering into account). - if FHeader.UseColumns then - begin - // The mouse coordinates don't include any horizontal scrolling hence take this also - // out from the returned column position. - NodeLeft := FHeader.Columns[MainColumn].Left + FEffectiveOffsetX; - NodeRight := NodeLeft + FHeader.Columns[MainColumn].Width; - end - else - begin - NodeLeft := 0 + FEffectiveOffsetX; - NodeRight := NodeLeft + ClientWidth; - end; - if CurrentBidiMode = bdLeftToRight then - Result := CollectSelectedNodesLTR(MainColumn, NodeLeft, NodeRight, CurrentAlignment, OldRect, NewRect) - else - Result := CollectSelectedNodesRTL(MainColumn, NodeLeft, NodeRight, CurrentAlignment, OldRect, NewRect); - end; - - if Result then - begin - // Do some housekeeping if there was a change. - MaxValue := PackArray(FSelection, FSelectionCount); - if MaxValue > -1 then - begin - FSelectionCount := MaxValue; - SetLength(FSelection, FSelectionCount); - end; - if FTempNodeCount > 0 then - begin - if tsClearOnNewSelection in fStates then - begin - DoStateChange([], [tsClearOnNewSelection]); - ClearSelection(False); - end; - - AddToSelection(FTempNodeCache, FTempNodeCount); - ClearTempCache; - end; - - Change(nil); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.HasVisibleNextSibling(Node: PVirtualNode): Boolean; - -// Helper method to determine if the given node has a visible next sibling. This is needed to -// draw correct tree lines. - -begin - // Check if there is a sibling at all. - Result := Assigned(Node.NextSibling); - - if Result then - begin - repeat - Node := Node.NextSibling; - Result := IsEffectivelyVisible[Node]; - until Result or (Node.NextSibling = nil); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.HasVisiblePreviousSibling(Node: PVirtualNode): Boolean; - -// Helper method to determine if the given node has a visible previous sibling. This is needed to -// draw correct tree lines. - -begin - // Check if there is a sibling at all. - Result := Assigned(Node.PrevSibling); - - if Result then - begin - repeat - Node := Node.PrevSibling; - Result := IsEffectivelyVisible[Node]; - until Result or (Node.PrevSibling = nil); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.ImageListChange(Sender: TObject); - -begin - if not (csDestroying in ComponentState) then - Invalidate; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.InitializeFirstColumnValues(var PaintInfo: TVTPaintInfo); - -// Determines initial index, position and cell size of the first visible column. - -begin - PaintInfo.Column := FHeader.Columns.GetFirstVisibleColumn; - with FHeader.Columns, PaintInfo do - begin - if Column > NoColumn then - begin - CellRect.Right := CellRect.Left + Items[Column].Width; - Position := Items[Column].Position; - end - else - Position := 0; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.InitRecursive(Node: PVirtualNode; Levels: Cardinal = MaxInt; pVisibleOnly: Boolean = True); - -// Initializes a node and optionally its children up to a certain level. -// The sepcified number of levels are latrive to the givne Node. - -var - Run: PVirtualNode; -begin - if not Assigned(Node) then - Node := FRoot; - - if (Node <> FRoot) and not (vsInitialized in Node.States) then - InitNode(Node); - if (Levels = 0) or (pVisibleOnly and not (vsExpanded in Node.States)) then - exit; - Run := Node.FirstChild; - - while Assigned(Run) do - begin - InitRecursive(Run, Levels - 1, pVisibleOnly); - Run := Run.NextSibling; - end; -end; - -procedure TBaseVirtualTree.InitRootNode(OldSize: Cardinal = 0); - -// Reinitializes the root node. - -var - NewSize: Cardinal; - -begin - NewSize := TreeNodeSize + FTotalInternalDataSize; - if FRoot = nil then - FRoot := AllocMem(NewSize) - else - begin - ReallocMem(FRoot, NewSize); - ZeroMemory(PByte(FRoot) + OldSize, NewSize - OldSize); - end; - - with FRoot^ do - begin - // Indication that this node is the root node. - PrevSibling := FRoot; - NextSibling := FRoot; - Parent := Pointer(Self); - States := [vsInitialized, vsExpanded, vsHasChildren, vsVisible]; - TotalHeight := FDefaultNodeHeight; - TotalCount := 1; - NodeHeight := FDefaultNodeHeight; - Align := 50; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.InterruptValidation(pWaitForValidationTermination: Boolean = True); - -var - WasValidating: Boolean; -begin - DoStateChange([tsStopValidation], [tsUseCache]); - - // Check the worker thread existance. It might already be gone (usually on destruction of the last tree). - WasValidating := (tsValidating in FStates); - TWorkerThread.RemoveTree(Self, pWaitForValidationTermination); - if WasValidating then - InvalidateCache(); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.IsFirstVisibleChild(Parent, Node: PVirtualNode): Boolean; - -// Helper method to check if Node is the same as the first visible child of Parent. - -var - Run: PVirtualNode; - -begin - // Find first visible child. - Run := Parent.FirstChild; - while Assigned(Run) and not IsEffectivelyVisible[Run] do - Run := Run.NextSibling; - - Result := Assigned(Run) and (Run = Node); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.IsLastVisibleChild(Parent, Node: PVirtualNode): Boolean; - -// Helper method to check if Node is the same as the last visible child of Parent. - -var - Run: PVirtualNode; - -begin - // Find last visible child. - Run := Parent.LastChild; - while Assigned(Run) and not IsEffectivelyVisible[Run] do - Run := Run.PrevSibling; - - Result := Assigned(Run) and (Run = Node); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.MakeNewNode: PVirtualNode; - -var - Size: Cardinal; - -begin - Size := TreeNodeSize; - if not (csDesigning in ComponentState) then - begin // Make sure FNodeDataSize is valid. - if FNodeDataSize <= 0 then - ValidateNodeDataSize(FNodeDataSize); - - // Take record alignment into account. - Inc(Size, FNodeDataSize); - end//not csDesigning - else - Inc(Size, SizeOf(Pointer)); // Fixes #702 - - - Result := AllocMem(Size + FTotalInternalDataSize); - - // Fill in some default values. - with Result^ do - begin - TotalCount := 1; - TotalHeight := FDefaultNodeHeight; - NodeHeight := FDefaultNodeHeight; - States := [vsVisible]; - Align := 50; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.PackArray({*}const TheArray: TNodeArray; Count: Integer): Integer; assembler; -// *This is an optimization to get as near as possible with the PUREPASCAL code without the -// compiler generating a _DynArrayAddRef call. We still modify the array's content via pointers. - -// Removes all entries from the selection array which are no longer in use. The selection array must be sorted for this -// algo to work. Values which must be removed are marked with bit 0 (LSB) set. This little trick works because memory -// is always allocated DWORD aligned. Since the selection array must be sorted while determining the entries to be -// removed it is much more efficient to increment the entry in question instead of setting it to nil (which would break -// the ordered appearance of the list). -// -// On enter EAX contains self reference, EDX the address to TheArray and ECX Count -// The returned value is the number of remaining entries in the array, so the caller can reallocate (shorten) -// the selection array if needed or -1 if nothing needs to be changed. - -{$ifdef CPUX64} -var - Source, Dest: ^PVirtualNode; - ConstOne: NativeInt; -begin - Source := Pointer(TheArray); - ConstOne := 1; - Result := 0; - // Do the fastest scan possible to find the first entry - while (Count <> 0) and {not Odd(NativeInt(Source^))} (NativeInt(Source^) and ConstOne = 0) do - begin - Inc(Result); - Inc(Source); - Dec(Count); - end; - - if Count <> 0 then - begin - Dest := Source; - repeat - // Skip odd entries - if {not Odd(NativeInt(Source^))} NativeInt(Source^) and ConstOne = 0 then - begin - Dest^ := Source^; - Inc(Result); - Inc(Dest); - end; - Inc(Source); // Point to the next entry - Dec(Count); - until Count = 0; - end; -end; -{$else} -asm - PUSH EBX - PUSH EDI - PUSH ESI - MOV ESI, EDX - MOV EDX, -1 - JCXZ @@Finish // Empty list? - INC EDX // init remaining entries counter - MOV EDI, ESI // source and destination point to the list memory - MOV EBX, 1 // use a register instead of immediate operant to check against -@@PreScan: - TEST [ESI], EBX // do the fastest scan possible to find the first entry - // which must be removed - JNZ @@DoMainLoop - INC EDX - ADD ESI, 4 - DEC ECX - JNZ @@PreScan - JMP @@Finish - -@@DoMainLoop: - MOV EDI, ESI -@@MainLoop: - TEST [ESI], EBX // odd entry? - JNE @@Skip // yes, so skip this one - MOVSD // else move the entry to new location - INC EDX // count the moved entries - DEC ECX - JNZ @@MainLoop // do it until all entries are processed - JMP @@Finish - -@@Skip: - ADD ESI, 4 // point to the next entry - DEC ECX - JNZ @@MainLoop // do it until all entries are processed -@@Finish: - MOV EAX, EDX // prepare return value - POP ESI - POP EDI - POP EBX -end; -{$endif CPUX64} - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.PrepareBitmaps(NeedButtons, NeedLines: Boolean); - -// initializes the contents of the internal bitmaps - -const - LineBitsDotted: array [0..8] of Word = ($55, $AA, $55, $AA, $55, $AA, $55, $AA, $55); - LineBitsSolid: array [0..7] of Word = (0, 0, 0, 0, 0, 0, 0, 0); - -var - PatternBitmap: HBITMAP; - Bits: Pointer; - Size: TSize; - Theme: HTHEME; - R: TRect; - - //--------------- local function -------------------------------------------- - - procedure FillBitmap (ABitmap: TBitmap); - begin - with ABitmap, Canvas do - begin - SetSize(Size.cx, Size.cy); - - if IsWinVistaOrAbove and (tsUseThemes in FStates) and (toUseExplorerTheme in FOptions.PaintOptions) or VclStyleEnabled then - begin - if (FHeader.MainColumn > NoColumn) then - Brush.Color := FHeader.Columns[FHeader.MainColumn].GetEffectiveColor - else - Brush.Color := FColors.BackGroundColor; - end - else - Brush.Color := clFuchsia; - - Transparent := True; - TransparentColor := Brush.Color; - - FillRect(Rect(0, 0, Width, Height)); - end; - end; - - //--------------- end local function ---------------------------------------- - -const - cMinExpandoHeight = 11; // pixels @100% -begin - if VclStyleEnabled and (seClient in StyleElements) then - begin - if NeedButtons then begin - if StyleServices.GetElementSize(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), TElementSize.esActual, Size) then - begin - Size.cx := Max(Size.cx, cMinExpandoHeight); // Use min size of 11, see issue #1035 / RSP-33715 - Size.cx := ScaledPixels(Size.cx) // I would have expected that the returned value is dpi-sclaed, but this is not the case in RAD Studio 10.4.1. See issue #984 - end - else - Size.cx := ScaledPixels(cMinExpandoHeight); - Size.cy := Size.cx; - FillBitmap(FPlusBM); - FillBitmap(FHotPlusBM); - FillBitmap(FSelectedHotPlusBM); - FillBitmap(FMinusBM); - FillBitmap(FHotMinusBM); - FillBitmap(FSelectedHotMinusBM); - R := Rect(0,0,Size. cx,Size.cy); - // tcbCategoryGlyphClosed, tcbCategoryGlyphOpened from CategoryButtons - StyleServices.DrawElement(FPlusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphClosed), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); - StyleServices.DrawElement(FMinusBM.Canvas.Handle, StyleServices.GetElementDetails(tcbCategoryGlyphOpened), R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); - FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); - FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); - FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); - FSelectedHotPlusBM.Canvas.Draw(0, 0, FPlusBM); - if Assigned(FOnPrepareButtonImages) then - FOnPrepareButtonImages(Self, FPlusBM, FHotPlusBM, FSelectedHotPlusBM, FMinusBM, FHotMinusBM, FSelectedHotMinusBM, size); - end;//if NeedButtons - end// if VclStyleEnabled - else - begin // No stlye - Size.cx := ScaledPixels(9); - Size.cy := ScaledPixels(9); - if tsUseThemes in FStates then - begin - R := Rect(0, 0, 100, 100); - {$if CompilerVersion >= 33} - if TOSVersion.Check(10) and (TOSVersion.Build >= 15063) then - Theme := OpenThemeDataForDPI(Handle, 'TREEVIEW', Self.FCurrentPPI) - else - Theme := OpenThemeData(Handle, 'TREEVIEW'); - {$else} - Theme := OpenThemeData(Handle, 'TREEVIEW'); - {$ifend} - GetThemePartSize(Theme, FPlusBM.Canvas.Handle, TVP_GLYPH, GLPS_OPENED, @R, TS_TRUE, Size); - end - else - Theme := 0; - - if NeedButtons then - begin - //VCL Themes do not really have ability to provide tree plus/minus images when not using the - //windows theme. The bitmap style designer doesn't have any elements for for them, and you - //cannot name any elements you add, which makes it useless. - //To mitigate this, Hook up the OnPrepareButtonImages and draw them yourself. - if Assigned(FOnPrepareButtonImages) then - begin - FillBitmap(FPlusBM); - FillBitmap(FHotPlusBM); - FillBitmap(FSelectedHotPlusBM); - FillBitmap(FMinusBM); - FillBitmap(FHotMinusBM); - FillBitmap(FSelectedHotMinusBM); - FOnPrepareButtonImages(Self, FPlusBM, FHotPlusBM, FSelectedHotPlusBM, FMinusBM, FHotMinusBM, FSelectedHotMinusBM, size); - end - else - begin - with FMinusBM, Canvas do - begin - // box is always of odd size - FillBitmap(FMinusBM); - FillBitmap(FHotMinusBM); - FillBitmap(FSelectedHotMinusBM); - // Weil die selbstgezeichneten Bitmaps sehen im Vcl Style scheiße aus - // Because the self-drawn bitmaps view Vcl Style shit - if Theme = 0 then - begin - if not(tsUseExplorerTheme in FStates) then - begin - if FButtonStyle = bsTriangle then - begin - Brush.Color := clBlack; - Pen.Color := clBlack; - Polygon([Point(0, 2), Point(8, 2), Point(4, 6)]); - end - else - begin - // Button style is rectangular. Now ButtonFillMode determines how to fill the interior. - if FButtonFillMode in [fmTreeColor, fmWindowColor, fmTransparent] then - begin - case FButtonFillMode of - fmTreeColor: - Brush.Color := FColors.BackGroundColor; - fmWindowColor: - Brush.Color := clWindow; - end; - Pen.Color := FColors.TreeLineColor; - Rectangle(0, 0, Width, Height); - Pen.Color := FColors.NodeFontColor; - MoveTo(2, Width div 2); - LineTo(Width - 2, Width div 2); - end - else - FMinusBM.Handle := LoadBitmap(HInstance, 'VT_XPBUTTONMINUS'); - end; - FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); - FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); - end; - end; - end; - with FPlusBM, Canvas do - begin - FillBitmap(FPlusBM); - FillBitmap(FHotPlusBM); - FillBitmap(FSelectedHotPlusBM); - if Theme = 0 then - begin - if not(tsUseExplorerTheme in FStates) then - begin - if FButtonStyle = bsTriangle then - begin - Brush.Color := clBlack; - Pen.Color := clBlack; - Polygon([Point(2, 0), Point(6, 4), Point(2, 8)]); - end - else - begin - // Button style is rectangular. Now ButtonFillMode determines how to fill the interior. - if FButtonFillMode in [fmTreeColor, fmWindowColor, fmTransparent] then - begin - case FButtonFillMode of - fmTreeColor: - Brush.Color := FColors.BackGroundColor; - fmWindowColor: - Brush.Color := clWindow; - end; - Pen.Color := FColors.TreeLineColor; - Rectangle(0, 0, Width, Height); - Pen.Color := FColors.NodeFontColor; - MoveTo(2, Width div 2); - LineTo(Width - 2, Width div 2); - MoveTo(Width div 2, 2); - LineTo(Width div 2, Width - 2); - end - else - FPlusBM.Handle := LoadBitmap(HInstance, 'VT_XPBUTTONPLUS'); - end; - FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); - FSelectedHotPlusBM.Canvas.Draw(0, 0, FPlusBM); - end; - end; - end; - - - // Overwrite glyph images if theme is active. - if (tsUseThemes in FStates) and (Theme <> 0) then - begin - R := Rect(0, 0, Size.cx, Size.cy); - DrawThemeBackground(Theme, FPlusBM.Canvas.Handle, TVP_GLYPH, GLPS_CLOSED, R, nil); - DrawThemeBackground(Theme, FMinusBM.Canvas.Handle, TVP_GLYPH, GLPS_OPENED, R, nil); - if tsUseExplorerTheme in FStates then - begin - DrawThemeBackground(Theme, FHotPlusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_CLOSED, R, nil); - DrawThemeBackground(Theme, FSelectedHotPlusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_CLOSED, R, nil); - DrawThemeBackground(Theme, FHotMinusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_OPENED, R, nil); - DrawThemeBackground(Theme, FSelectedHotMinusBM.Canvas.Handle, TVP_HOTGLYPH, GLPS_OPENED, R, nil); - end - else - begin - FHotPlusBM.Canvas.Draw(0, 0, FPlusBM); - FSelectedHotPlusBM.Canvas.Draw(0, 0, FPlusBM); - FHotMinusBM.Canvas.Draw(0, 0, FMinusBM); - FSelectedHotMinusBM.Canvas.Draw(0, 0, FMinusBM); - end; - end; - end; - if tsUseThemes in FStates then - CloseThemeData(Theme); - end;// if NeedButtons - end;// else - - if NeedLines then - begin - if FDottedBrush <> 0 then - DeleteObject(FDottedBrush); - case FLineStyle of - lsDotted: - Bits := @LineBitsDotted; - lsSolid: - Bits := @LineBitsSolid; - else // lsCustomStyle - Bits := @LineBitsDotted; - DoGetLineStyle(Bits); - end; - PatternBitmap := CreateBitmap(8, 8, 1, 1, Bits); - FDottedBrush := CreatePatternBrush(PatternBitmap); - DeleteObject(PatternBitmap); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetAlignment(const Value: TAlignment); - -begin - if FAlignment <> Value then - begin - FAlignment := Value; - if not (csLoading in ComponentState) then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetAnimationDuration(const Value: Cardinal); - -begin - FAnimationDuration := Value; - if FAnimationDuration = 0 then - FOptions.AnimationOptions := FOptions.AnimationOptions - [toAnimatedToggle] - else - FOptions.AnimationOptions := FOptions.AnimationOptions + [toAnimatedToggle] -end; - -//---------------------------------------------------------------------------------------------------------------------- -{ New, Support for transparent background: - * Image types: BMP, PNG, GIF, ICO, EMF, TIFF and WMF are automatically identified to support transparent background - * Also detects certain third party image classes registered for PNG, GIF and other image types so that the - transparency related code is used for them. See the code below. - * If some other third party image class is registered that is not detected, - set the flag BackgroundTransparentExternalType explicitly in order to properly do - transparent painting. -} -procedure TBaseVirtualTree.SetBackground(const Value: TPicture); - -begin - FBackground.Assign(Value); - Invalidate; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetBackGroundImageTransparent(const Value: Boolean); - -begin - if Value <> FBackGroundImageTransparent then - begin - FBackGroundImageTransparent := Value; - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetBackgroundOffset(const Index, Value: Integer); - -begin - case Index of - 0: - if FBackgroundOffsetX <> Value then - begin - FBackgroundOffsetX := Value; - Invalidate; - end; - 1: - if FBackgroundOffsetY <> Value then - begin - FBackgroundOffsetY := Value; - Invalidate; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetBorderStyle(Value: TBorderStyle); - -begin - if FBorderStyle <> Value then - begin - FBorderStyle := Value; - RecreateWnd; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetBottomNode(Node: PVirtualNode); - -var - Run: PVirtualNode; - R: TRect; - -begin - if Assigned(Node) then - begin - // make sure all parents of the node are expanded - Run := Node.Parent; - while Run <> FRoot do - begin - if not (vsExpanded in Run.States) then - ToggleNode(Run); - Run := Run.Parent; - end; - R := GetDisplayRect(Node, FHeader.MainColumn, True); - DoSetOffsetXY(Point(FOffsetX, FOffsetY + ClientHeight - R.Top - Integer(NodeHeight[Node])), - [suoRepaintScrollBars, suoUpdateNCArea]); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetBottomSpace(const Value: Cardinal); - -begin - if FBottomSpace <> Value then - begin - FBottomSpace := Value; - UpdateVerticalScrollBar(True); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetButtonFillMode(const Value: TVTButtonFillMode); - -begin - if FButtonFillMode <> Value then - begin - FButtonFillMode := Value; - if not (csLoading in ComponentState) then - begin - PrepareBitmaps(True, False); - if HandleAllocated then - Invalidate; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetButtonStyle(const Value: TVTButtonStyle); - -begin - if FButtonStyle <> Value then - begin - FButtonStyle := Value; - if not (csLoading in ComponentState) then - begin - PrepareBitmaps(True, False); - if HandleAllocated then - Invalidate; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetCheckState(Node: PVirtualNode; Value: TCheckState); - -begin - if (Node.CheckState <> Value) and DoChecking(Node, Value) then - DoCheckClick(Node, Value); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetCheckStateForAll(aCheckState: TCheckState; pSelectedOnly: Boolean; pExcludeDisabled: Boolean = True); - -// Changes the check state for all or for all seledcted nodes. -// aCheckState: The new check state. -// pSelectedOnly: If passed True, only the selected nodes will bechnaged, if passed False all nodes in the control will be changed. -// pExcludeDisabled: Optiopnal. If passed True (the default value), disabled checkboxes won't be changed, if passed False disabled checkboxes will be altered too. - -var - lItem : PVirtualNode; -begin - With Self do begin - Screen.Cursor := crHourGlass; - BeginUpdate; - try - if pSelectedOnly then - lItem := GetFirstSelected - else - lItem := GetFirst; - //for i:=0 to List.Items.Count-1 do begin - while Assigned(lItem) do begin - if not pExcludeDisabled or not CheckState[lItem].IsDisabled() then - CheckState[lItem] := aCheckState; - if pSelectedOnly then - lItem := GetNextSelected(lItem) - else - lItem := GetNext(lItem); - end;//while - finally - Screen.Cursor := crDefault; - EndUpdate; - end;//try..finally - end;//With -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetCheckType(Node: PVirtualNode; Value: TCheckType); - -begin - if (Node.CheckType <> Value) and not (toReadOnly in FOptions.MiscOptions) then - begin - Node.CheckType := Value; - if (Value <> ctTriStateCheckBox) and (Node.CheckState in [csMixedNormal, csMixedPressed]) then - Node.CheckState := csUncheckedNormal;// reset check state if it doesn't fit the new check type - // For check boxes with tri-state check box parents we have to initialize differently. - if (toAutoTriStateTracking in FOptions.AutoOptions) and (Value in [ctCheckBox, ctTriStateCheckBox]) and - (Node.Parent <> FRoot) then - begin - if not (vsInitialized in Node.Parent.States) then - InitNode(Node.Parent); - if (Node.Parent.CheckType = ctTriStateCheckBox) then begin - if (GetCheckState(Node.Parent) in [csUncheckedNormal, csUncheckedDisabled]) then - CheckState[Node] := csUncheckedNormal - else if (GetCheckState(Node.Parent) in [csCheckedNormal, csCheckedDisabled]) then - CheckState[Node] := csCheckedNormal; - end;//if - end;//if - InvalidateNode(Node); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetChildCount(Node: PVirtualNode; NewChildCount: Cardinal); - -// Changes a node's child structure to accomodate the new child count. This is used to add or delete -// child nodes to/from the end of the node's child list. To insert or delete a specific node a separate -// routine is used. - -var - Remaining: Cardinal; - Index: Cardinal; - Child: PVirtualNode; - Count: Integer; - NewHeight: Integer; -begin - if not (toReadOnly in FOptions.MiscOptions) then - begin - if Node = nil then - Node := FRoot; - - Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be changed in UI thread.'); - if NewChildCount = 0 then - DeleteChildren(Node) - else - begin - // If nothing changed then do nothing. - if NewChildCount <> Node.ChildCount then - begin - InterruptValidation; - - if NewChildCount > Node.ChildCount then - begin - Remaining := NewChildCount - Node.ChildCount; - Count := Remaining; - NewHeight := Node.TotalHeight; - - // New nodes to add. - if Assigned(Node.LastChild) then - Index := Node.LastChild.Index + 1 - else - begin - Index := 0; - Include(Node.States, vsHasChildren); - end; - Node.States := Node.States - [vsAllChildrenHidden, vsHeightMeasured]; - if (vsExpanded in Node.States) and FullyVisible[Node] then - Inc(FVisibleCount, Count); // Do this before a possible init of the sub-nodes in DoMeasureItem() - - // New nodes are by default always visible, so we don't need to check the visibility. - while Remaining > 0 do - begin - Child := MakeNewNode; - Child.Index := Index; - Child.PrevSibling := Node.LastChild; - if Assigned(Node.LastChild) then - Node.LastChild.NextSibling := Child; - Child.Parent := Node; - Node.LastChild := Child; - if Node.FirstChild = nil then - Node.FirstChild := Child; - Dec(Remaining); - Inc(Index); - - if (toVariableNodeHeight in FOptions.MiscOptions) then - GetNodeHeight(Child); - Inc(NewHeight, Child.TotalHeight); - end; - - if vsExpanded in Node.States then - AdjustTotalHeight(Node, NewHeight, False); - - AdjustTotalCount(Node, Count, True); - Node.ChildCount := NewChildCount; - if (FUpdateCount = 0) and (toAutoSort in FOptions.AutoOptions) and (FHeader.SortColumn > InvalidColumn) then - Sort(Node, FHeader.SortColumn, FHeader.SortDirection, True); - - InvalidateCache; - end//if NewChildCount > Node.ChildCount - else - begin - // Nodes have to be deleted. - Remaining := Node.ChildCount - NewChildCount; - while Remaining > 0 do - begin - DeleteNode(Node.LastChild); - Dec(Remaining); - end; - end; - - if FUpdateCount = 0 then - begin - ValidateCache; - UpdateScrollBars(True); - Invalidate; - end; - - if Node = FRoot then - StructureChange(nil, crChildAdded) - else - StructureChange(Node, crChildAdded); - end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetClipboardFormats(const Value: TClipboardFormats); - -var - I: Integer; - -begin - // Add string by string instead doing an Assign or AddStrings because the list may return -1 for - // invalid entries which cause trouble for the standard implementation. - FClipboardFormats.Clear; - for I := 0 to Value.Count - 1 do - FClipboardFormats.Add(Value[I]); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetColors(const Value: TVTColors); - -begin - FColors.Assign(Value); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetCheckImageKind(Value: TCheckImageKind); -begin - if (Value < Low(Value)) or (Value> High(Value)) then - Value := ckSystemDefault; - // property is deprecated. See issue #622 - if FCheckImageKind <> Value then - begin - if FCheckImageKind = ckSystemDefault then - FreeAndNil(FCheckImages); - FCheckImageKind := Value; - if Value = ckCustom then - FCheckImages := FCustomCheckImages - else if HandleAllocated then - FCheckImages := CreateSystemImageSet(Self); - if HandleAllocated and (FUpdateCount = 0) and not (csLoading in ComponentState) then - InvalidateRect(Handle, nil, False); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetCustomCheckImages(const Value: TCustomImageList); - -begin - if FCustomCheckImages <> Value then - begin - if Assigned(FCustomCheckImages) then - begin - FCustomCheckImages.UnRegisterChanges(FCustomCheckChangeLink); - FCustomCheckImages.RemoveFreeNotification(Self); - // Reset the internal check image list reference too, if necessary. - if FCheckImages = FCustomCheckImages then - FCheckImages := nil; - end; - FCustomCheckImages := Value; - if Assigned(FCustomCheckImages) then - begin - // If custom check images are assigned, we switch the property CheckImageKind to ckCustom so that they are actually used - CheckImageKind := ckCustom; - FCustomCheckImages.RegisterChanges(FCustomCheckChangeLink); - FCustomCheckImages.FreeNotification(Self); - end - else - CheckImageKind := ckSystemDefault; - if not (csLoading in ComponentState) then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetDefaultNodeHeight(Value: Cardinal); - -begin - if Value = 0 then - Value := 18; - if FDefaultNodeHeight <> Value then - begin - Inc(Integer(FRoot.TotalHeight), Integer(Value) - Integer(FDefaultNodeHeight)); - Inc(SmallInt(FRoot.NodeHeight), Integer(Value) - Integer(FDefaultNodeHeight)); - FDefaultNodeHeight := Value; - InvalidateCache; - if (FUpdateCount = 0) and HandleAllocated and not (csLoading in ComponentState) then - begin - ValidateCache; - UpdateScrollBars(True); - ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, True); - Invalidate; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetDisabled(Node: PVirtualNode; Value: Boolean); - -begin - if Assigned(Node) and (Value xor (vsDisabled in Node.States)) then - begin - if Value then - Include(Node.States, vsDisabled) - else - Exclude(Node.States, vsDisabled); - - if FUpdateCount = 0 then - InvalidateNode(Node); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetDoubleBuffered(const Value: Boolean); -begin - // empty by intention, we do our own buffering -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetDoubleBuffered: Boolean; -begin - Result := True; // we do our own buffering -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetEmptyListMessage(const Value: string); - -begin - if Value <> EmptyListMessage then - begin - FEmptyListMessage := Value; - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetExpanded(Node: PVirtualNode; Value: Boolean); - -begin - if Assigned(Node) and (Node <> FRoot) and (Value xor (vsExpanded in Node.States)) then - ToggleNode(Node); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetFocusedColumn(Value: TColumnIndex); - -begin - if (FFocusedColumn <> Value) and - DoFocusChanging(FFocusedNode, FFocusedNode, FFocusedColumn, Value) then - begin - CancelEditNode; - InvalidateColumn(FFocusedColumn); - InvalidateColumn(Value); - FFocusedColumn := Value; - if Assigned(FFocusedNode) and not (toDisableAutoscrollOnFocus in FOptions.AutoOptions) then - begin - if ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, True) then - InvalidateNode(FFocusedNode); - end; - - if Assigned(FDropTargetNode) then - InvalidateNode(FDropTargetNode); - - DoFocusChange(FFocusedNode, FFocusedColumn); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetFocusedNode(Value: PVirtualNode); - -var - WasDifferent: Boolean; - -begin - WasDifferent := Value <> FFocusedNode; - DoFocusNode(Value, True); - // Do change event only if there was actually a change. - if WasDifferent and (FFocusedNode = Value) then - DoFocusChange(FFocusedNode, FFocusedColumn); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetFullyVisible(Node: PVirtualNode; Value: Boolean); - -// This method ensures that a node is visible and all its parent nodes are expanded and also visible -// if Value is True. Otherwise the visibility flag of the node is reset but the expand state -// of the parent nodes stays untouched. - -begin - Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter'); - - IsVisible[Node] := Value; - if Value then - begin - repeat - Node := Node.Parent; - if Node = FRoot then - Break; - if not (vsExpanded in Node.States) then - ToggleNode(Node); - if not (vsVisible in Node.States) then - IsVisible[Node] := True; - until False; - end; - ScrollIntoView(Node, False); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetHasChildren(Node: PVirtualNode; Value: Boolean); - -begin - if Assigned(Node) and not (toReadOnly in FOptions.MiscOptions) then - begin - if Value then - Include(Node.States, vsHasChildren) - else - begin - Exclude(Node.States, vsHasChildren); - DeleteChildren(Node); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetHeader(const Value: TVTHeader); - -begin - FHeader.Assign(Value); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetHotNode(Value: PVirtualNode); - -begin - FCurrentHotNode := Value; -end; - - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetFiltered(Node: PVirtualNode; Value: Boolean); - -// Sets the 'filtered' flag of the given node according to Value and updates all dependent states. - -var - NeedUpdate: Boolean; - -begin - Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); - - // Initialize the node if necessary as this might change the filtered state. - if not (vsInitialized in Node.States) then - InitNode(Node); - - if Value <> (vsFiltered in Node.States) then - begin - InterruptValidation; - NeedUpdate := False; - if Value then - begin - Include(Node.States, vsFiltered); - if not (toShowFilteredNodes in FOptions.PaintOptions) then - begin - if (vsInitializing in Node.States) and not (vsHasChildren in Node.States) then - AdjustTotalHeight(Node, 0, False) - else - AdjustTotalHeight(Node, -Integer(NodeHeight[Node]), True); - if FullyVisible[Node] then - begin - Dec(FVisibleCount); - NeedUpdate := True; - end; - if FocusedNode = Node then - FocusedNode := nil; - end; - - if FUpdateCount = 0 then - DetermineHiddenChildrenFlag(Node.Parent) - else - Include(FStates, tsUpdateHiddenChildrenNeeded); - end - else - begin - Exclude(Node.States, vsFiltered); - if not (toShowFilteredNodes in FOptions.PaintOptions) then - begin - AdjustTotalHeight(Node, Integer(NodeHeight[Node]), True); - if FullyVisible[Node] then - begin - Inc(FVisibleCount); - NeedUpdate := True; - end; - end; - - if vsVisible in Node.States then - // Update the hidden children flag of the parent. - // Since this node is now visible we simply have to remove the flag. - Exclude(Node.Parent.States, vsAllChildrenHidden); - end; - - InvalidateCache; - if NeedUpdate and (FUpdateCount = 0) then - begin - ValidateCache; - UpdateScrollBars(True); - Invalidate; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.SetImages(const Value: TCustomImageList); - -begin - if FImages <> Value then - begin - if Assigned(FImages) then - begin - FImages.UnRegisterChanges(FImageChangeLink); - FImages.RemoveFreeNotification(Self); - end; - FImages := Value; - if Assigned(FImages) then - begin - FImages.RegisterChanges(FImageChangeLink); - FImages.FreeNotification(Self); - end; - if not (csLoading in ComponentState) then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetIndent(Value: Cardinal); - -begin - if FIndent <> Value then - begin - FIndent := Value; - if not (csLoading in ComponentState) and (FUpdateCount = 0) and HandleAllocated then - begin - UpdateScrollBars(True); - Invalidate; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetLineMode(const Value: TVTLineMode); - -begin - if FLineMode <> Value then - begin - FLineMode := Value; - if HandleAllocated and not (csLoading in ComponentState) then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetLineStyle(const Value: TVTLineStyle); - -begin - if FLineStyle <> Value then - begin - FLineStyle := Value; - if not (csLoading in ComponentState) then - begin - PrepareBitmaps(False, True); - if HandleAllocated then - Invalidate; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetMargin(Value: Integer); - -begin - if FMargin <> Value then - begin - FMargin := Value; - if HandleAllocated and not (csLoading in ComponentState) then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetMultiline(Node: PVirtualNode; const Value: Boolean); - -begin - if Assigned(Node) and (Node <> FRoot) then - if Value <> (vsMultiline in Node.States) then - begin - if Value then - Include(Node.States, vsMultiline) - else - Exclude(Node.States, vsMultiline); - - if FUpdateCount = 0 then - InvalidateNode(Node); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetNodeAlignment(const Value: TVTNodeAlignment); - -begin - if FNodeAlignment <> Value then - begin - FNodeAlignment := Value; - if HandleAllocated and not (csReading in ComponentState) then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetNodeData(pNode: PVirtualNode; pUserData: Pointer); - - // Can be used to set user data of a PVirtualNode with the size of a pointer, useful for setting - // A pointer to a record or a reference to a class instance. - -var - NodeData: PPointer; -begin - // Check if there is initial user data and there is also enough user data space allocated. - Assert(FNodeDataSize >= SizeOf(Pointer), Self.Classname + ': Cannot set initial user data because there is not enough user data space allocated.'); - NodeData := PPointer(@pNode.Data); - NodeData^ := pUserData; - Include(pNode.States, vsOnFreeNodeCallRequired); -end; - -procedure TBaseVirtualTree.SetNodeData(pNode: PVirtualNode; pUserData: T); - - // Can be used to set user data of a PVirtualNode to a class instance. - -begin - pNode.SetData(pUserData); -end; - -procedure TBaseVirtualTree.SetNodeData(pNode: PVirtualNode; const pUserData: IInterface); - - // Can be used to set user data of a PVirtualNode to a class instance, - // will take care about reference counting. - -begin - pNode.SetData(pUserData); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetNodeDataSize(Value: Integer); - -var - LastRootCount: Cardinal; - -begin - if Value < -1 then - Value := -1; - if FNodeDataSize <> Value then - begin - FNodeDataSize := Value; - if not (csLoading in ComponentState) and not (csDesigning in ComponentState) then - begin - LastRootCount := FRoot.ChildCount; - Clear; - SetRootNodeCount(LastRootCount); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetNodeHeight(Node: PVirtualNode; Value: Cardinal); - -var - Difference: Integer; - -begin - Assert(Assigned(Node), 'SetNodeHeight() cannot be called with Node = nil'); - Assert((Node <> FRoot), 'SetNodeHeight() cannot be called for the root node FRoot'); - if (Node.NodeHeight <> Value) then - begin - Difference := Integer(Value) - Integer(Node.NodeHeight); - Node.NodeHeight := Value; - - // If the node is effectively filtered out, nothing else has to be done, as it is not visible anyway. - if not IsEffectivelyFiltered[Node] then - begin - AdjustTotalHeight(Node, Difference, True); - - // If an edit operation is currently active then update the editors boundaries as well. - UpdateEditBounds; - - InvalidateCache; - // Stay away from touching the node cache while it is being validated. - if not (tsValidating in FStates) and FullyVisible[Node] then - begin - if (FUpdateCount = 0) and ([tsPainting, tsSizing] * FStates = []) then - begin - ValidateCache; - InvalidateToBottom(Node); - UpdateScrollBars(True); - end; - end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetNodeParent(Node: PVirtualNode; const Value: PVirtualNode); - -begin - if Assigned(Node) and Assigned(Value) and (Node.Parent <> Value) then - MoveTo(Node, Value, amAddChildLast, False); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetOffsetX(const Value: Integer); - -begin - DoSetOffsetXY(Point(Value, FOffsetY), DefaultScrollUpdateFlags); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetOffsetXY(const Value: TPoint); - -begin - DoSetOffsetXY(Value, DefaultScrollUpdateFlags); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetOffsetY(const Value: Integer); - -begin - DoSetOffsetXY(Point(FOffsetX, Value), DefaultScrollUpdateFlags); -end; - -procedure TBaseVirtualTree.SetOnPrepareButtonImages(const Value: TVTPrepareButtonImagesEvent); -begin - FOnPrepareButtonImages := Value; - PrepareBitmaps(True, False); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetOptions(const Value: TCustomVirtualTreeOptions); - -begin - FOptions.Assign(Value); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetRangeX(value: Cardinal); -begin - FRangeX := value; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetRootNodeCount(Value: Cardinal); - -begin - // Don't set the root node count until all other properties (in particular the OnInitNode event) have been set. - if csLoading in ComponentState then - begin - FRoot.ChildCount := Value; - DoStateChange([tsNeedRootCountUpdate]); - end - else - if FRoot.ChildCount <> Value then - begin - BeginUpdate; - InterruptValidation; - SetChildCount(FRoot, Value); - EndUpdate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetScrollBarOptions(Value: TScrollBarOptions); - -begin - FScrollBarOptions.Assign(Value); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetSearchOption(const Value: TVTIncrementalSearch); - -begin - if FIncrementalSearch <> Value then - begin - FIncrementalSearch := Value; - if FIncrementalSearch = isNone then - begin - StopTimer(SearchTimer); - FSearchBuffer := ''; - FLastSearchNode := nil; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetSelected(Node: PVirtualNode; Value: Boolean); - -begin - if not FSelectionLocked and Assigned(Node) and (Node <> FRoot) and (Value xor (vsSelected in Node.States)) then - begin - if Value then - begin - if FSelectionCount = 0 then - FRangeAnchor := Node - else begin - if not (toMultiSelect in FOptions.SelectionOptions) then - ClearSelection; - if FRangeAnchor = nil then - FRangeAnchor := Node; - end; - - AddToSelection(Node, True); - - if not (toMultiSelect in FOptions.SelectionOptions) then - FocusedNode := GetFirstSelected; // if only one node can be selected, make sure the focused node changes with the selected node - // Make sure there is a valid column selected (if there are columns at all). - if ((FFocusedColumn < 0) or not (coVisible in FHeader.Columns[FFocusedColumn].Options)) and - (FHeader.MainColumn > NoColumn) then - if ([coVisible, coAllowFocus] * FHeader.Columns[FHeader.MainColumn].Options = [coVisible, coAllowFocus]) then - FFocusedColumn := FHeader.MainColumn - else - FFocusedColumn := FHeader.Columns.GetFirstVisibleColumn(True); - end - else - begin - RemoveFromSelection(Node); - if FSelectionCount = 0 then - ResetRangeAnchor; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetSelectionCurveRadius(const Value: Cardinal); - -begin - if FSelectionCurveRadius <> Value then - begin - FSelectionCurveRadius := Value; - if HandleAllocated and not (csLoading in ComponentState) then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetStateImages(const Value: TCustomImageList); - -begin - if FStateImages <> Value then - begin - if Assigned(FStateImages) then - begin - FStateImages.UnRegisterChanges(FStateChangeLink); - FStateImages.RemoveFreeNotification(Self); - end; - FStateImages := Value; - if Assigned(FStateImages) then - begin - FStateImages.RegisterChanges(FStateChangeLink); - FStateImages.FreeNotification(Self); - end; - if HandleAllocated and not (csLoading in ComponentState) then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetTextMargin(Value: Integer); - -begin - if FTextMargin <> Value then - begin - FTextMargin := Value; - if not (csLoading in ComponentState) then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetTopNode(Node: PVirtualNode); - -var - R: TRect; - Run: PVirtualNode; - -begin - if Assigned(Node) then - begin - // make sure all parents of the node are expanded - Run := Node.Parent; - while Run <> FRoot do - begin - if not (vsExpanded in Run.States) then - ToggleNode(Run); - Run := Run.Parent; - end; - R := GetDisplayRect(Node, FHeader.MainColumn, True); - SetOffsetY(FOffsetY - R.Top); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetUpdateState(Updating: Boolean); - -begin - // The check for visibility is necessary otherwise the tree is automatically shown when - // updating is allowed. As this happens internally the VCL does not get notified and - // still assumes the control is hidden. This results in weird "cannot focus invisible control" errors. - if Visible and HandleAllocated and (FUpdateCount = 0) then - SendMessage(Handle, WM_SETREDRAW, Ord(not Updating), 0); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetVerticalAlignment(Node: PVirtualNode; Value: Byte); - -begin - if Value > 100 then - Value := 100; - if Node.Align <> Value then - begin - Node.Align := Value; - if FullyVisible[Node] and not IsEffectivelyFiltered[Node] then - InvalidateNode(Node); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetVisible(Node: PVirtualNode; Value: Boolean); - -// Sets the visibility style of the given node according to Value. - -var - NeedUpdate: Boolean; - -begin - Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); - - if Value <> (vsVisible in Node.States) then - begin - InterruptValidation; - NeedUpdate := False; - if Value then - begin - Include(Node.States, vsVisible); - if vsExpanded in Node.Parent.States then - AdjustTotalHeight(Node.Parent, Node.TotalHeight, True); - if VisiblePath[Node] then - begin - Inc(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); - NeedUpdate := True; - end; - - // Update the hidden children flag of the parent. - // Since this node is now visible we simply have to remove the flag. - if not IsEffectivelyFiltered[Node] then - Exclude(Node.Parent.States, vsAllChildrenHidden); - end - else - begin - if vsExpanded in Node.Parent.States then - AdjustTotalHeight(Node.Parent, -Integer(Node.TotalHeight), True); - if VisiblePath[Node] then - begin - Dec(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); - NeedUpdate := True; - end; - Exclude(Node.States, vsVisible); - - if FUpdateCount = 0 then - DetermineHiddenChildrenFlag(Node.Parent) - else - Include(FStates, tsUpdateHiddenChildrenNeeded); - end; - - InvalidateCache; - if NeedUpdate and (FUpdateCount = 0) then - begin - ValidateCache; - UpdateScrollBars(True); - Invalidate; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetVisiblePath(Node: PVirtualNode; Value: Boolean); - -// If Value is True then all parent nodes of Node are expanded. - -begin - Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); - - if Value then - begin - repeat - Node := Node.Parent; - if Node = FRoot then - Break; - if not (vsExpanded in Node.States) then - ToggleNode(Node); - until False; - end; -end; - -// ---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.PrepareBackGroundPicture(Source: TPicture; - DrawBitmap: TBitmap; DrawBitmapWidth: Integer; DrawBitMapHeight: Integer; ABkgcolor: TColor); -const - DST = $00AA0029; // Ternary Raster Operation - Destination unchanged - - // fill background will work for transparent images and - // will not disturb non-transparent ones - procedure FillDrawBitmapWithBackGroundColor; - begin - DrawBitmap.Canvas.Brush.Color := ABkgcolor; - DrawBitmap.Canvas.FillRect(Rect(0, 0, DrawBitmap.Width, DrawBitmap.Height)); - end; - -begin - DrawBitmap.SetSize(DrawBitmapWidth, DrawBitMapHeight); - - if (Source.Graphic is TBitmap) and - (FBackGroundImageTransparent or Source.Bitmap.TRANSPARENT) - then - begin - FillDrawBitmapWithBackGroundColor; - MaskBlt(DrawBitmap.Canvas.Handle, 0, 0, Source.Width, Source.Height, - Source.Bitmap.Canvas.Handle, 0, 0, Source.Bitmap.MaskHandle, 0, 0, - MakeROP4(DST, SRCCOPY)); - end - else - begin - // Similar to TImage's Transparent property behavior, we don't want - // to draw transparent if the following flag is OFF. - if FBackGroundImageTransparent then - FillDrawBitmapWithBackGroundColor; - DrawBitmap.Canvas.Draw(0, 0, Source.Graphic); - end -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.StaticBackground(Source: TPicture; Target: TCanvas; OffsetPosition: TPoint; R: TRect; aBkgColor: TColor); - -// Draws the given source graphic so that it stays static in the given rectangle which is relative to the target bitmap. -// The graphic is aligned so that it always starts at the upper left corner of the target canvas. -// Offset gives the position of the target window as a possible superordinated surface. - -const - DST = $00AA0029; // Ternary Raster Operation - Destination unchanged - -var - PicRect: TRect; - AreaRect: TRect; - DrawRect: TRect; - DrawBitmap: TBitmap; -begin - DrawBitmap := TBitmap.Create; - try - // clear background - Target.Brush.Color := aBkgColor; - Target.FillRect(R); - - // Picture rect in relation to client viewscreen. - PicRect := Rect(FBackgroundOffsetX, FBackgroundOffsetY, FBackgroundOffsetX + Source.Width, FBackgroundOffsetY + Source.Height); - - // Area to be draw in relation to client viewscreen. - AreaRect := Rect(OffsetPosition.X + R.Left, OffsetPosition.Y + R.Top, OffsetPosition.X + R.Right, OffsetPosition.Y + R.Bottom); - - // If picture falls in AreaRect, return intersection (DrawRect). - if IntersectRect(DrawRect, PicRect, AreaRect) then - begin - PrepareBackGroundPicture(Source, DrawBitmap, Source.Width, Source.Height, aBkgColor); - // copy image to destination - BitBlt(Target.Handle, DrawRect.Left - OffsetPosition.X, DrawRect.Top - OffsetPosition.Y, (DrawRect.Right - OffsetPosition.X) - (DrawRect.Left - OffsetPosition.X), - (DrawRect.Bottom - OffsetPosition.Y) - (DrawRect.Top - OffsetPosition.Y) + R.Top, DrawBitmap.Canvas.Handle, DrawRect.Left - PicRect.Left, DrawRect.Top - PicRect.Top, - SRCCOPY); - end; - finally - DrawBitmap.Free; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.StopTimer(ID: Integer); - -begin - if HandleAllocated then - KillTimer(Handle, ID); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.SetWindowTheme(const Theme: string); - -begin - FChangingTheme := True; - Winapi.UxTheme.SetWindowTheme(Handle, PWideChar(Theme), nil); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -//used by TCustomVirtualTreeOptions -procedure TBaseVirtualTree.SetVisibleCount(value : Cardinal); -begin - FVisibleCount := value; -end; - - -procedure TBaseVirtualTree.TileBackground(Source: TPicture; Target: TCanvas; Offset: TPoint; R: TRect; aBkgColor: TColor); - -// Draws the given source graphic so that it tiles into the given rectangle which is relative to the target bitmap. -// The graphic is aligned so that it always starts at the upper left corner of the target canvas. -// Offset gives the position of the target window in an possible superordinated surface. - -var - SourceX, - SourceY, - TargetX, - DeltaY: Integer; - DrawBitmap: TBitmap; -begin - DrawBitmap := TBitmap.Create; - try - PrepareBackGroundPicture(Source, DrawBitmap, Source.Width, Source.Height, aBkgColor); - with Target do - begin - SourceY := (R.Top + Offset.Y + FBackgroundOffsetY) mod Source.Height; - // Always wrap the source coordinates into positive range. - if SourceY < 0 then - SourceY := Source.Height + SourceY; - - // Tile image vertically until target rect is filled. - while R.Top < R.Bottom do - begin - SourceX := (R.Left + Offset.X + FBackgroundOffsetX) mod Source.Width; - // always wrap the source coordinates into positive range - if SourceX < 0 then - SourceX := Source.Width + SourceX; - - TargetX := R.Left; - // height of strip to draw - DeltaY := Min(R.Bottom - R.Top, Source.Height - SourceY); - - // tile the image horizontally - while TargetX < R.Right do - begin - BitBlt(Handle, TargetX, R.Top, Min(R.Right - TargetX, Source.Width - SourceX), DeltaY, - DrawBitmap.Canvas.Handle, SourceX, SourceY, SRCCOPY); - Inc(TargetX, Source.Width - SourceX); - SourceX := 0; - end; - Inc(R.Top, Source.Height - SourceY); - SourceY := 0; - end; - end; - finally - DrawBitmap.Free; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.ToggleCallback(Step, StepSize: Integer; Data: Pointer): Boolean; - -var - Column: TColumnIndex; - Run: TRect; - SecondaryStepSize: Integer; - - //--------------- local functions ------------------------------------------- - - procedure EraseLine; - - var - LocalBrush: HBRUSH; - - begin - with TToggleAnimationData(Data^), FHeader.Columns do - begin - // Iterate through all columns and erase background in their local color. - // LocalBrush is a brush in the color of the particular column. - Column := GetFirstVisibleColumn; - while (Column > InvalidColumn) and (Run.Left < ClientWidth) do - begin - GetColumnBounds(Column, Run.Left, Run.Right); - if coParentColor in Items[Column].Options then - FillRect(DC, Run, Brush) - else - begin - if VclStyleEnabled then - LocalBrush := CreateSolidBrush(ColorToRGB(FColors.BackGroundColor)) - else - LocalBrush := CreateSolidBrush(ColorToRGB(Items[Column].Color)); - FillRect(DC, Run, LocalBrush); - DeleteObject(LocalBrush); - end; - Column := GetNextVisibleColumn(Column); - end; - end; - end; - - //--------------------------------------------------------------------------- - - procedure DoScrollUp(DC: HDC; Brush: HBRUSH; Area: TRect; Steps: Integer); - - begin - ScrollDC(DC, 0, -Steps, Area, Area, 0, nil); - - if Step = 0 then - if not FHeader.UseColumns then - FillRect(DC, Rect(Area.Left, Area.Bottom - Steps - 1, Area.Right, Area.Bottom), Brush) - else - begin - Run := Rect(Area.Left, Area.Bottom - Steps - 1, Area.Right, Area.Bottom); - EraseLine; - end; - end; - - //--------------------------------------------------------------------------- - - procedure DoScrollDown(DC: HDC; Brush: HBRUSH; Area: TRect; Steps: Integer); - - begin - ScrollDC(DC, 0, Steps, Area, Area, 0, nil); - - if Step = 0 then - if not FHeader.UseColumns then - FillRect(DC, Rect(Area.Left, Area.Top, Area.Right, Area.Top + Steps + 1), Brush) - else - begin - Run := Rect(Area.Left, Area.Top, Area.Right, Area.Top + Steps + 1); - EraseLine; - end; - end; - - //--------------- end local functions --------------------------------------- - -begin - Result := True; - if StepSize > 0 then - begin - SecondaryStepSize := 0; - with TToggleAnimationData(Data^) do - begin - if Mode1 <> tamNoScroll then - begin - if Mode1 = tamScrollUp then - DoScrollUp(DC, Brush, R1, StepSize) - else - DoScrollDown(DC, Brush, R1, StepSize); - - if (Mode2 <> tamNoScroll) and (ScaleFactor > 0) then - begin - // As this routine is able to scroll two independent areas at once, the missing StepSize is - // computed in that case. To ensure the maximal accuracy the rounding error is accumulated. - SecondaryStepSize := Round((StepSize + MissedSteps) * ScaleFactor); - MissedSteps := MissedSteps + StepSize * ScaleFactor - SecondaryStepSize; - end; - end - else - SecondaryStepSize := StepSize; - - if Mode2 <> tamNoScroll then - if Mode2 = tamScrollUp then - DoScrollUp(DC, Brush, R2, SecondaryStepSize) - else - DoScrollDown(DC, Brush, R2, SecondaryStepSize); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMColorChange(var Message: TMessage); - -begin - if not (csLoading in ComponentState) then - begin - PrepareBitmaps(True, False); - if HandleAllocated then - Invalidate; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMCtl3DChanged(var Message: TMessage); - -begin - inherited; - if FBorderStyle = bsSingle then - RecreateWnd; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMBiDiModeChanged(var Message: TMessage); - -begin - inherited; - - if UseRightToLeftAlignment then - FEffectiveOffsetX := Integer(FRangeX) - ClientWidth + FOffsetX - else - FEffectiveOffsetX := -FOffsetX; - if FEffectiveOffsetX < 0 then - FEffectiveOffsetX := 0; - - if toAutoBidiColumnOrdering in FOptions.AutoOptions then - FHeader.Columns.ReorderColumns(UseRightToLeftAlignment); - FHeader.Invalidate(nil); -end; - -procedure TBaseVirtualTree.CMBorderChanged(var Message: TMessage); -begin - inherited; - if VclStyleEnabled and (seBorder in StyleElements) then - RecreateWnd; -end; - -procedure TBaseVirtualTree.CMParentDoubleBufferedChange(var Message: TMessage); -begin - // empty by intention, we do our own buffering -end; - -procedure TBaseVirtualTree.CMStyleChanged(var Message: TMessage); -begin - VclStyleChanged; - RecreateWnd; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMDenySubclassing(var Message: TMessage); - -// If a Windows XP Theme Manager component is used in the application it will try to subclass all controls which do not -// explicitly deny this. Virtual Treeview knows how to handle XP themes so it does not need subclassing. - -begin - Message.Result := 1; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMDrag(var Message: TCMDrag); - -var - S: TObject; - ShiftState: Integer; - P: TPoint; - Formats: TFormatArray; - Effect: Integer; - -begin - with Message, DragRec^ do - begin - S := Source; - Formats := nil; - - // Let the ancestor handle dock operations. - if S is TDragDockObject then - inherited - else - begin - // We need an extra check for the control drag object as there might be other objects not derived from - // this class (e.g. TActionDragObject). - if not (tsUserDragObject in FStates) and (S is TBaseDragControlObject) then - S := (S as TBaseDragControlObject).Control; - case DragMessage of - dmDragEnter, dmDragLeave, dmDragMove: - begin - if DragMessage = dmDragEnter then - DoStateChange([tsVCLDragging]); - if DragMessage = dmDragLeave then - DoStateChange([tsVCLDragFinished], [tsVCLDragging]); - - if DragMessage = dmDragMove then - with ScreenToClient(Pos) do - DoAutoScroll(X, Y); - - ShiftState := 0; - // Alt key will be queried by the KeysToShiftState function in DragOver. - if GetKeyState(VK_SHIFT) < 0 then - ShiftState := ShiftState or MK_SHIFT; - if GetKeyState(VK_CONTROL) < 0 then - ShiftState := ShiftState or MK_CONTROL; - - // Allowed drop effects are simulated for VCL dd. - Effect := DROPEFFECT_MOVE or DROPEFFECT_COPY; - DragOver(S, ShiftState, TDragState(DragMessage), Pos, Effect); - FLastVCLDragTarget := FDropTargetNode; - FVCLDragEffect := Effect; - if (DragMessage = dmDragLeave) and Assigned(FDropTargetNode) then - begin - InvalidateNode(FDropTargetNode); - FDropTargetNode := nil; - end; - Result := LRESULT(Effect); - end; - dmDragDrop: - begin - ShiftState := 0; - // Alt key will be queried by the KeysToShiftState function in DragOver - if GetKeyState(VK_SHIFT) < 0 then - ShiftState := ShiftState or MK_SHIFT; - if GetKeyState(VK_CONTROL) < 0 then - ShiftState := ShiftState or MK_CONTROL; - - // allowed drop effects are simulated for VCL dd, - // replace target node with cached node from other VCL dd messages - if Assigned(FDropTargetNode) then - InvalidateNode(FDropTargetNode); - FDropTargetNode := FLastVCLDragTarget; - P := Point(Pos.X, Pos.Y); - P := ScreenToClient(P); - try - DoDragDrop(S, nil, Formats, KeysToShiftState(ShiftState), P, FVCLDragEffect, FLastDropMode); - finally - if Assigned(FDropTargetNode) then - begin - InvalidateNode(FDropTargetNode); - FDropTargetNode := nil; - end; - end; - end; - dmFindTarget: - begin - Result := LRESULT(ControlAtPos(ScreenToClient(Pos), False)); - if Result = 0 then - Result := LRESULT(Self); - - // This is a reliable place to check whether VCL drag has - // really begun. - if tsVCLDragPending in FStates then - DoStateChange([tsVCLDragging], [tsVCLDragPending, tsEditPending, tsClearPending]); - end; - end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMEnabledChanged(var Message: TMessage); - -begin - inherited; - - // Need to invalidate the non-client area as well, since the header must be redrawn too. - if csDesigning in ComponentState then - RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMFontChanged(var Message: TMessage); - -var - HeaderMessage: TMessage; - -begin - inherited; - - if not (csLoading in ComponentState) then - begin - if HandleAllocated then begin - AutoScale(False); - Invalidate; - end - end; - - HeaderMessage.Msg := CM_PARENTFONTCHANGED; - HeaderMessage.WParam := 0; - HeaderMessage.LParam := 0; - HeaderMessage.Result := 0; - FHeader.HandleMessage(HeaderMessage); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMHintShow(var Message: TCMHintShow); - -// Determines hint message (tooltip) and out-of-hint rect. -// Note: A special handling is needed here because we cannot pass wide strings back to the caller. -// I had to introduce the hint data record anyway so we can use this to pass the hint string. -// We still need to set a dummy hint string in the message to make the VCL showing the hint window. - -var - NodeRect: TRect; - SpanColumn, - Dummy, - ColLeft, - ColRight: Integer; - HitInfo: THitInfo; - ShowOwnHint: Boolean; - IsFocusedOrEditing: Boolean; - ParentForm: TCustomForm; - BottomRightCellContentMargin: TPoint; - HintKind: TVTHintKind; -begin - with Message do - begin - Result := 1; - - if PtInRect(FLastHintRect, HintInfo.CursorPos) then - Exit; - - // Determine node for which to show hint/tooltip. - with HintInfo^ do - GetHitTestInfoAt(CursorPos.X, CursorPos.Y, True, HitInfo); - - // Make sure a hint is only shown if the tree or at least its parent form is active. - // Active editing is ok too as long as we don't want the hint for the current edit node. - if IsEditing then - IsFocusedOrEditing := HitInfo.HitNode <> FFocusedNode - else - begin - IsFocusedOrEditing := Focused; - ParentForm := GetParentForm(Self); - if Assigned(ParentForm) then - IsFocusedOrEditing := ParentForm.Focused or Application.Active; - end; - - if (GetCapture = 0) and ShowHint and not (Dragging or IsMouseSelecting) and ([tsScrolling] * FStates = []) and - (FHeader.States = []) and IsFocusedOrEditing then - begin - with HintInfo^ do - begin - Result := 0; - ShowOwnHint := False; - - //workaround for issue #291 - //it duplicates parts of the following code and code in TVirtualTreeHintWindow - HintStr := ''; - if FHeader.UseColumns and (hoShowHint in FHeader.Options) and FHeader.InHeader(CursorPos) then - begin - CursorRect := FHeaderRect; - // Convert the cursor rectangle into real client coordinates. - OffsetRect(CursorRect, 0, -Integer(FHeader.Height)); - HitInfo.HitColumn := FHeader.Columns.GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right); - if (HitInfo.HitColumn > NoColumn) and not (csLButtonDown in ControlState) and - (FHeader.Columns[HitInfo.HitColumn].Hint <> '') then - HintStr := FHeader.Columns[HitInfo.HitColumn].Hint; - end - else - if HintMode = hmDefault then - HintStr := GetShortHint(Hint) - else - if Assigned(HitInfo.HitNode) and (HitInfo.HitColumn > InvalidColumn) then - begin - if HintMode = hmToolTip then - HintStr := DoGetNodeToolTip(HitInfo.HitNode, HitInfo.HitColumn, fHintData.LineBreakStyle) - else - HintStr := DoGetNodeHint(HitInfo.HitNode, HitInfo.HitColumn, fHintData.LineBreakStyle); - end; - - // First check whether there is a header hint to show. - if FHeader.UseColumns and (hoShowHint in FHeader.Options) and FHeader.InHeader(CursorPos) then - begin - CursorRect := FHeaderRect; - // Convert the cursor rectangle into real client coordinates. - OffsetRect(CursorRect, 0, -Integer(FHeader.Height)); - HitInfo.HitColumn := FHeader.Columns.GetColumnAndBounds(CursorPos, CursorRect.Left, CursorRect.Right); - // align the vertical hint position on the bottom bound of the header, but - // avoid overlapping of mouse cursor and hint - HintPos.Y := Max(HintPos.Y, ClientToScreen(Point(0, CursorRect.Bottom)).Y); - // Note: the test for the left mouse button in ControlState might cause problems whenever the VCL does not - // realize when the button is released. This, for instance, happens when doing OLE drag'n drop and - // cancel this with ESC. - if (HitInfo.HitColumn > NoColumn) and not (csLButtonDown in ControlState) then - begin - HintStr := FHeader.Columns[HitInfo.HitColumn].Hint; - if HintStr = '' then - with FHeader.Columns[HitInfo.HitColumn] do - begin - if (2 * FMargin + CaptionWidth + 1) >= Width then - HintStr := FCaptionText; - end; - if HintStr <> '' then - ShowOwnHint := True - else - Result := 1; - end - else - Result := 1; - end - else - begin - // Default mode is handled as would the tree be a usual VCL control (no own hint window necessary). - if FHintMode = hmDefault then - HintStr := GetShortHint(Hint) - else - begin - if Assigned(HitInfo.HitNode) and (HitInfo.HitColumn > InvalidColumn) then - begin - // An owner-draw tree should only display a hint when at least - // its OnGetHintSize event handler is assigned. - DoGetHintKind(HitInfo.HitNode, HitInfo.HitColumn, HintKind); - FHintData.HintRect := Rect(0, 0, 0, 0); - if (HintKind = vhkOwnerDraw) then - begin - DoGetHintSize(HitInfo.HitNode, HitInfo.HitColumn, FHintData.HintRect); - ShowOwnHint := not IsRectEmpty(FHintData.HintRect); - end - else - // For trees displaying text hints, a decision about showing the hint or not is based - // on the hint string (if it is empty then no hint is shown). - ShowOwnHint := True; - - if ShowOwnHint then - begin - if HitInfo.HitColumn > NoColumn then - begin - FHeader.Columns.GetColumnBounds(HitInfo.HitColumn, ColLeft, ColRight); - // The right column border might be extended if column spanning is enabled. - if toAutoSpanColumns in FOptions.AutoOptions then - begin - SpanColumn := HitInfo.HitColumn; - repeat - Dummy := FHeader.Columns.GetNextVisibleColumn(SpanColumn); - if (Dummy = InvalidColumn) or not ColumnIsEmpty(HitInfo.HitNode, Dummy) then - Break; - SpanColumn := Dummy; - until False; - if SpanColumn <> HitInfo.HitColumn then - FHeader.Columns.GetColumnBounds(SpanColumn, Dummy, ColRight); - end; - end - else - begin - ColLeft := 0; - ColRight := ClientWidth; - end; - - if FHintMode <> hmTooltip then - begin - // Node specific hint text. - CursorRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, False); - CursorRect.Left := ColLeft; - CursorRect.Right := ColRight; - // Align the vertical hint position on the bottom bound of the node, but - // avoid overlapping of mouse cursor and hint. - HintPos.Y := Max(HintPos.Y, ClientToScreen(CursorRect.BottomRight).Y) + ScaledPixels(2); - end - else - begin - // Tool tip to show. This means the full caption of the node must be displayed. - if vsMultiline in HitInfo.HitNode.States then - begin - if hiOnItemLabel in HitInfo.HitPositions then - begin - ShowOwnHint := True; - NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, True, False); - end - else - ShowOwnHint := False; - end - else - begin - NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, True, True, True); - BottomRightCellContentMargin := DoGetCellContentMargin(HitInfo.HitNode, HitInfo.HitColumn, ccmtBottomRightOnly); - - ShowOwnHint := (HitInfo.HitColumn > InvalidColumn) and PtInRect(NodeRect, CursorPos) and - (CursorPos.X <= ColRight) and (CursorPos.X >= ColLeft) and - ( - // Show hint also if the node text is partially out of the client area. - // "ColRight - 1", since the right column border is not part of this cell. - ( (NodeRect.Right + BottomRightCellContentMargin.X) > Min(ColRight - 1, ClientWidth) ) or - (NodeRect.Left < Max(ColLeft, 0)) or - ( (NodeRect.Bottom + BottomRightCellContentMargin.Y) > ClientHeight ) or - (NodeRect.Top < 0) - ); - end; - - if ShowOwnHint then - begin - // Node specific hint text given will be retrieved when needed. - HintPos := ClientToScreen(Point(NodeRect.Left, NodeRect.Top)); - CursorRect := NodeRect; - end - else - // nothing to show - Result := 1; - end; - end - else - Result := 1; // Avoid hint if this is a draw tree returning an empty hint rectangle. - end - else - begin - // No node so fall back to control's hint (if indicated) or show nothing. - if FHintMode = hmHintAndDefault then - begin - HintStr := GetShortHint(Hint); - - // Fix for the problem: Default Hint once shown stayed even when - // node hint was to be displayed. The reason was that CursorRect - // was for the full client area. Now reducing it to remove the - // columns from it. - if BidiMode = bdLeftToRight then - CursorRect.Left := Header.Columns.TotalWidth - else - CursorRect.right := CursorRect.right - Header.Columns.TotalWidth; - - if Length(HintStr) = 0 then - Result := 1 - else - ShowOwnHint := True; - end - else - Result := 1; - end; - end; - end; - - // Set our own hint window class and prepare structure to be passed to the hint window. - if ShowOwnHint and (Result = 0) then - begin - HintWindowClass := GetHintWindowClass; - FHintData.HintText := HintStr; - FHintData.Tree := Self; - FHintData.Column := HitInfo.HitColumn; - FHintData.Node := HitInfo.HitNode; - FLastHintRect := CursorRect; - HintData := @FHintData; - end - else - FLastHintRect := Rect(0, 0, 0, 0); - end; - - // Remind that a hint is about to show. - if Result = 0 then - DoStateChange([tsHint]) - else - DoStateChange([], [tsHint]); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMHintShowPause(var Message: TCMHintShowPause); - -// Tells the application that the tree (and only the tree) does not want a delayed tool tip. -// Normal hints / header hints use the default delay (except for the first time). - - begin - if ShowHint and (FHintMode = hmToolTip) then - Message.Pause^ := 0; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMMouseEnter(var Message: TMessage); -begin - DoMouseEnter(); - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMMouseLeave(var Message: TMessage); - -var - LeaveStates: TVirtualTreeStates; - -begin - // Reset the last used hint rectangle in case the mouse enters the window within the bounds - if Assigned(FHintData.Tree) then - FHintData.Tree.FLastHintRect := Rect(0, 0, 0, 0); - - LeaveStates := [tsHint]; - if [tsWheelPanning, tsWheelScrolling] * FStates = [] then - begin - StopTimer(ScrollTimer); - LeaveStates := LeaveStates + [tsScrollPending, tsScrolling]; - end; - DoStateChange([], LeaveStates); - if Assigned(FCurrentHotNode) then - begin - DoHotChange(FCurrentHotNode, nil); - if (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.MiscOptions) then - InvalidateNode(FCurrentHotNode); - FCurrentHotNode := nil; - end; - - if Assigned(Header) then - begin - Header.FColumns.DownIndex := NoColumn; - Header.FColumns.HoverIndex := NoColumn; - Header.FColumns.CheckBoxHit := False; - end; - DoMouseLeave(); - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CMMouseWheel(var Message: TCMMouseWheel); - -var - ScrollAmount: Integer; - ScrollLines: DWORD; - RTLFactor: Integer; - WheelFactor: Double; - -begin - StopWheelPanning; - - inherited; - - if Message.Result = 0 then - begin - with Message do - begin - Result := 1; - WheelFactor := WheelDelta / WHEEL_DELTA; - if (FRangeY > Cardinal(ClientHeight)) and (not (ssShift in ShiftState)) then - begin - // Scroll vertically if there's something to scroll... - if ssCtrl in ShiftState then - ScrollAmount := Trunc(WheelFactor * ClientHeight) - else - begin - SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, @ScrollLines, 0); - if ScrollLines = WHEEL_PAGESCROLL then - ScrollAmount := Trunc(WheelFactor * ClientHeight) - else - ScrollAmount := Integer(Trunc(WheelFactor * ScrollLines * FDefaultNodeHeight)); - end; - SetOffsetY(FOffsetY + ScrollAmount); - end - else - begin - // ...else scroll horizontally if there's something to scroll. - if UseRightToLeftAlignment then - RTLFactor := -1 - else - RTLFactor := 1; - - if ssCtrl in ShiftState then - ScrollAmount := Trunc(WheelFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth)) - else - begin - SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, @ScrollLines, 0); - ScrollAmount := Trunc(WheelFactor * ScrollLines * FHeader.Columns.GetScrollWidth); - end; - SetOffsetX(FOffsetX + RTLFactor * ScrollAmount); - end; - end; - - end; - -end; - -//---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.CMSysColorChange(var Message: TMessage); - -begin - inherited; - Message.Msg := WM_SYSCOLORCHANGE; - DefaultHandler(Message); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.TVMGetItem(var Message: TMessage); - -// Screen reader support function. The method returns information about a particular node. - -const - StateMask = TVIS_STATEIMAGEMASK or TVIS_OVERLAYMASK or TVIS_EXPANDED or TVIS_DROPHILITED or TVIS_CUT or - TVIS_SELECTED or TVIS_FOCUSED; - -var - Item: PTVItemEx; - Node: PVirtualNode; - Ghosted: Boolean; - ImageIndex: TImageIndex; - R: TRect; - Text: string; -begin - // We can only return valid data if a nodes reference is given. - Item := Pointer(Message.LParam); - Message.Result := Ord(((Item.mask and TVIF_HANDLE) <> 0) and Assigned(Item.hItem)); - if Message.Result = 1 then - begin - Node := Pointer(Item.hItem); - // Child count requested? - if (Item.mask and TVIF_CHILDREN) <> 0 then - Item.cChildren := Node.ChildCount; - // Index for normal image requested? - if (Item.mask and TVIF_IMAGE) <> 0 then - begin - ImageIndex := -1; - DoGetImageIndex(Node, ikNormal, -1, Ghosted, ImageIndex); - Item.iImage := ImageIndex; - end; - // Index for selected image requested? - if (Item.mask and TVIF_SELECTEDIMAGE) <> 0 then - begin - ImageIndex := -1; - DoGetImageIndex(Node, ikSelected, -1, Ghosted, ImageIndex); - Item.iSelectedImage := ImageIndex; - end; - // State info requested? - if (Item.mask and TVIF_STATE) <> 0 then - begin - // Everything, which is possible is returned. - Item.stateMask := StateMask; - Item.state := 0; - if Node = FFocusedNode then - Item.state := Item.state or TVIS_FOCUSED; - if vsSelected in Node.States then - Item.state := Item.state or TVIS_SELECTED; - if vsCutOrCopy in Node.States then - Item.state := Item.state or TVIS_CUT; - if Node = FDropTargetNode then - Item.state := Item.state or TVIS_DROPHILITED; - if vsExpanded in Node.States then - Item.state := Item.state or TVIS_EXPANDED; - - // Construct state image and overlay image indices. They are one based, btw. - // and zero means there is no image. - ImageIndex := -1; - DoGetImageIndex(Node, ikState, -1, Ghosted, ImageIndex); - Item.state := Item.state or Byte(IndexToStateImageMask(ImageIndex + 1)); - ImageIndex := -1; - DoGetImageIndex(Node, ikOverlay, -1, Ghosted, ImageIndex); - Item.state := Item.state or Byte(IndexToOverlayMask(ImageIndex + 1)); - end; - // Node caption requested? - if (Item.mask and TVIF_TEXT) <> 0 then - begin - GetTextInfo(Node, -1, Font, R, Text); - - StrLCopy(Item.pszText, PWideChar(Text), Item.cchTextMax - 1); - Item.pszText[Length(Text)] := #0; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.TVMGetItemRect(var Message: TMessage); - -// Screen read support function. This method returns a node's display rectangle. - -var - TextOnly: Boolean; - Node: PVirtualNode; - -begin - // The lparam member is used two-way. On enter it contains a pointer to the item (node). - // On exit it is to be considered as pointer to a rectangle structure. - Node := Pointer(Pointer(Message.LParam)^); - Message.Result := Ord(IsVisible[Node]); - if Message.Result <> 0 then - begin - TextOnly := Message.WParam <> 0; - PRect(Message.LParam)^ := GetDisplayRect(Node, NoColumn, TextOnly); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.TVMGetNextItem(var Message: TMessage); - -// Screen read support function. This method returns a node depending on the requested case. - -var - Node: PVirtualNode; - -begin - // Start with a nil result. - Message.Result := 0; - Node := Pointer(Message.LParam); - case Message.WParam of - TVGN_CARET: - Message.Result := LRESULT(FFocusedNode); - TVGN_CHILD: - if Assigned(Node) then - Message.Result := LRESULT(GetFirstChild(Node)); - TVGN_DROPHILITE: - Message.Result := LRESULT(FDropTargetNode); - TVGN_FIRSTVISIBLE: - Message.Result := LRESULT(GetFirstVisible(nil, True)); - TVGN_LASTVISIBLE: - Message.Result := LRESULT(GetLastVisible(nil, True)); - TVGN_NEXT: - if Assigned(Node) then - Message.Result := LRESULT(GetNextSibling(Node)); - TVGN_NEXTVISIBLE: - if Assigned(Node) then - Message.Result := LRESULT(GetNextVisible(Node, True)); - TVGN_PARENT: - if Assigned(Node) and (Node <> FRoot) and (Node.Parent <> FRoot) then - Message.Result := LRESULT(Node.Parent); - TVGN_PREVIOUS: - if Assigned(Node) then - Message.Result := LRESULT(GetPreviousSibling(Node)); - TVGN_PREVIOUSVISIBLE: - if Assigned(Node) then - Message.Result := LRESULT(GetPreviousVisible(Node, True)); - TVGN_ROOT: - Message.Result := LRESULT(GetFirst); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMCancelMode(var Message: TWMCancelMode); - -begin - // Clear any transient state. - StopTimer(ExpandTimer); - StopTimer(EditTimer); - StopTimer(HeaderTimer); - StopTimer(ScrollTimer); - StopTimer(SearchTimer); - StopTimer(ThemeChangedTimer); - FSearchBuffer := ''; - FLastSearchNode := nil; - - DoStateChange([], [tsClearPending, tsEditPending, tsOLEDragPending, tsVCLDragPending, tsDrawSelecting, - tsDrawSelPending, tsIncrementalSearching]); - - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMChar(var Message: TWMChar); - -begin - if tsIncrementalSearchPending in FStates then - begin - HandleIncrementalSearch(Message.CharCode); - DoStateChange([], [tsIncrementalSearchPending]); - end; - - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMContextMenu(var Message: TWMContextMenu); - -// This method is called when a popup menu is about to be displayed. -// We have to cancel some pending states here to avoid interferences. - -begin - DoStateChange([], [tsClearPending, tsEditPending, tsOLEDragPending, tsVCLDragPending]); - - if not (tsPopupMenuShown in FStates) then - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMCopy(var Message: TWMCopy); - -begin - CopyToClipboard; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMCut(var Message: TWMCut); - -begin - CutToClipboard; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMEnable(var Message: TWMEnable); - -begin - inherited; - RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMEraseBkgnd(var Message: TWMEraseBkgnd); - -begin - Message.Result := 1; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMGetDlgCode(var Message: TWMGetDlgCode); - -begin - Message.Result := DLGC_WANTCHARS or DLGC_WANTARROWS; - if FWantTabs then - Message.Result := Message.Result or DLGC_WANTTAB; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMGetObject(var Message: TMessage); - -begin - if TVTAccessibilityFactory.GetAccessibilityFactory <> nil then - begin - // Create the IAccessibles for the tree view and tree view items, if necessary. - if FAccessible = nil then - FAccessible := TVTAccessibilityFactory.GetAccessibilityFactory.CreateIAccessible(Self); - if FAccessibleItem = nil then - FAccessibleItem := TVTAccessibilityFactory.GetAccessibilityFactory.CreateIAccessible(Self); - if Cardinal(Message.LParam) = OBJID_CLIENT then - if Assigned(Accessible) then - Message.Result := LresultFromObject(IID_IAccessible, Message.WParam, FAccessible) - else - Message.Result := 0; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMHScroll(var Message: TWMHScroll); - - //--------------- local functions ------------------------------------------- - - function GetRealScrollPosition: Integer; - - var - SI: TScrollInfo; - Code: Integer; - - begin - SI.cbSize := SizeOf(TScrollInfo); - SI.fMask := SIF_TRACKPOS; - Code := SB_HORZ; - GetScrollInfo(Handle, Code, SI); - Result := SI.nTrackPos; - end; - - //--------------- end local functions --------------------------------------- - -var - RTLFactor: Integer; - -begin - if UseRightToLeftAlignment then - RTLFactor := -1 - else - RTLFactor := 1; - - case Message.ScrollCode of - SB_BOTTOM: - SetOffsetX(-Integer(FRangeX)); - SB_ENDSCROLL: - begin - DoStateChange([], [tsThumbTracking]); - // avoiding to adjust the vertical scroll position while tracking makes it much smoother - // but we need to adjust the final position here then - UpdateHorizontalScrollBar(False); - end; - SB_LINELEFT: - SetOffsetX(FOffsetX + RTLFactor * FScrollBarOptions.HorizontalIncrement); - SB_LINERIGHT: - SetOffsetX(FOffsetX - RTLFactor * FScrollBarOptions.VerticalIncrement); - SB_PAGELEFT: - SetOffsetX(FOffsetX + RTLFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth)); - SB_PAGERIGHT: - SetOffsetX(FOffsetX - RTLFactor * (ClientWidth - FHeader.Columns.GetVisibleFixedWidth)); - SB_THUMBPOSITION, - SB_THUMBTRACK: - begin - DoStateChange([tsThumbTracking]); - if UseRightToLeftAlignment then - SetOffsetX(-Integer(FRangeX) + ClientWidth + GetRealScrollPosition) - else - SetOffsetX(-GetRealScrollPosition); - end; - SB_TOP: - SetOffsetX(0); - end; - - Message.Result := 0; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMKeyDown(var Message: TWMKeyDown); - -// Keyboard event handling for node focus, selection, node specific popup menus and help invokation. -// For a detailed description of every action done here read the help. - -var - Shift: TShiftState; - Node, Temp, - LastFocused: PVirtualNode; - Offset: Integer; - ClearPending, - NeedInvalidate, - DoRangeSelect, - PerformMultiSelect: Boolean; - Context: Integer; - ParentControl: TWinControl; - R: TRect; - NewCheckState: TCheckState; - TempColumn, - NewColumn: TColumnIndex; - ActAsGrid: Boolean; - ForceSelection: Boolean; - NewWidth, - NewHeight: Integer; - RTLFactor: Integer; - - // for tabulator handling - GetStartColumn: function(ConsiderAllowFocus: Boolean = False): TColumnIndex of object; - GetNextColumn: function(Column: TColumnIndex; ConsiderAllowFocus: Boolean = False): TColumnIndex of object; - GetNextNode: TGetNextNodeProc; - - KeyState: TKeyboardState; - Buffer: array[0..1] of AnsiChar; - - //--------------- local functions ------------------------------------------- - function getPreviousVisibleAutoSpanColumn(acolumn: TColumnIndex; anode: PVirtualNode): TColumnIndex; - var - PrevColumn: Integer; - begin - if (not assigned(anode)) - or (not FHeader.UseColumns) - or (not (toAutoSpanColumns in FOptions.AutoOptions)) - or (acolumn = FHeader.MainColumn) then - begin - //previously existing logic - result := FHeader.Columns.GetPreviousVisibleColumn(acolumn, True); - exit; - end; - //consider auto spanning - with FHeader.Columns do //standard loop for auto span - begin - PrevColumn := acolumn; - repeat - result := FHeader.Columns.GetPreviousVisibleColumn(PrevColumn); - if (result = InvalidColumn) or - (not ColumnIsEmpty(anode, result)) - //Any other BidiMode is not supported as already - //documented by original developer - or (Items[result].BidiMode <> bdLeftToRight) then - Break; - PrevColumn := result; - until False; - end; - end; - - //--------------------------------------------------------------------------- - function getNextVisibleAutoSpanColumn(acolumn: TColumnIndex; anode: PVirtualNode): TColumnIndex; - var - NextColumn: Integer; - begin - if (not assigned(anode)) - or (not FHeader.UseColumns) - or (not (toAutoSpanColumns in FOptions.AutoOptions)) - or (acolumn = FHeader.MainColumn) then - begin - //previously existing logic - result := FHeader.Columns.GetNextVisibleColumn(acolumn, True); - exit; - end; - //consider auto spanning - with FHeader.Columns do //standard loop for auto span - begin - NextColumn := acolumn; - repeat - result := FHeader.Columns.GetNextVisibleColumn(NextColumn); - if (result = InvalidColumn) or - not ColumnIsEmpty(anode, result) - //Any other BidiMode is not supported as already - //documented by original developer - or (Items[result].BidiMode <> bdLeftToRight) then - Break; - NextColumn := result; - until False; - end; - end; - - //--------------------------------------------------------------------------- - function isEmptyAutoSpanColumn(acolumn: TColumnIndex; anode: PVirtualNode): boolean; - var - previousColumn: Integer; - begin - result := false; - if (not assigned(anode)) - or (not FHeader.UseColumns) - or (not (toAutoSpanColumns in FOptions.AutoOptions)) - or (acolumn = FHeader.MainColumn) then - exit; - with FHeader.Columns do - begin - previousColumn := FHeader.Columns.GetPreviousVisibleColumn(acolumn); - if (previousColumn = InvalidColumn) //there is no previous column - //Any other BidiMode is not supported as already - //documented by original developer - or (Items[acolumn].BidiMode <> bdLeftToRight) then - exit; //returning false - result := ColumnIsEmpty(anode, acolumn); - end; - end; - - - //--------------- end local functions --------------------------------------- - -begin - // Make form key preview work and let application modify the key if it wants this. - inherited; - - with Message do - begin - Shift := KeyDataToShiftState(KeyData); - // Ask the application if the default key handling is desired. - if DoKeyAction(CharCode, Shift) then - begin - if (CharCode in [VK_HOME, VK_END, VK_PRIOR, VK_NEXT, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_BACK, VK_TAB]) and (RootNode.FirstChild <> nil) then - begin - PerformMultiSelect := (ssShift in Shift) and (toMultiSelect in FOptions.SelectionOptions) and not IsEditing; - - // Flag to avoid range selection in case of single node advance. - DoRangeSelect := (CharCode in [VK_HOME, VK_END, VK_PRIOR, VK_NEXT]) and PerformMultiSelect and not IsEditing; - - NeedInvalidate := DoRangeSelect or (FSelectionCount > 1); - ActAsGrid := toGridExtensions in FOptions.MiscOptions; - ClearPending := (Shift = []) or (ActAsGrid and not (ssShift in Shift)) or - not (toMultiSelect in FOptions.SelectionOptions) or (CharCode in [VK_TAB, VK_BACK]); - - // Keep old focused node for range selection. Use a default node if none was focused until now. - LastFocused := FFocusedNode; - if (LastFocused = nil) and (Shift <> []) then - LastFocused := GetFirstVisible(nil, True); - - // Set an initial range anchor if there is not yet one. - if FRangeAnchor = nil then - FRangeAnchor := GetFirstSelected; - if FRangeAnchor = nil then - FRangeAnchor := GetFirst; - - if UseRightToLeftAlignment then - RTLFactor := -1 - else - RTLFactor := 1; - - // Determine new focused node. - case CharCode of - VK_HOME, VK_END: - begin - if (CharCode = VK_END) xor UseRightToLeftAlignment then - begin - GetStartColumn := FHeader.Columns.GetLastVisibleColumn; - GetNextColumn := FHeader.Columns.GetPreviousVisibleColumn; - GetNextNode := GetPreviousVisible; - Node := GetLastVisible(nil, True); - end - else - begin - GetStartColumn := FHeader.Columns.GetFirstVisibleColumn; - GetNextColumn := FHeader.Columns.GetNextVisibleColumn; - GetNextNode := GetNextVisible; - Node := GetFirstVisible(nil, True); - end; - - // Advance to next/previous visible column. - if FHeader.UseColumns then - NewColumn := GetStartColumn - else - NewColumn := NoColumn; - // Find a column for the new/current node which can be focused. - // Make the 'DoFocusChanging' for finding a valid column - // identifiable from the 'DoFocusChanging' raised later on by - // "FocusedNode := Node;" - while (NewColumn > NoColumn) and not DoFocusChanging(FFocusedNode, FFocusedNode, FFocusedColumn, NewColumn) do - NewColumn := GetNextColumn(NewColumn); - if NewColumn > InvalidColumn then - begin - if (Shift = [ssCtrl]) and not ActAsGrid then - begin - ScrollIntoView(Node, toCenterScrollIntoView in FOptions.SelectionOptions, - not (toDisableAutoscrollOnFocus in FOptions.AutoOptions)); - if (CharCode = VK_HOME) and not UseRightToLeftAlignment then - SetOffsetX(0) - else - SetOffsetX(-MaxInt); - end - else - begin - if not ActAsGrid or (ssCtrl in Shift) then - FocusedNode := Node; - //fix: In grid mode, if full row select option is ON, - //then also go to the node determined from the earlier logic - if ActAsGrid and (toFullRowSelect in FOptions.SelectionOptions) then - FocusedNode := Node; - if ActAsGrid and not (toFullRowSelect in FOptions.SelectionOptions) then - begin - FocusedColumn := NewColumn; - // fix: If auto span is ON the last column may be a merged column. So take - // care of selecting the whole merged column on END key. - if (CharCode = VK_END) and isEmptyAutoSpanColumn(NewColumn, FFocusedNode) then - FocusedColumn := getPreviousVisibleAutoSpanColumn(NewColumn, FFocusedNode); - end; - end; - end; - end; - VK_PRIOR: - if Shift = [ssCtrl, ssShift] then - SetOffsetX(FOffsetX + ClientWidth) - else - if [ssShift, ssAlt] = Shift then - begin - if FFocusedColumn <= NoColumn then - NewColumn := FHeader.Columns.GetFirstVisibleColumn - else - begin - Offset := FHeader.Columns.GetVisibleFixedWidth; - NewColumn := FFocusedColumn; - while True do - begin - TempColumn := FHeader.Columns.GetPreviousVisibleColumn(NewColumn); - NewWidth := FHeader.Columns[NewColumn].Width; - if (TempColumn <= NoColumn) or - (Offset + NewWidth >= ClientWidth) or - (coFixed in FHeader.Columns[TempColumn].Options) then - Break; - NewColumn := TempColumn; - Inc(Offset, NewWidth); - end; - end; - SetFocusedColumn(NewColumn); - end - else - if ssCtrl in Shift then - SetOffsetY(FOffsetY + ClientHeight) - else - begin - Offset := 0; - // If there's no focused node then just take the very first visible one. - if FFocusedNode = nil then - Node := GetFirstVisible(nil, True) - else - begin - // Go up as many nodes as comprise together a size of ClientHeight. - Node := FFocusedNode; - while True do - begin - Temp := GetPreviousVisible(Node, True); - NewHeight := NodeHeight[Node]; - if (Temp = nil) or (Offset + NewHeight >= ClientHeight) then - Break; - Node := Temp; - Inc(Offset, NodeHeight[Node]); - end; - end; - FocusedNode := Node; - end; - VK_NEXT: - if Shift = [ssCtrl, ssShift] then - SetOffsetX(FOffsetX - ClientWidth) - else - if [ssShift, ssAlt] = Shift then - begin - if FFocusedColumn <= NoColumn then - NewColumn := FHeader.Columns.GetFirstVisibleColumn - else - begin - Offset := FHeader.Columns.GetVisibleFixedWidth; - NewColumn := FFocusedColumn; - while True do - begin - TempColumn := FHeader.Columns.GetNextVisibleColumn(NewColumn); - NewWidth := FHeader.Columns[NewColumn].Width; - if (TempColumn <= NoColumn) or - (Offset + NewWidth >= ClientWidth) or - (coFixed in FHeader.Columns[TempColumn].Options) then - Break; - NewColumn := TempColumn; - Inc(Offset, NewWidth); - end; - end; - SetFocusedColumn(NewColumn); - end - else - if ssCtrl in Shift then - SetOffsetY(FOffsetY - ClientHeight) - else - begin - Offset := 0; - // If there's no focused node then just take the very last one. - if FFocusedNode = nil then - Node := GetLastVisible(nil, True) - else - begin - // Go up as many nodes as comprise together a size of ClientHeight. - Node := FFocusedNode; - while True do - begin - Temp := GetNextVisible(Node, True); - NewHeight := NodeHeight[Node]; - if (Temp = nil) or (Offset + NewHeight >= ClientHeight) then - Break; - Node := Temp; - Inc(Offset, NewHeight); - end; - end; - FocusedNode := Node; - end; - VK_UP: - begin - // scrolling without selection change - if ssCtrl in Shift then - SetOffsetY(FOffsetY + Integer(FDefaultNodeHeight)) - else - begin - if FFocusedNode = nil then - Node := GetLastVisible(nil, True) - else - Node := GetPreviousVisible(FFocusedNode, True); - - if Assigned(Node) then - begin - if not EndEditNode then - exit; - if (not PerformMultiSelect or (CompareNodePositions(LastFocused, Node) < -1)) and Assigned(FFocusedNode) then - ClearSelection(False); // Clear selection only if more than one node was skipped. See issue #926 - if FFocusedColumn <= NoColumn then - FFocusedColumn := FHeader.MainColumn; - FocusedNode := Node; - end - else - if Assigned(FFocusedNode) then - InvalidateNode(FFocusedNode); - end; - end; - VK_DOWN: - begin - // scrolling without selection change - if ssCtrl in Shift then - SetOffsetY(FOffsetY - Integer(FDefaultNodeHeight)) - else - begin - if FFocusedNode = nil then - Node := GetFirstVisible(nil, True) - else - Node := GetNextVisible(FFocusedNode, True); - - if Assigned(Node) then - begin - if not EndEditNode then - exit; - if (not PerformMultiSelect or (CompareNodePositions(LastFocused, Node) > 1)) and Assigned(FFocusedNode) then - ClearSelection(False); // Clear selection only if more than one node was skipped. See issue #926 - if FFocusedColumn <= NoColumn then - FFocusedColumn := FHeader.MainColumn; - FocusedNode := Node; - end - else - if Assigned(FFocusedNode) then - InvalidateNode(FFocusedNode); - end; - end; - VK_LEFT: - begin - // special handling - if ssCtrl in Shift then - SetOffsetX(FOffsetX + RTLFactor * FHeader.Columns.GetScrollWidth) - else - begin - // other special cases - Context := NoColumn; - if (toExtendedFocus in FOptions.SelectionOptions) and (toGridExtensions in FOptions.MiscOptions) then - begin - Context := getPreviousVisibleAutoSpanColumn(FFocusedColumn, FFocusedNode); - if Context > NoColumn then - FocusedColumn := Context; - end - else - if Assigned(FFocusedNode) and (vsExpanded in FFocusedNode.States) and - (Shift = []) and (vsHasChildren in FFocusedNode.States) then - ToggleNode(FFocusedNode) - else - begin - if FFocusedNode = nil then - FocusedNode := GetFirstVisible(nil, True) - else - begin - if FFocusedNode.Parent <> FRoot then - Node := FFocusedNode.Parent - else - Node := nil; - if Assigned(Node) then - begin - if PerformMultiSelect then - begin - // and a third special case - if FFocusedNode.Index > 0 then - DoRangeSelect := True - else - if CompareNodePositions(Node, FRangeAnchor) > 0 then - RemoveFromSelection(FFocusedNode); - end; - FocusedNode := Node; - end - else begin - // If already a root node is selected, then scroll to the left as there is nothing else we could do. #691 - SetOffsetX(FOffsetX + RTLFactor * FHeader.Columns.GetScrollWidth); - end;//else - end; - end; - end; - end; - VK_RIGHT: - begin - // special handling - if ssCtrl in Shift then - SetOffsetX(FOffsetX - RTLFactor * FHeader.Columns.GetScrollWidth) - else - begin - // other special cases - Context := NoColumn; - if (toExtendedFocus in FOptions.SelectionOptions) and (toGridExtensions in FOptions.MiscOptions) then - begin - Context := getNextVisibleAutoSpanColumn(FFocusedColumn, FFocusedNode); - if Context > NoColumn then - FocusedColumn := Context; - end - else - if Assigned(FFocusedNode) and not (vsExpanded in FFocusedNode.States) and - (Shift = []) and (vsHasChildren in FFocusedNode.States) then - ToggleNode(FFocusedNode) - else - begin - if FFocusedNode = nil then - FocusedNode := GetFirstVisible(nil, True) - else - begin - Node := GetFirstVisibleChild(FFocusedNode); - if Assigned(Node) then - begin - if PerformMultiSelect and (CompareNodePositions(Node, FRangeAnchor) < 0) then - RemoveFromSelection(FFocusedNode); - FocusedNode := Node; - end - else begin - // If already a leaf node is selected, then scroll to the right as there is nothing else we could do. #691 - SetOffsetX(FOffsetX - RTLFactor * FHeader.Columns.GetScrollWidth); - end;//else - end; - end; - end; - end; - VK_BACK: - if tsIncrementalSearching in FStates then - DoStateChange([tsIncrementalSearchPending]) - else - if Assigned(FFocusedNode) and (FFocusedNode.Parent <> FRoot) then - FocusedNode := FocusedNode.Parent; - VK_TAB: - if (toExtendedFocus in FOptions.SelectionOptions) and FHeader.UseColumns then - begin - // In order to avoid duplicating source code just to change the direction - // we use function variables. - if ssShift in Shift then - begin - GetStartColumn := FHeader.Columns.GetLastVisibleColumn; - GetNextColumn := FHeader.Columns.GetPreviousVisibleColumn; - GetNextNode := GetPreviousVisible; - end - else - begin - GetStartColumn := FHeader.Columns.GetFirstVisibleColumn; - GetNextColumn := FHeader.Columns.GetNextVisibleColumn; - GetNextNode := GetNextVisible; - end; - - // Advance to next/previous visible column/node. - Node := FFocusedNode; - NewColumn := GetNextColumn(FFocusedColumn, True); - repeat - // Find a column for the current node which can be focused. - while (NewColumn > NoColumn) and not DoFocusChanging(FFocusedNode, Node, FFocusedColumn, NewColumn) - //Fix: for Tab Key to properly skip the empty auto span column - or isEmptyAutoSpanColumn(NewColumn, Node) do - NewColumn := GetNextColumn(NewColumn, True); - - if NewColumn > NoColumn then - begin - // Set new node and column in one go. - SetFocusedNodeAndColumn(Node, NewColumn); - Break; - end; - - // No next column was accepted for the current node. So advance to next node and try again. - Node := GetNextNode(Node); - NewColumn := GetStartColumn; - - // fix: From last column, the Tab key should always go to next row irrespective of auto span - // Similarly the Shift-Tab key should go to previos row from first column - if (Node <> nil) and (NewColumn > NoColumn) then - SetFocusedNodeAndColumn(Node, NewColumn); - - until Node = nil; - end; - end; - - // Clear old selection if required but take care to select the new focused node if it was not selected before. - ForceSelection := False; - if ClearPending and ((LastFocused <> FFocusedNode) or (FSelectionCount <> 1)) then - begin - ClearSelection(not Assigned(FFocusedNode)); - ForceSelection := True; - end; - - // Determine new selection anchor. - if Shift = [] then - begin - FRangeAnchor := FFocusedNode; - FLastSelectionLevel := GetNodeLevelForSelectConstraint(FFocusedNode); - end; - - if Assigned(FFocusedNode) then - begin - // Finally change the selection for a specific range of nodes. - if DoRangeSelect then - ToggleSelection(LastFocused, FFocusedNode) - // Make sure the new focused node is also selected. - else if (LastFocused <> FFocusedNode) then begin - if ForceSelection then - AddToSelection(FFocusedNode, False) - else - ToggleSelection(LastFocused, FFocusedNode); // See issue #926 - end; - end; - - // If a repaint is needed then paint the entire tree because of the ClearSelection call, - if NeedInvalidate then - Invalidate; - end - else - begin - // Second chance for keys not directly concerned with selection changes. - - // For +, -, /, * keys on the main keyboard (not numpad) there is no virtual key code defined. - // We have to do special processing to get them working too. - GetKeyboardState(KeyState); - // Avoid conversion to control characters. We have captured the control key state already in Shift. - KeyState[VK_CONTROL] := 0; - if ToASCII(Message.CharCode, (Message.KeyData shr 16) and 7, KeyState, PChar(@Buffer), 0) > 0 then - begin - case Buffer[0] of - '*': - CharCode := VK_MULTIPLY; - '+': - CharCode := VK_ADD; - '/': - CharCode := VK_DIVIDE; - '-': - CharCode := VK_SUBTRACT; - end; - end; - - // According to https://web.archive.org/web/20041129085958/http://www.it-faq.pl/mskb/99/337.HTM - // there is a problem with ToASCII when used in conjunction with dead chars. - // The article recommends to call ToASCII twice to restore a deleted flag in the key message - // structure under certain circumstances. It turned out it is best to always call ToASCII twice. - ToASCII(Message.CharCode, (Message.KeyData shr 16) and 7, KeyState, PChar(@Buffer), 0); - - case CharCode of - VK_F2: - if (Shift = []) and Assigned(FFocusedNode) and CanEdit(FFocusedNode, FFocusedColumn) then - begin - FEditColumn := FFocusedColumn; - DoEdit; - end; - VK_ADD: - if not (tsIncrementalSearching in FStates) then - begin - if ssCtrl in Shift then begin// When changing this code review issue #781 - if ((toReverseFullExpandHotKey in TreeOptions.MiscOptions) and (Shift = [ssCtrl])) xor (Shift = [ssCtrl, ssShift]) then - FullExpand - else if Shift = [ssCtrl] then - FHeader.AutoFitColumns - end - else if Shift = [] then begin - if Assigned(FFocusedNode) and not (vsExpanded in FFocusedNode.States) then - ToggleNode(FFocusedNode); - end// if Shift = [] - else - DoStateChange([tsIncrementalSearchPending]); - end;//if not (tsIncrementalSearching in FStates) - VK_SUBTRACT: - if not (tsIncrementalSearching in FStates) then - begin - if ssCtrl in Shift then - if (toReverseFullExpandHotKey in TreeOptions.MiscOptions) xor (ssShift in Shift) then - FullCollapse - else - FHeader.RestoreColumns - else - if Assigned(FFocusedNode) and (vsExpanded in FFocusedNode.States) then - ToggleNode(FFocusedNode); - end - else - DoStateChange([tsIncrementalSearchPending]); - VK_MULTIPLY: - if not (tsIncrementalSearching in FStates) then - begin - if Assigned(FFocusedNode) then - FullExpand(FFocusedNode); - end - else - DoStateChange([tsIncrementalSearchPending]); - VK_DIVIDE: - if not (tsIncrementalSearching in FStates) then - begin - if Assigned(FFocusedNode) then - FullCollapse(FFocusedNode); - end - else - DoStateChange([tsIncrementalSearchPending]); - VK_ESCAPE: // cancel actions currently in progress - begin - if IsMouseSelecting then - begin - DoStateChange([], [tsDrawSelecting, tsDrawSelPending]); - Invalidate; - end - else - if IsEditing then - CancelEditNode; - end; - VK_SPACE: - if (toCheckSupport in FOptions.MiscOptions) and Assigned(FFocusedNode) and - (FFocusedNode.CheckType <> ctNone) then - begin - NewCheckState := DetermineNextCheckState(FFocusedNode.CheckType, GetCheckState(FFocusedNode)); - if DoChecking(FFocusedNode, NewCheckState) then - begin - if SelectedCount > 1 then - SetCheckStateForAll(NewCheckState, True) - else - DoCheckClick(FFocusedNode, NewCheckState); - end; - end - else - DoStateChange([tsIncrementalSearchPending]); - VK_F1: - if Assigned(FOnGetHelpContext) then - begin - Context := 0; - if Assigned(FFocusedNode) then - begin - Node := FFocusedNode; - // Traverse the tree structure up to the root. - repeat - FOnGetHelpContext(Self, Node, IfThen(FFocusedColumn > NoColumn, FFocusedColumn, 0), Context); - Node := Node.Parent; - until (Node = FRoot) or (Context <> 0); - end; - - // If no help context could be found try the tree's one or its parent's contexts. - ParentControl := Self; - while Assigned(ParentControl) and (Context = 0) do - begin - Context := ParentControl.HelpContext; - ParentControl := ParentControl.Parent; - end; - if Context <> 0 then - Application.HelpContext(Context); - end; - VK_APPS: - if Assigned(FFocusedNode) then - begin - R := GetDisplayRect(FFocusedNode, FFocusedColumn, True); - Offset := DoGetNodeWidth(FFocusedNode, FFocusedColumn); - if FFocusedColumn >= 0 then - begin - if Offset > FHeader.Columns[FFocusedColumn].Width then - Offset := FHeader.Columns[FFocusedColumn].Width; - end - else - begin - if Offset > ClientWidth then - Offset := ClientWidth; - end; - DoPopupMenu(FFocusedNode, FFocusedColumn, Point(R.Left + Offset div 2, (R.Top + R.Bottom) div 2)); - end - else - DoPopupMenu(nil, FFocusedColumn, Point(-1, -1)); - Ord('a'), Ord('A'): - if ssCtrl in Shift then - SelectAll(True) - else - DoStateChange([tsIncrementalSearchPending]); - else - begin - // Use the key for incremental search. - // Since we are dealing with Unicode all the time there should be a more sophisticated way - // of checking for valid characters for incremental search. - // This is available but would require to include a significant amount of Unicode character - // properties, so we stick with the simple space check. - if ((Shift * [ssCtrl, ssAlt] = []) or ((Shift * [ssCtrl, ssAlt] = [ssCtrl, ssAlt]))) and (CharCode >= 32) then - DoStateChange([tsIncrementalSearchPending]); - end; - end; - end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMKeyUp(var Message: TWMKeyUp); - -begin - inherited; - - case Message.CharCode of - VK_TAB: - EnsureNodeFocused(); // Always select a node if the control gets the focus via TAB key, #237 - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMKillFocus(var Msg: TWMKillFocus); - -var - Form: TCustomForm; - Control: TWinControl; - Pos: TSmallPoint; - Unknown: IUnknown; - -begin - inherited; - - // Remove hint if shown currently. - if tsHint in Self.FStates then - Application.CancelHint; - - // Stop wheel panning if active. - StopWheelPanning; - - // Don't let any timer continue if the tree is no longer the active control (except change timers). - StopTimer(ExpandTimer); - StopTimer(EditTimer); - StopTimer(HeaderTimer); - StopTimer(ScrollTimer); - StopTimer(SearchTimer); - FSearchBuffer := ''; - FLastSearchNode := nil; - - DoStateChange([], [tsScrollPending, tsScrolling, tsEditPending, tsLeftButtonDown, tsRightButtonDown, - tsMiddleButtonDown, tsOLEDragPending, tsVCLDragPending, tsIncrementalSearching, tsNodeHeightTrackPending, - tsNodeHeightTracking]); - - if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.PaintOptions) then - Invalidate - else - if Assigned(FFocusedNode) then - InvalidateNode(FFocusedNode); - - // Workaround for wrapped non-VCL controls (like TWebBrowser), which do not use VCL mechanisms and - // leave the ActiveControl property in the wrong state, which causes trouble when the control is refocused. - Form := GetParentForm(Self); - if Assigned(Form) and (Form.ActiveControl = Self) then - begin - Cardinal(Pos) := GetMessagePos; - Control := FindVCLWindow(SmallPointToPoint(Pos)); - // Every control derived from TOleControl has potentially the focus problem. In order to avoid including - // the OleCtrls unit (which will, among others, include Variants), which would allow to test for the TOleControl - // class, the IOleClientSite interface is used for the test, which is supported by TOleControl and a good indicator. - if Assigned(Control) and Control.GetInterface(IOleClientSite, Unknown) then - Form.ActiveControl := nil; - - // For other classes the active control should not be modified. Otherwise you need two clicks to select it. - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMLButtonDblClk(var Message: TWMLButtonDblClk); - -var - HitInfo: THitInfo; - -begin - DoStateChange([tsLeftDblClick]); - try - // get information about the hit, before calling inherited, is this may change the scroll postion and so the node under the mouse would chnage and would no longer be the one the user actually clicked - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - HandleMouseDblClick(Message, HitInfo); - // Call inherited after doing our standard handling, as the event handler may close the form or re-fill the control, so our clicked node would be no longer valid. - // Our standard handling does not do that. - inherited; - // #909 - // if we show a modal form in the HandleMouseDblClick(), the mouse capture wont be released - if csCaptureMouse in ControlStyle then MouseCapture := False; - finally - DoStateChange([], [tsLeftDblClick]); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMLButtonDown(var Message: TWMLButtonDown); - -var - HitInfo: THitInfo; - -begin - DoStateChange([tsLeftButtonDown]); - inherited; - - // get information about the hit - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - HandleMouseDown(Message, HitInfo); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMLButtonUp(var Message: TWMLButtonUp); - -var - HitInfo: THitInfo; - -begin - DoStateChange([], [tsLeftButtonDown, tsNodeHeightTracking, tsNodeHeightTrackPending]); - - // get information about the hit - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - HandleMouseUp(Message, HitInfo); - - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMMButtonDblClk(var Message: TWMMButtonDblClk); - -var - HitInfo: THitInfo; - -begin - DoStateChange([tsMiddleDblClick]); - inherited; - - // get information about the hit - if toMiddleClickSelect in FOptions.SelectionOptions then - begin - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - HandleMouseDblClick(Message, HitInfo); - end; - DoStateChange([], [tsMiddleDblClick]); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMMButtonDown(var Message: TWMMButtonDown); - -var - HitInfo: THitInfo; - -begin - DoStateChange([tsMiddleButtonDown]); - - if FHeader.States = [] then - begin - inherited; - - // Start wheel panning or scrolling if not already active, allowed and scrolling is useful at all. - if (toWheelPanning in FOptions.MiscOptions) and ([tsWheelScrolling, tsWheelPanning] * FStates = []) and - ((Integer(FRangeX) > ClientWidth) or (Integer(FRangeY) > ClientHeight)) then - begin - FLastClickPos := SmallPointToPoint(Message.Pos); - StartWheelPanning(FLastClickPos); - end - else - begin - StopWheelPanning; - - // Get information about the hit. - if toMiddleClickSelect in FOptions.SelectionOptions then - begin - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - HandleMouseDown(Message, HitInfo); - end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMMButtonUp(var Message: TWMMButtonUp); - -var - HitInfo: THitInfo; - -begin - DoStateChange([], [tsMiddleButtonDown]); - - // If wheel panning/scrolling is active and the mouse has not yet been moved then the user starts wheel auto scrolling. - // Indicate this by removing the panning flag. Otherwise (the mouse has moved meanwhile) stop panning. - if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then - begin - if tsWheelScrolling in FStates then - DoStateChange([], [tsWheelPanning]) - else - StopWheelPanning; - end - else - if FHeader.States = [] then - begin - inherited; - - // get information about the hit - if toMiddleClickSelect in FOptions.SelectionOptions then - begin - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - HandleMouseUp(Message, HitInfo); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMNCCalcSize(var Message: TWMNCCalcSize); - -begin - inherited; - - with FHeader do - if hoVisible in FHeader.Options then - with Message.CalcSize_Params^ do - Inc(rgrc[0].Top, FHeight); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMNCDestroy(var Message: TWMNCDestroy); - -// Used to release a reference of the drag manager. This is the only reliable way we get notified about -// window destruction, because of the automatic release of a window if its parent window is freed. - -begin - InterruptValidation; - - StopTimer(ChangeTimer); - StopTimer(StructureChangeTimer); - - if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.MiscOptions) then - RevokeDragDrop(Handle); - - // Clean up other stuff. - DeleteObject(FDottedBrush); - FDottedBrush := 0; - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMNCHitTest(var Message: TWMNCHitTest); - -begin - inherited; - if (hoVisible in FHeader.Options) and - FHeader.InHeader(ScreenToClient(SmallPointToPoint(Message.Pos))) then - Message.Result := HTBORDER; -end; - -//---------------------------------------------------------------------------------------------------------------------- - - -procedure TBaseVirtualTree.WMNCPaint(var Message: TWMNCPaint); - -var - DC: HDC; - R: TRect; - Flags: DWORD; - ExStyle: Integer; - TempRgn: HRGN; - BorderWidth, - BorderHeight: Integer; - -begin - if tsUseThemes in FStates then - begin - // If theming is enabled and the client edge border is set for the window then prevent the default window proc - // from painting the old border to avoid flickering. - ExStyle := GetWindowLong(Handle, GWL_EXSTYLE); - if (ExStyle and WS_EX_CLIENTEDGE) <> 0 then - begin - GetWindowRect(Handle, R); - // Determine width of the client edge. - BorderWidth := GetSystemMetrics(SM_CXEDGE); - BorderHeight := GetSystemMetrics(SM_CYEDGE); - InflateRect(R, -BorderWidth, -BorderHeight); - TempRgn := CreateRectRgnIndirect(R); - // Exclude the border from the message region if there is one. Otherwise just use the inflated - // window area region. - if Message.Rgn <> 1 then - CombineRgn(TempRgn, Message.Rgn, TempRgn, RGN_AND); - DefWindowProc(Handle, Message.Msg, WPARAM(TempRgn), 0); - DeleteObject(TempRgn); - end - else - DefaultHandler(Message); - end - else - DefaultHandler(Message); - - Flags := DCX_CACHE or DCX_CLIPSIBLINGS or DCX_WINDOW or DCX_VALIDATE; - - if (Message.Rgn = 1) then - DC := GetDCEx(Handle, 0, Flags) - else - DC := GetDCEx(Handle, Message.Rgn, Flags or DCX_INTERSECTRGN); - - if DC <> 0 then - try - OriginalWMNCPaint(DC); - finally - ReleaseDC(Handle, DC); - end; - if (((tsUseThemes in FStates) and not VclStyleEnabled) or (VclStyleEnabled and (seBorder in StyleElements))) then - StyleServices.PaintBorder(Self, False) - else - if (VclStyleEnabled and not (seBorder in StyleElements)) then - TStyleManager.SystemStyle.PaintBorder(Self, False) -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMPaint(var Message: TWMPaint); -var - DC: HDC; -begin - if tsVCLDragging in FStates then - ImageList_DragShowNolock(False); - if csPaintCopy in ControlState then - FUpdateRect := ClientRect - else - GetUpdateRect(Handle, FUpdateRect, True); - - inherited; - - if tsVCLDragging in FStates then - ImageList_DragShowNolock(True); - - if hoVisible in FHeader.Options then - begin - DC := GetDCEx(Handle, 0, DCX_CACHE or DCX_CLIPSIBLINGS or DCX_WINDOW or DCX_VALIDATE); - if DC <> 0 then - try - FHeader.Columns.PaintHeader(DC, FHeaderRect, -FEffectiveOffsetX); - finally - ReleaseDC(Handle, DC); - end; - end;//if header visible -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMPaste(var Message: TWMPaste); - -begin - PasteFromClipboard; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMPrint(var Message: TWMPrint); - -// This message is sent to request that the tree draws itself to a given device context. This includes not only -// the client area but also the non-client area (header!). - -begin - // Draw only if the window is visible or visibility is not required. - if ((Message.Flags and PRF_CHECKVISIBLE) = 0) or IsWindowVisible(Handle) then - Header.Columns.PaintHeader(Message.DC, FHeaderRect, -FEffectiveOffsetX); - - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMPrintClient(var Message: TWMPrintClient); - -var - Window: TRect; - Target: TPoint; - Canvas: TCanvas; - -begin - // Draw only if the window is visible or visibility is not required. - if ((Message.Flags and PRF_CHECKVISIBLE) = 0) or IsWindowVisible(Handle) then - begin - // Determine area of the entire tree to be displayed in the control. - Window := ClientRect; - Target := Window.TopLeft; - - // The Window rectangle is given in client coordinates. We have to convert it into - // a sliding window of the tree image. - OffsetRect(Window, FEffectiveOffsetX, -FOffsetY); - - Canvas := TCanvas.Create; - try - Canvas.Handle := Message.DC; - PaintTree(Canvas, Window, Target, [poBackground, poDrawFocusRect, poDrawDropMark, poDrawSelection, poGridLines]); - finally - Canvas.Handle := 0; - Canvas.Free; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMRButtonDblClk(var Message: TWMRButtonDblClk); - -var - HitInfo: THitInfo; - -begin - DoStateChange([tsRightDblClick]); - inherited; - - // get information about the hit - if toMiddleClickSelect in FOptions.SelectionOptions then - begin - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - HandleMouseDblClick(Message, HitInfo); - end; - DoStateChange([], [tsRightDblClick]); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMRButtonDown(var Message: TWMRButtonDown); - -var - HitInfo: THitInfo; - RemoveSynchMode: Boolean; // Needed to restore tsSynchMode correctly - -begin - DoStateChange([tsRightButtonDown]); - - if FHeader.States = [] then - begin - inherited; - - // get information about the hit - if toRightClickSelect in FOptions.SelectionOptions then - begin - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - // Go temporarily into sync mode to avoid a delayed change event for the node when selecting. #679 - RemoveSynchMode := not (tsSynchMode in FStates); - Include(FStates, tsSynchMode); - HandleMouseDown(Message, HitInfo); - if RemoveSynchMode then - Exclude(FStates, tsSynchMode); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMRButtonUp(var Message: TWMRButtonUp); - -// handle right click selection and node specific popup menu - -var - HitInfo: THitInfo; - -begin - DoStateChange([], [tsPopupMenuShown, tsRightButtonDown]); - - if FHeader.States = [] then - begin - Application.CancelHint; - - if IsMouseSelecting and Assigned(PopupMenu) then - begin - // Reset selection state already here, before the inherited handler opens the default menu. - DoStateChange([], [tsDrawSelecting, tsDrawSelPending]); - Invalidate; - end; - - inherited; - - // get information about the hit - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - - if toRightClickSelect in FOptions.SelectionOptions then - HandleMouseUp(Message, HitInfo); - - if not Assigned(PopupMenu) then - DoPopupMenu(HitInfo.HitNode, HitInfo.HitColumn, Point(Message.XPos, Message.YPos)); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMSetCursor(var Message: TWMSetCursor); - -// Sets the hot node mouse cursor for the tree. Cursor changes for the header are handled in Header.HandleMessage. - -var - NewCursor: TCursor; - HitInfo: THitInfo; - P: TPoint; - Node: PVirtualNode; - -begin - with Message do - begin - // Feature: design-time header #415 - // Allow header to handle cursor and return control's default if it did nothing - if (CursorWnd = Handle) and - ([tsWheelPanning, tsWheelScrolling] * FStates = []) then - begin - if not FHeader.HandleMessage(TMessage(Message)) then - begin - // Apply own cursors only if there is no global cursor set. - if Screen.Cursor = crDefault then - begin - // node resizing and hot tracking - for run-time only - if not (csDesigning in ComponentState) then - begin - NewCursor := crDefault; - if (toNodeHeightResize in FOptions.MiscOptions) then - begin - GetCursorPos(P); - P := ScreenToClient(P); - GetHitTestInfoAt(P.X, P.Y, True, HitInfo); - if (hiOnItem in HitInfo.HitPositions) and - ([hiUpperSplitter, hiLowerSplitter] * HitInfo.HitPositions <> []) then - begin - if hiUpperSplitter in HitInfo.HitPositions then - Node := GetPreviousVisible(HitInfo.HitNode, True) - else - Node := HitInfo.HitNode; - - if CanSplitterResizeNode(P, Node, HitInfo.HitColumn) then - NewCursor := crVertSplit; - end; - end; - - if (NewCursor = crDefault) then - if (toHotTrack in FOptions.PaintOptions) and Assigned(FCurrentHotNode) and (FHotCursor <> crDefault) then - NewCursor := FHotCursor - else - NewCursor := Cursor; - - DoGetCursor(NewCursor); - end - else - NewCursor := Cursor; - Winapi.Windows.SetCursor(Screen.Cursors[NewCursor]); - Message.Result := 1; - end - else - inherited; - end; - end - else - inherited; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMSetFocus(var Msg: TWMSetFocus); - -begin - inherited; - if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.PaintOptions) then - Invalidate; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMSize(var Message: TWMSize); - -begin - inherited; - - // Need to update scroll bars here. This will cause a recursion because of the change of the client area - // when changing a scrollbar. Usually this is no problem since with the second level recursion no change of the - // window size happens (the same values for the scrollbars are set, which shouldn't cause a window size change). - // Appearently, this applies not to all systems, however. - if HandleAllocated and ([tsSizing, tsWindowCreating] * FStates = []) and (ClientHeight > 0) then - try - DoStateChange([tsSizing]); - // This call will invalidate the entire non-client area which needs recalculation on resize. - FHeader.RescaleHeader; - FHeader.UpdateSpringColumns; - UpdateScrollBars(True); - - if (tsEditing in FStates) and not FHeader.UseColumns then - UpdateEditBounds; - finally - DoStateChange([], [tsSizing]); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMThemeChanged(var Message: TMessage); - -begin - inherited; - - if StyleServices.Enabled and (toThemeAware in TreeOptions.PaintOptions) then - DoStateChange([tsUseThemes]) - else - DoStateChange([], [tsUseThemes]); - - // Updating the visuals here will not work correctly. Therefore we postpone - // the update by using a timer. - if not FChangingTheme then - SetTimer(Handle, ThemeChangedTimer, ThemeChangedTimerDelay, nil); - FChangingTheme := False; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMTimer(var Message: TWMTimer); - -// centralized timer handling happens here - -begin - with Message do - begin - case TimerID of - ExpandTimer: - DoDragExpand; - EditTimer: - DoEdit; - ScrollTimer: - begin - if tsScrollPending in FStates then - begin - Application.CancelHint; - // Scroll delay has elapsed, set to normal scroll interval now. - SetTimer(Handle, ScrollTimer, FAutoScrollInterval, nil); - DoStateChange([tsScrolling], [tsScrollPending]); - end; - DoTimerScroll; - end; - ChangeTimer: - if tsChangePending in FStates then // see issue #602 - DoChange(FLastChangedNode); - StructureChangeTimer: - DoStructureChange(FLastStructureChangeNode, FLastStructureChangeReason); - SearchTimer: - begin - // When this event triggers then the user did not pressed any key for the specified timeout period. - // Hence incremental searching is stopped. - DoStateChange([], [tsIncrementalSearching]); - StopTimer(SearchTimer); - FSearchBuffer := ''; - FLastSearchNode := nil; - end; - ThemeChangedTimer: - begin - StopTimer(ThemeChangedTimer); - RecreateWnd; - end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.WMVScroll(var Message: TWMVScroll); - - //--------------- local functions ------------------------------------------- - - function GetRealScrollPosition: Integer; - - var - SI: TScrollInfo; - Code: Integer; - - begin - SI.cbSize := SizeOf(TScrollInfo); - SI.fMask := SIF_TRACKPOS; - Code := SB_VERT; - GetScrollInfo(Handle, Code, SI); - Result := SI.nTrackPos; - end; - - //--------------- end local functions --------------------------------------- - -begin - case Message.ScrollCode of - SB_BOTTOM: - SetOffsetY(-Integer(FRoot.TotalHeight)); - SB_ENDSCROLL: - begin - DoStateChange([], [tsThumbTracking]); - // Avoiding to adjust the horizontal scroll position while tracking makes scrolling much smoother - // but we need to adjust the final position here then. - UpdateScrollBars(True); - // Really weird invalidation needed here (and I do it only because it happens so rarely), because - // when showing the horizontal scrollbar while scrolling down using the down arrow button, - // the button will be repainted on mouse up (at the wrong place in the far right lower corner)... - RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN); - end; - SB_LINEUP: - SetOffsetY(FOffsetY + FScrollBarOptions.VerticalIncrement); - SB_LINEDOWN: - SetOffsetY(FOffsetY - FScrollBarOptions.VerticalIncrement); - SB_PAGEUP: - SetOffsetY(FOffsetY + ClientHeight); - SB_PAGEDOWN: - SetOffsetY(FOffsetY - ClientHeight); - - SB_THUMBPOSITION, - SB_THUMBTRACK: - begin - DoStateChange([tsThumbTracking]); - SetOffsetY(-GetRealScrollPosition); - end; - SB_TOP: - SetOffsetY(0); - end; - Message.Result := 0; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.AddToSelection(Node: PVirtualNode; NotifySynced: Boolean); - -var - Changed: Boolean; - RemoveSyncAfterChange: Boolean; -begin - if not FSelectionLocked then - begin - Assert(Assigned(Node), 'Node must not be nil!'); - Changed := InternalAddToSelection(Node, False); - if Changed then - begin - UpdateNextNodeToSelect(Node); - if (SelectedCount = 1) then - FocusedNode := Node; // if only one node is selected, make sure the focused node changes with the selected node - InvalidateNode(Node); - RemoveSyncAfterChange := NotifySynced and not (tsSynchMode in fStates); - if RemoveSyncAfterChange then - Include(FStates, tsSynchMode); - try - Change(Node); - finally - if RemoveSyncAfterChange then - Exclude(FStates, tsSynchMode); - end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.AddToSelection(const NewItems: TNodeArray; NewLength: Integer; ForceInsert: Boolean = False); - -// Adds the given items all at once into the current selection array. NewLength is the amount of -// nodes to add (necessary to allow NewItems to be larger than the actual used entries). -// ForceInsert is True if nodes must be inserted without consideration of level select constraint or -// already set selected flags (e.g. when loading from stream). -// Note: In the case ForceInsert is True the caller is responsible for making sure the new nodes aren't already in the -// selection array! - -var - Changed: Boolean; - -begin - Changed := InternalAddToSelection(NewItems, NewLength, ForceInsert); - if Changed then - begin - if NewLength = 1 then - begin - InvalidateNode(NewItems[0]); - Change(NewItems[0]); - end - else - begin - Invalidate; - Change(nil); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.AdjustPaintCellRect(var PaintInfo: TVTPaintInfo; var NextNonEmpty: TColumnIndex); - -// Used in descendants to modify the paint rectangle of the current column while painting a certain node. - -begin - // Since cells are always drawn from left to right the next column index is independent of the - // bidi mode, but not the column borders, which might change depending on the cell's content. - NextNonEmpty := FHeader.Columns.GetNextVisibleColumn(PaintInfo.Column); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.AdjustPanningCursor(X, Y: Integer); - -// Triggered by a mouse move when wheel panning/scrolling is active. -// Loads the proper cursor which indicates into which direction scrolling is done. - -var - Name: string; - NewCursor: HCURSOR; - ScrollHorizontal, - ScrollVertical: Boolean; - -begin - ScrollHorizontal := Integer(FRangeX) > ClientWidth; - ScrollVertical := Integer(FRangeY) > ClientHeight; - - if (Abs(X - FLastClickPos.X) < 8) and (Abs(Y - FLastClickPos.Y) < 8) then - begin - // Mouse is in the neutral zone. - if ScrollHorizontal then - begin - if ScrollVertical then - Name := 'VT_MOVEALL' - else - Name := 'VT_MOVEEW'; - end - else - Name := 'VT_MOVENS'; - end - else - begin - // One of 8 directions applies: north, north-east, east, south-east, south, south-west, west and north-west. - // Check also if scrolling in the particular direction is possible. - if ScrollVertical and ScrollHorizontal then - begin - // All directions allowed. - if X - FLastClickPos.X < -8 then - begin - // Left hand side. - if Y - FLastClickPos.Y < -8 then - Name := 'VT_MOVENW' - else - if Y - FLastClickPos.Y > 8 then - Name := 'VT_MOVESW' - else - Name := 'VT_MOVEW'; - end - else - if X - FLastClickPos.X > 8 then - begin - // Right hand side. - if Y - FLastClickPos.Y < -8 then - Name := 'VT_MOVENE' - else - if Y - FLastClickPos.Y > 8 then - Name := 'VT_MOVESE' - else - Name := 'VT_MOVEE'; - end - else - begin - // Up or down. - if Y < FLastClickPos.Y then - Name := 'VT_MOVEN' - else - Name := 'VT_MOVES'; - end; - end - else - if ScrollHorizontal then - begin - // Only horizontal movement allowed. - if X < FLastClickPos.X then - Name := 'VT_MOVEW' - else - Name := 'VT_MOVEE'; - end - else - begin - // Only vertical movement allowed. - if Y < FLastClickPos.Y then - Name := 'VT_MOVEN' - else - Name := 'VT_MOVES'; - end; - end; - - // Now load the cursor and apply it. - NewCursor := LoadCursor(HInstance, PChar(Name)); - if FPanningCursor <> NewCursor then - begin - DeleteObject(FPanningCursor); - FPanningCursor := NewCursor; - Winapi.Windows.SetCursor(FPanningCursor); - end - else - DeleteObject(NewCursor); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.AdviseChangeEvent(StructureChange: Boolean; Node: PVirtualNode; Reason: TChangeReason); - -// Used to register a delayed change event. If StructureChange is False then we have a selection change event (without -// a specific reason) otherwise it is a structure change. - -begin - if StructureChange then - begin - if tsStructureChangePending in FStates then - StopTimer(StructureChangeTimer) - else - DoStateChange([tsStructureChangePending]); - - FLastStructureChangeNode := Node; - if FLastStructureChangeReason = crIgnore then - FLastStructureChangeReason := Reason - else - if Reason <> crIgnore then - FLastStructureChangeReason := crAccumulated; - end - else - begin - if tsChangePending in FStates then - StopTimer(ChangeTimer) - else - DoStateChange([tsChangePending]); - - FLastChangedNode := Node; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.AllocateInternalDataArea(Size: Cardinal): Cardinal; - -// Simple registration method to be called by each descendant to claim their internal data area. -// Result is the offset from the begin of the node to the internal data area of the calling tree class. - -begin - Assert((FRoot = nil) or (FRoot.ChildCount = 0), 'Internal data allocation must be done before any node is created.'); - Result := TreeNodeSize + FTotalInternalDataSize; - Inc(FTotalInternalDataSize, (Size + (SizeOf(Pointer) - 1)) and not (SizeOf(Pointer) - 1)); - InitRootNode(Result); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.Animate(Steps, Duration: Cardinal; Callback: TVTAnimationCallback; Data: Pointer); - -// This method does the calculation part of an animation as used for node toggling and hint animations. -// Steps is the maximum amount of animation steps to do and Duration determines the milliseconds the animation -// has to run. Callback is a task specific method which is called in the loop for every step and Data is simply -// something to pass on to the callback. -// The callback is called with the current step, the current step size and the Data parameter. Since the step amount -// as well as the step size are possibly adjusted during the animation, it is impossible to determine if the current -// step is the last step, even if the original step amount is known. To solve this problem the callback will be -// called after the loop has finished with a step size of 0 indicating so to execute any post processing. - -var - StepSize, - RemainingTime, - RemainingSteps, - NextTimeStep, - CurrentStep, - StartTime: Cardinal; - CurrentTime: Int64; - -begin - if not (tsInAnimation in FStates) and (Duration > 0) then - begin - DoStateChange([tsInAnimation]); - try - RemainingTime := Duration; - RemainingSteps := Steps; - - // Determine the initial step size which is either 1 if the needed steps are less than the number of - // steps possible given by the duration or > 1 otherwise. - StepSize := Round(Max(1, RemainingSteps / Duration)); - RemainingSteps := RemainingSteps div StepSize; - CurrentStep := 0; - - while (RemainingSteps > 0) and (RemainingTime > 0) and not Application.Terminated do - begin - StartTime := timeGetTime; - NextTimeStep := StartTime + RemainingTime div RemainingSteps; - if not Callback(CurrentStep, StepSize, Data) then - Break; - - // Keep duration for this step for rest calculation. - CurrentTime := timeGetTime; - // Wait until the calculated time has been reached. - while CurrentTime < NextTimeStep do - CurrentTime := timeGetTime; - - // Subtract the time this step really needed. - if RemainingTime >= CurrentTime - StartTime then - begin - Dec(RemainingTime, CurrentTime - StartTime); - Dec(RemainingSteps); - end - else - begin - RemainingTime := 0; - RemainingSteps := 0; - end; - // If the remaining time per step is less than one time step then we have to decrease the - // step count and increase the step size. - if (RemainingSteps > 0) and ((RemainingTime div RemainingSteps) < 1) then - begin - repeat - Inc(StepSize); - RemainingSteps := RemainingTime div StepSize; - until (RemainingSteps <= 0) or ((RemainingTime div RemainingSteps) >= 1); - end; - CurrentStep := Steps - RemainingSteps; - end; - - if not Application.Terminated then - Callback(0, 0, Data); - finally - DoStateChange([], [tsInAnimation]); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.StartOperation(OperationKind: TVTOperationKind); - -// Called to indicate that a long-running operation has been started. - -begin - Inc(FOperationCount); - if FOperationCount = 1 then - FOperationCanceled := False; - DoStartOperation(OperationKind); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CalculateSelectionRect(X, Y: Integer): Boolean; - -// Recalculates old and new selection rectangle given that X, Y are new mouse coordinates. -// Returns True if there was a change since the last call. - -var - MaxValue: Integer; - -begin - if tsDrawSelecting in FStates then - FLastSelRect := FNewSelRect; - FNewSelRect.BottomRight := Point(X + FEffectiveOffsetX, Y - FOffsetY); - if FNewSelRect.Right < 0 then - FNewSelRect.Right := 0; - if FNewSelRect.Bottom < 0 then - FNewSelRect.Bottom := 0; - MaxValue := ClientWidth; - if FRangeX > Cardinal(MaxValue) then - MaxValue := FRangeX; - if FNewSelRect.Right > MaxValue then - FNewSelRect.Right := MaxValue; - MaxValue := ClientHeight; - if FRangeY > Cardinal(MaxValue) then - MaxValue := FRangeY; - if FNewSelRect.Bottom > MaxValue then - FNewSelRect.Bottom := MaxValue; - - Result := not CompareMem(@FLastSelRect, @FNewSelRect, SizeOf(FNewSelRect)); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CanAutoScroll: Boolean; - -// Determines if auto scrolling is currently allowed. - -var - IsDropTarget: Boolean; - IsDrawSelecting: Boolean; - IsWheelPanning: Boolean; - -begin - // Don't scroll the client area if the header is currently doing tracking or dragging. - // Do auto scroll only if there is a draw selection in progress or the tree is the current drop target or - // wheel panning/scrolling is active. - IsDropTarget := Assigned(FDragManager) and DragManager.IsDropTarget; - IsDrawSelecting := [tsDrawSelPending, tsDrawSelecting] * FStates <> []; - IsWheelPanning := [tsWheelPanning, tsWheelScrolling] * FStates <> []; - Result := ((toAutoScroll in FOptions.AutoOptions) or IsWheelPanning) and - (FHeader.States = []) and (IsDrawSelecting or IsDropTarget or (tsVCLDragging in FStates) or IsWheelPanning); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CanShowDragImage: Boolean; - -// Determines whether a drag image should be shown. - -begin - Result := FDragImageKind <> diNoImage; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CanSplitterResizeNode(P: TPoint; Node: PVirtualNode; Column: TColumnIndex): Boolean; - -begin - Result := (toNodeHeightResize in FOptions.MiscOptions) and Assigned(Node) and (Node <> FRoot) and - (Column > NoColumn) and (coFixed in FHeader.Columns[Column].Options); - DoCanSplitterResizeNode(P, Node, Column, Result); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.Change(Node: PVirtualNode); - -begin - AdviseChangeEvent(False, Node, crIgnore); - - if FUpdateCount = 0 then - begin - if (FChangeDelay > 0) and HandleAllocated and not (tsSynchMode in FStates) then - SetTimer(Handle, ChangeTimer, FChangeDelay, nil) - else - DoChange(Node); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; isDpiChange: Boolean{$ifend}); -{$if CompilerVersion < 27} -const - DefaultScalingFlags = [sfLeft, sfTop, sfWidth, sfHeight, sfFont]; // Was introduced with XE6: http://docwiki.embarcadero.com/Libraries/XE6/en/Vcl.Controls.TControl.DefaultScalingFlags -{$ifend} -var - Flags: TScalingFlags; - Run: PVirtualNode; - lNewNodeTotalHeight: Cardinal; -begin - if (toAutoChangeScale in FOptions.AutoOptions) then - begin - if (M <> D) then - begin - // It is important to evaluate the TScalingFlags before calling inherited, becuase they are differetn afterwards! - if csLoading in ComponentState then - Flags := ScalingFlags - else - Flags := DefaultScalingFlags; // Important for #677 - if (sfHeight in Flags) then begin - FHeader.ChangeScale(M, D, {$if CompilerVersion >= 31}isDpiChange{$ELSE} M <> D{$ifend}); - SetDefaultNodeHeight(MulDiv(FDefaultNodeHeight, M, D)); - Indent := MulDiv(Indent, M, D); - FTextMargin := MulDiv(FTextMargin, M, D); - FMargin := MulDiv(FMargin, M, D); - FImagesMargin := MulDiv(FImagesMargin, M, D); - // Scale utility images, #796 - if FCheckImageKind = ckSystemDefault then begin - FreeAndNil(FCheckImages); - if HandleAllocated then - FCheckImages := CreateSystemImageSet(Self); - end; - UpdateHeaderRect(); - // Scale also node heights - BeginUpdate(); - try - Run := GetFirst(); - while Assigned(Run) do - begin - if vsInitialized in Run.States then - SetNodeHeight(Run, MulDiv(Run.NodeHeight, M, D)) - else // prevent initialization of non-initialzed nodes - begin - Run.NodeHeight := MulDiv(Run.NodeHeight, M, D); - // The next three lines fix issue #1000 - lNewNodeTotalHeight := MulDiv(Run.TotalHeight, M, D); - FRoot.TotalHeight := FRoot.TotalHeight + lNewNodeTotalHeight - Run.TotalHeight; // 1 EIntOverflow exception seen here in debug build in 01/2021 - Run.TotalHeight := lNewNodeTotalHeight; - end; - Run := GetNextNoInit(Run); - end; // while - finally - EndUpdate(); - end; - end;//if sfHeight - end;// if M<>D - end;//if toAutoChangeScale - inherited ChangeScale(M, D{$if CompilerVersion >= 31}, isDpiChange{$ifend}); - if (M <> D) then - PrepareBitmaps(True, False); // See issue #991 - // It is important to do this call after calling inherited, so that the Font has been updated. - AutoScale(M <> D); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirtualTreeStates); -begin - //TODO: If this works reliable, move to TWorkerThread - if not (csDestroying in ComponentState) then - TThread.Synchronize(nil, procedure - begin - // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) - if not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then - DoStateChange(EnterStates, LeaveStates); - if (tsValidating in FStates) and (tsValidating in LeaveStates) then - UpdateEditBounds(); - end); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CheckParentCheckState(Node: PVirtualNode; NewCheckState: TCheckState): Boolean; - -// Checks all siblings of node to determine which check state Node's parent must get. - -var - CheckCount, - BoxCount: Cardinal; - PartialCheck: Boolean; - Run: PVirtualNode; - -begin - CheckCount := 0; - BoxCount := 0; - PartialCheck := False; - Run := Node.Parent.FirstChild; - while Assigned(Run) do - begin - if Run = Node then - begin - // The given node cannot be checked because it does not yet have its new check state (as this depends - // on the outcome of this method). Instead NewCheckState is used as this contains the new state the node - // will get if this method returns True. - if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then - begin - Inc(BoxCount); - if NewCheckState.IsChecked then - Inc(CheckCount); - PartialCheck := PartialCheck or (NewCheckState = csMixedNormal); - end; - end - else - if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then - begin - Inc(BoxCount); - if GetCheckState(Run).IsChecked then - Inc(CheckCount); - PartialCheck := PartialCheck or (GetCheckState(Run) = csMixedNormal); - end; - Run := Run.NextSibling; - end; - - if (CheckCount = 0) and not PartialCheck then - NewCheckState := csUncheckedNormal - else - if CheckCount < BoxCount then - NewCheckState := csMixedNormal - else - NewCheckState := csCheckedNormal; - - Node := Node.Parent; - Result := DoChecking(Node, NewCheckState); - if Result then - begin - DoCheckClick(Node, NewCheckState); - // Recursively adjust parent of parent. - // This is already done in the function DoCheckClick() called in the above line - // We revent unnecessary upward recursion by commenting this code. - // with Node^ do - // begin - // if not (vsInitialized in Parent.States) then - // InitNode(Parent); - // if ([vsChecking, vsDisabled] * Parent.States = []) and (Parent <> FRoot) and - // (Parent.CheckType = ctTriStateCheckBox) then - // Result := CheckParentCheckState(Node, NewCheckState); - // end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.ClearTempCache; - -// make sure the temporary node cache is in a reliable state - -begin - FTempNodeCache := nil; - FTempNodeCount := 0; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.ColumnIsEmpty(Node: PVirtualNode; Column: TColumnIndex): Boolean; - -// Returns True if the given column is to be considered as being empty. This will usually be determined by -// descendants as the base tree implementation has not enough information to decide. - -begin - Result := True; - if Assigned(FOnGetCellIsEmpty) then - FOnGetCellIsEmpty(Self, Node, Column, Result); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.ComputeRTLOffset(ExcludeScrollBar: Boolean): Integer; - -// Computes the horizontal offset needed when all columns are automatically right aligned (in RTL bidi mode). -// ExcludeScrollBar determines if the left-hand vertical scrollbar is to be included (if visible) or not. - -var - HeaderWidth: Integer; - ScrollBarVisible: Boolean; -begin - ScrollBarVisible := (Integer(FRangeY) > ClientHeight) and (ScrollBarOptions.ScrollBars in [ssVertical, ssBoth]); - if ScrollBarVisible then - Result := GetSystemMetrics(SM_CXVSCROLL) - else - Result := 0; - - // Make everything right aligned. - HeaderWidth := FHeaderRect.Right - FHeaderRect.Left; - if Integer(FRangeX) + Result <= HeaderWidth then - Result := HeaderWidth - Integer(FRangeX); - // Otherwise take only left-hand vertical scrollbar into account. - - if ScrollBarVisible and ExcludeScrollBar then - Dec(Result, GetSystemMetrics(SM_CXVSCROLL)); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CountLevelDifference(Node1, Node2: PVirtualNode): Integer; - -// This method counts how many indentation levels the given nodes are apart. If both nodes have the same parent then the -// difference is 0 otherwise the result is basically GetNodeLevel(Node2) - GetNodeLevel(Node1), but with sign. -// If the result is negative then Node2 is less intended than Node1. - -var - Level1, Level2: Integer; - -begin - Assert(Assigned(Node1) and Assigned(Node2), 'Both nodes must be Assigned.'); - - Level1 := 0; - while Node1.Parent <> FRoot do - begin - Inc(Level1); - Node1 := Node1.Parent; - end; - - Level2 := 0; - while Node2.Parent <> FRoot do - begin - Inc(Level2); - Node2 := Node2.Parent; - end; - - Result := Level2 - Level1; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CountVisibleChildren(Node: PVirtualNode): Cardinal; - -// Returns the number of visible child nodes of the given node. - -begin - Result := 0; - - // The node's direct children... - if vsExpanded in Node.States then - begin - // ...and their children. - Node := Node.FirstChild; - while Assigned(Node) do - begin - if vsVisible in Node.States then - Inc(Result, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); - Node := Node.NextSibling; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CreateParams(var Params: TCreateParams); - -const - ScrollBar: array[TScrollStyle] of Cardinal = (0, WS_HSCROLL, WS_VSCROLL, WS_HSCROLL or WS_VSCROLL); - -begin - inherited CreateParams(Params); - - with Params do - begin - Style := Style or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or ScrollBar[ScrollBarOptions.FScrollBars]; - if toFullRepaintOnResize in FOptions.MiscOptions then - WindowClass.style := WindowClass.style or CS_HREDRAW or CS_VREDRAW - else - WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW); - if FBorderStyle = bsSingle then - begin - if Ctl3D then - begin - ExStyle := ExStyle or WS_EX_CLIENTEDGE; - Style := Style and not WS_BORDER; - end - else - Style := Style or WS_BORDER; - end - else - Style := Style and not WS_BORDER; - - AddBiDiModeExStyle(ExStyle); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.CreateWnd; - -// Initializes data which depends on a valid window handle. - -begin - VclStyleChanged(); // Moved here due to issue #986 - DoStateChange([tsWindowCreating]); - inherited; - DoStateChange([], [tsWindowCreating]); - - if not Assigned(FCheckImages) then - FCheckImages := CreateSystemImageSet(Self); - - if ((StyleServices.Enabled ) and (toThemeAware in TreeOptions.PaintOptions) ) then - begin - DoStateChange([tsUseThemes]); - if (toUseExplorerTheme in FOptions.PaintOptions) and IsWinVistaOrAbove then - begin - DoStateChange([tsUseExplorerTheme]); - SetWindowTheme('explorer'); - end - else - DoStateChange([], [tsUseExplorerTheme]); - end - else - DoStateChange([], [tsUseThemes, tsUseExplorerTheme]); - - // Because of the special recursion and update stopper when creating the window (or resizing it) - // we have to manually trigger the auto size calculation here. - if hsNeedScaling in FHeader.States then - FHeader.RescaleHeader; - if hoAutoResize in FHeader.Options then - FHeader.Columns.AdjustAutoSize(InvalidColumn); - - PrepareBitmaps(True, True); - - // Register tree as OLE drop target. - if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.MiscOptions) then - if not (csLoading in ComponentState) then // will be done in Loaded after all inherited settings are loaded from the DFMs - RegisterDragDrop(Handle, DragManager as IDropTarget); - - UpdateScrollBars(True); - UpdateHeaderRect; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.FakeReadIdent(Reader: TReader); -begin - Assert(Reader.NextValue = vaIdent); - Reader.ReadIdent; -end; - - -procedure TBaseVirtualTree.DefineProperties(Filer: TFiler); - -// There were heavy changes in some properties during development of VT. This method helps to make migration easier -// by reading old properties manually and put them into the new properties as appropriate. -// Note: these old properties are never written again and silently disappear. -// June 2002: Meanwhile another task is done here too: working around the problem that TCollection is not streamed -// correctly when using Visual Form Inheritance (VFI). - -var - StoreIt: Boolean; - -begin - inherited; - - // The header can prevent writing columns altogether. - if FHeader.CanWriteColumns then - begin - // Check if we inherit from an ancestor form (Visual Form Inheritance). - StoreIt := Filer.Ancestor = nil; - // If there is an ancestor then save columns only if they are different to the base set. - if not StoreIt then - StoreIt := not FHeader.Columns.Equals(TBaseVirtualTree(Filer.Ancestor).FHeader.Columns); - end - else - StoreIt := False; - - Filer.DefineProperty('Columns', FHeader.ReadColumns, FHeader.WriteColumns, StoreIt); - - // #622 made old DFMs incompatible with new VTW - so the program is compiled successfully - // and then suddenly crashes at user site in runtime. - Filer.DefineProperty('CheckImageKind', FakeReadIdent, nil, false); - /// #730 removed property HintAnimation - Filer.DefineProperty('HintAnimation', FakeReadIdent, nil, false); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.DetermineDropMode(const P: TPoint; var HitInfo: THitInfo; var NodeRect: TRect): TDropMode; - -// Determine the DropMode. - -var - ImageHit: Boolean; - LabelHit: Boolean; - ItemHit: Boolean; - -begin - ImageHit := HitInfo.HitPositions * [hiOnNormalIcon, hiOnStateIcon] <> []; - LabelHit := hiOnItemLabel in HitInfo.HitPositions; - ItemHit := (hiOnItem in HitInfo.HitPositions) and ((toFullRowDrag in FOptions.MiscOptions) or - (toFullRowSelect in FOptions.SelectionOptions)); - - // In report mode only direct hits of the node captions/images in the main column are accepted as hits. - if (toReportMode in FOptions.MiscOptions) and not (ItemHit or ((LabelHit or ImageHit) and - (HitInfo.HitColumn = FHeader.MainColumn))) then - HitInfo.HitNode := nil; - - if Assigned(HitInfo.HitNode) then - begin - if LabelHit or ImageHit or not (toShowDropmark in FOptions.PaintOptions) then - Result := dmOnNode - else - if ((NodeRect.Top + NodeRect.Bottom) div 2) > P.Y then - Result := dmAbove - else - Result := dmBelow; - end - else - Result := dmNowhere; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.DetermineHiddenChildrenFlag(Node: PVirtualNode); - -// Update the hidden children flag of the given node. - -var - Run: PVirtualNode; - -begin - if Node.ChildCount = 0 then - begin - if vsHasChildren in Node.States then - Exclude(Node.States, vsAllChildrenHidden) - else - Include(Node.States, vsAllChildrenHidden); - end - else - begin - // Iterate through all siblings and stop when one visible is found. - Run := Node.FirstChild; - while Assigned(Run) and not IsEffectivelyVisible[Run] do - Run := Run.NextSibling; - if Assigned(Run) then - Exclude(Node.States, vsAllChildrenHidden) - else - Include(Node.States, vsAllChildrenHidden); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.DetermineHiddenChildrenFlagAllNodes; - -var - Run: PVirtualNode; - -begin - Run := GetFirstNoInit(False); - while Assigned(Run) do - begin - DetermineHiddenChildrenFlag(Run); - Run := GetNextNoInit(Run); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.DetermineHitPositionLTR(var HitInfo: THitInfo; Offset, Right: Integer; - Alignment: TAlignment); - -// This method determines the hit position within a node with left-to-right orientation. - -var - MainColumnHit: Boolean; - lIndent, - TextWidth, - ImageOffset: Integer; - lOffsets: TVTOffsets; -begin - MainColumnHit := HitInfo.HitColumn = FHeader.MainColumn; - GetOffsets(HitInfo.HitNode, lOffsets, ofsRightOfText, HitInfo.HitColumn); - - if (MainColumnHit and (Offset < lOffsets[ofsCheckbox])) then - begin - // Position is to the left of calculated indentation which can only happen for the main column. - // Check whether it corresponds to a button/checkbox. - if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then - begin - // Position of button is interpreted very generously to avoid forcing the user - // to click exactly into the 9x9 pixels area. The entire node height and one full - // indentation level is accepted as button hit. - if Offset >= lOffsets[ofsCheckbox] - Integer(FIndent) then - Include(HitInfo.HitPositions, hiOnItemButton); - if Offset > lOffsets[ofsToggleButton] then - Include(HitInfo.HitPositions, hiOnItemButtonExact); - end; - // no button hit so position is on indent - if HitInfo.HitPositions = [] then - Include(HitInfo.HitPositions, hiOnItemIndent); - end - else - begin - // The next hit positions can be: - // - on the check box - // - on the state image - // - on the normal image - // - to the left of the text area - // - on the label or - // - to the right of the text area - // (in this order). + // Advance to next/previous visible column. + if FHeader.UseColumns then + NewColumn := GetStartColumn + else + NewColumn := NoColumn; + // Find a column for the new/current node which can be focused. + // Make the 'DoFocusChanging' for finding a valid column + // identifiable from the 'DoFocusChanging' raised later on by + // "FocusedNode := Node;" + while (NewColumn > NoColumn) and not DoFocusChanging(FFocusedNode, FFocusedNode, FFocusedColumn, NewColumn) do + NewColumn := GetNextColumn(NewColumn); + if NewColumn > InvalidColumn then + begin + if (Shift = [ssCtrl]) and not ActAsGrid then + begin + ScrollIntoView(Node, toCenterScrollIntoView in FOptions.SelectionOptions, + not (toDisableAutoscrollOnFocus in FOptions.AutoOptions)); + if (CharCode = VK_HOME) and not UseRightToLeftAlignment then + SetOffsetX(0) + else + SetOffsetX(-MaxInt); + end + else + begin + if not ActAsGrid or (ssCtrl in Shift) then + FocusedNode := Node; + //fix: In grid mode, if full row select option is ON, + //then also go to the node determined from the earlier logic + if ActAsGrid and (toFullRowSelect in FOptions.SelectionOptions) then + FocusedNode := Node; + if ActAsGrid and not (toFullRowSelect in FOptions.SelectionOptions) then + begin + FocusedColumn := NewColumn; + // fix: If auto span is ON the last column may be a merged column. So take + // care of selecting the whole merged column on END key. + if (CharCode = VK_END) and isEmptyAutoSpanColumn(NewColumn, FFocusedNode) then + FocusedColumn := getPreviousVisibleAutoSpanColumn(NewColumn, FFocusedNode); + end; + end; + end; + end; + VK_PRIOR: + if Shift = [ssCtrl, ssShift] then + SetOffsetX(FOffsetX + ClientWidth) + else + if [ssShift, ssAlt] = Shift then + begin + if FFocusedColumn <= NoColumn then + NewColumn := FHeader.Columns.GetFirstVisibleColumn + else + begin + Offset := FHeader.Columns.GetVisibleFixedWidth; + NewColumn := FFocusedColumn; + while True do + begin + TempColumn := FHeader.Columns.GetPreviousVisibleColumn(NewColumn); + NewWidth := FHeader.Columns[NewColumn].Width; + if (TempColumn <= NoColumn) or + (Offset + NewWidth >= ClientWidth) or + (coFixed in FHeader.Columns[TempColumn].Options) then + Break; + NewColumn := TempColumn; + Inc(Offset, NewWidth); + end; + end; + SetFocusedColumn(NewColumn); + end + else + if ssCtrl in Shift then + SetOffsetY(FOffsetY + ClientHeight) + else + begin + Offset := 0; + // If there's no focused node then just take the very first visible one. + if FFocusedNode = nil then + Node := GetFirstVisible(nil, True) + else + begin + // Go up as many nodes as comprise together a size of ClientHeight. + Node := FFocusedNode; + while True do + begin + Temp := GetPreviousVisible(Node, True); + NewHeight := NodeHeight[Node]; + if (Temp = nil) or (Offset + NewHeight >= ClientHeight) then + Break; + Node := Temp; + Inc(Offset, NodeHeight[Node]); + end; + end; + FocusedNode := Node; + end; + VK_NEXT: + if Shift = [ssCtrl, ssShift] then + SetOffsetX(FOffsetX - ClientWidth) + else + if [ssShift, ssAlt] = Shift then + begin + if FFocusedColumn <= NoColumn then + NewColumn := FHeader.Columns.GetFirstVisibleColumn + else + begin + Offset := FHeader.Columns.GetVisibleFixedWidth; + NewColumn := FFocusedColumn; + while True do + begin + TempColumn := FHeader.Columns.GetNextVisibleColumn(NewColumn); + NewWidth := FHeader.Columns[NewColumn].Width; + if (TempColumn <= NoColumn) or + (Offset + NewWidth >= ClientWidth) or + (coFixed in FHeader.Columns[TempColumn].Options) then + Break; + NewColumn := TempColumn; + Inc(Offset, NewWidth); + end; + end; + SetFocusedColumn(NewColumn); + end + else + if ssCtrl in Shift then + SetOffsetY(FOffsetY - ClientHeight) + else + begin + Offset := 0; + // If there's no focused node then just take the very last one. + if FFocusedNode = nil then + Node := GetLastVisible(nil, True) + else + begin + // Go up as many nodes as comprise together a size of ClientHeight. + Node := FFocusedNode; + while True do + begin + Temp := GetNextVisible(Node, True); + NewHeight := NodeHeight[Node]; + if (Temp = nil) or (Offset + NewHeight >= ClientHeight) then + Break; + Node := Temp; + Inc(Offset, NewHeight); + end; + end; + FocusedNode := Node; + end; + VK_UP: + begin + // scrolling without selection change + if ssCtrl in Shift then + SetOffsetY(FOffsetY + Integer(FDefaultNodeHeight)) + else + begin + if FFocusedNode = nil then + Node := GetLastVisible(nil, True) + else + Node := GetPreviousVisible(FFocusedNode, True); - // In report mode no hit other than in the main column is possible. - if MainColumnHit or not (toReportMode in FOptions.MiscOptions) then - begin - if MainColumnHit and (Offset < lOffsets[ofsStateImage]) then - begin - HitInfo.HitPositions := [hiOnItem]; - if (HitInfo.HitNode.CheckType <> ctNone) then - Include(HitInfo.HitPositions, hiOnItemCheckBox); - end - else - begin - ImageOffset := lOffsets[ofsImage]; - if Offset < ImageOffset then - Include(HitInfo.HitPositions, hiOnStateIcon) - else - begin - ImageOffset := lOffsets[ofsLabel]; - if Offset < ImageOffset then - Include(HitInfo.HitPositions, hiOnNormalIcon) - else - begin - TextWidth := lOffsets[ofsRightOfText] - lOffsets[ofsText]; - // ImageOffset contains now the left border of the node label area. This is used to calculate the - // correct alignment in the column. + if Assigned(Node) then + begin + if not EndEditNode then + exit; + if (not PerformMultiSelect or (CompareNodePositions(LastFocused, Node) < -1)) and Assigned(FFocusedNode) then + ClearSelection(False); // Clear selection only if more than one node was skipped. See issue #926 + if FFocusedColumn <= NoColumn then + FFocusedColumn := FHeader.MainColumn; + FocusedNode := Node; + end + else + if Assigned(FFocusedNode) then + InvalidateNode(FFocusedNode); + end; + end; + VK_DOWN: + begin + // scrolling without selection change + if ssCtrl in Shift then + SetOffsetY(FOffsetY - Integer(FDefaultNodeHeight)) + else + begin + if FFocusedNode = nil then + Node := GetFirstVisible(nil, True) + else + Node := GetNextVisible(FFocusedNode, True); - // Check if the text can be aligned at all. This is only possible if there is enough room - // in the remaining text rectangle. - if TextWidth > Right - ImageOffset then - Include(HitInfo.HitPositions, hiOnItemLabel) - else + if Assigned(Node) then + begin + if not EndEditNode then + exit; + if (not PerformMultiSelect or (CompareNodePositions(LastFocused, Node) > 1)) and Assigned(FFocusedNode) then + ClearSelection(False); // Clear selection only if more than one node was skipped. See issue #926 + if FFocusedColumn <= NoColumn then + FFocusedColumn := FHeader.MainColumn; + FocusedNode := Node; + end + else + if Assigned(FFocusedNode) then + InvalidateNode(FFocusedNode); + end; + end; + VK_LEFT: begin - case Alignment of - taCenter: + // special handling + if ssCtrl in Shift then + SetOffsetX(FOffsetX + RTLFactor * FHeader.Columns.GetScrollWidth) + else + begin + // other special cases + Context := NoColumn; + if (toExtendedFocus in FOptions.SelectionOptions) and (toGridExtensions in FOptions.MiscOptions) then + begin + Context := getPreviousVisibleAutoSpanColumn(FFocusedColumn, FFocusedNode); + if Context > NoColumn then + FocusedColumn := Context; + end + else + if Assigned(FFocusedNode) and (vsExpanded in FFocusedNode.States) and + (Shift = []) and (vsHasChildren in FFocusedNode.States) then + ToggleNode(FFocusedNode) + else begin - lIndent := (ImageOffset + Right - TextWidth) div 2; - if Offset < lIndent then - Include(HitInfo.HitPositions, hiOnItemLeft) + if FFocusedNode = nil then + FocusedNode := GetFirstVisible(nil, True) else - if Offset < lIndent + TextWidth then - Include(HitInfo.HitPositions, hiOnItemLabel) + begin + if FFocusedNode.Parent <> FRoot then + Node := FFocusedNode.Parent else - Include(HitInfo.HitPositions, hiOnItemRight); + Node := nil; + if Assigned(Node) then + begin + if PerformMultiSelect then + begin + // and a third special case + if FFocusedNode.Index > 0 then + DoRangeSelect := True + else + if CompareNodePositions(Node, FRangeAnchor) > 0 then + RemoveFromSelection(FFocusedNode); + end; + FocusedNode := Node; + end + else begin + // If already a root node is selected, then scroll to the left as there is nothing else we could do. #691 + SetOffsetX(FOffsetX + RTLFactor * FHeader.Columns.GetScrollWidth); + end;//else + end; end; - taRightJustify: + end; + end; + VK_RIGHT: + begin + // special handling + if ssCtrl in Shift then + SetOffsetX(FOffsetX - RTLFactor * FHeader.Columns.GetScrollWidth) + else + begin + // other special cases + Context := NoColumn; + if (toExtendedFocus in FOptions.SelectionOptions) and (toGridExtensions in FOptions.MiscOptions) then + begin + Context := getNextVisibleAutoSpanColumn(FFocusedColumn, FFocusedNode); + if Context > NoColumn then + FocusedColumn := Context; + end + else + if Assigned(FFocusedNode) and not (vsExpanded in FFocusedNode.States) and + (Shift = []) and (vsHasChildren in FFocusedNode.States) then + ToggleNode(FFocusedNode) + else begin - lIndent := Right - TextWidth; - if Offset < lIndent then - Include(HitInfo.HitPositions, hiOnItemLeft) + if FFocusedNode = nil then + FocusedNode := GetFirstVisible(nil, True) else - Include(HitInfo.HitPositions, hiOnItemLabel); + begin + Node := GetFirstVisibleChild(FFocusedNode); + if Assigned(Node) then + begin + if PerformMultiSelect and (CompareNodePositions(Node, FRangeAnchor) < 0) then + RemoveFromSelection(FFocusedNode); + FocusedNode := Node; + end + else begin + // If already a leaf node is selected, then scroll to the right as there is nothing else we could do. #691 + SetOffsetX(FOffsetX - RTLFactor * FHeader.Columns.GetScrollWidth); + end;//else + end; end; - else // taLeftJustify - if Offset < ImageOffset + TextWidth then - Include(HitInfo.HitPositions, hiOnItemLabel) - else - Include(HitInfo.HitPositions, hiOnItemRight); end; end; - end; - end; - end; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- + VK_BACK: + if tsIncrementalSearching in FStates then + DoStateChange([tsIncrementalSearchPending]) + else + if Assigned(FFocusedNode) and (FFocusedNode.Parent <> FRoot) then + FocusedNode := FocusedNode.Parent; + VK_TAB: + if (toExtendedFocus in FOptions.SelectionOptions) and FHeader.UseColumns then + begin + // In order to avoid duplicating source code just to change the direction + // we use function variables. + if ssShift in Shift then + begin + GetStartColumn := FHeader.Columns.GetLastVisibleColumn; + GetNextColumn := FHeader.Columns.GetPreviousVisibleColumn; + GetNextNode := GetPreviousVisible; + end + else + begin + GetStartColumn := FHeader.Columns.GetFirstVisibleColumn; + GetNextColumn := FHeader.Columns.GetNextVisibleColumn; + GetNextNode := GetNextVisible; + end; -procedure TBaseVirtualTree.DetermineHitPositionRTL(var HitInfo: THitInfo; Offset, Right: Integer; Alignment: TAlignment); + // Advance to next/previous visible column/node. + Node := FFocusedNode; + NewColumn := GetNextColumn(FFocusedColumn, True); + repeat + // Find a column for the current node which can be focused. + while (NewColumn > NoColumn) and not DoFocusChanging(FFocusedNode, Node, FFocusedColumn, NewColumn) + //Fix: for Tab Key to properly skip the empty auto span column + or isEmptyAutoSpanColumn(NewColumn, Node) do + NewColumn := GetNextColumn(NewColumn, True); -// This method determines the hit position within a node with right-to-left orientation. + if NewColumn > NoColumn then + begin + // Set new node and column in one go. + SetFocusedNodeAndColumn(Node, NewColumn); + Break; + end; -var - MainColumnHit: Boolean; - Run: PVirtualNode; - Indent, - TextWidth, - ImageOffset: Integer; + // No next column was accepted for the current node. So advance to next node and try again. + Node := GetNextNode(Node); + NewColumn := GetStartColumn; -begin - MainColumnHit := HitInfo.HitColumn = FHeader.MainColumn; + // fix: From last column, the Tab key should always go to next row irrespective of auto span + // Similarly the Shift-Tab key should go to previos row from first column + if (Node <> nil) and (NewColumn > NoColumn) then + SetFocusedNodeAndColumn(Node, NewColumn); - // If columns are not used or the main column is hit then the tree indentation must be considered too. - if MainColumnHit then - begin - if toFixedIndent in FOptions.PaintOptions then - Dec(Right, FIndent) - else - begin - Run := HitInfo.HitNode; - while (Run.Parent <> FRoot) do - begin - Dec(Right, FIndent); - Run := Run.Parent; - end; - if toShowRoot in FOptions.PaintOptions then - Dec(Right, FIndent); - end; - end; + until Node = nil; + end; + end; - if Offset >= Right then - begin - // Position is to the right of calculated indentation which can only happen for the main column. - // Check whether it corresponds to a button/checkbox. - if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then - begin - // Position of button is interpreted very generously to avoid forcing the user - // to click exactly into the 9x9 pixels area. The entire node height and one full - // indentation level is accepted as button hit. - if Offset <= Right + Integer(FIndent) then - Include(HitInfo.HitPositions, hiOnItemButton); - if Offset <= Right + FPlusBM.Width then - Include(HitInfo.HitPositions, hiOnItemButtonExact); - end; - // no button hit so position is on indent - if HitInfo.HitPositions = [] then - Include(HitInfo.HitPositions, hiOnItemIndent); - end - else - begin - // The next hit positions can be: - // - on the check box - // - on the state image - // - on the normal image - // - to the left of the text area - // - on the label or - // - to the right of the text area - // (in this order). + // Clear old selection if required but take care to select the new focused node if it was not selected before. + ForceSelection := False; + if ClearPending and ((LastFocused <> FFocusedNode) or (FSelectionCount <> 1)) then + begin + ClearSelection(not Assigned(FFocusedNode)); + ForceSelection := True; + end; - // In report mode no hit other than in the main column is possible. - if MainColumnHit or not (toReportMode in FOptions.MiscOptions) then - begin - ImageOffset := Right - FMargin; + // Determine new selection anchor. + if Shift = [] then + begin + FRangeAnchor := FFocusedNode; + FLastSelectionLevel := GetNodeLevelForSelectConstraint(FFocusedNode); + end; - // Check support is only available for the main column. - if MainColumnHit and (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages) and - (HitInfo.HitNode.CheckType <> ctNone) then - Dec(ImageOffset, FCheckImages.Width + FImagesMargin); + if Assigned(FFocusedNode) then + begin + // Finally change the selection for a specific range of nodes. + if DoRangeSelect then + ToggleSelection(LastFocused, FFocusedNode) + // Make sure the new focused node is also selected. + else if (LastFocused <> FFocusedNode) then begin + if ForceSelection then + AddToSelection(FFocusedNode, False) + else + ToggleSelection(LastFocused, FFocusedNode); // See issue #926 + end; + end; - if MainColumnHit and (Offset > ImageOffset) then - begin - HitInfo.HitPositions := [hiOnItem]; - if (HitInfo.HitNode.CheckType <> ctNone) then - Include(HitInfo.HitPositions, hiOnItemCheckBox); + // If a repaint is needed then paint the entire tree because of the ClearSelection call, + if NeedInvalidate then + Invalidate; end else begin - Dec(ImageOffset, GetImageSize(HitInfo.HitNode, ikState, HitInfo.HitColumn).cx); - if Offset > ImageOffset then - Include(HitInfo.HitPositions, hiOnStateIcon) - else + // Second chance for keys not directly concerned with selection changes. + + // For +, -, /, * keys on the main keyboard (not numpad) there is no virtual key code defined. + // We have to do special processing to get them working too. + GetKeyboardState(KeyState); + // Avoid conversion to control characters. We have captured the control key state already in Shift. + KeyState[VK_CONTROL] := 0; + if ToASCII(Message.CharCode, (Message.KeyData shr 16) and 7, KeyState, PChar(@Buffer), 0) > 0 then begin - Dec(ImageOffset, GetImageSize(HitInfo.HitNode, ikNormal, HitInfo.HitColumn).cx); - if Offset > ImageOffset then - Include(HitInfo.HitPositions, hiOnNormalIcon) - else - begin - // ImageOffset contains now the right border of the node label area. This is used to calculate the - // correct alignment in the column. - TextWidth := DoGetNodeWidth(HitInfo.HitNode, HitInfo.HitColumn); + case Buffer[0] of + '*': + CharCode := VK_MULTIPLY; + '+': + CharCode := VK_ADD; + '/': + CharCode := VK_DIVIDE; + '-': + CharCode := VK_SUBTRACT; + end; + end; - // Check if the text can be aligned at all. This is only possible if there is enough room - // in the remaining text rectangle. - if TextWidth > ImageOffset then - Include(HitInfo.HitPositions, hiOnItemLabel) + // According to https://web.archive.org/web/20041129085958/http://www.it-faq.pl/mskb/99/337.HTM + // there is a problem with ToASCII when used in conjunction with dead chars. + // The article recommends to call ToASCII twice to restore a deleted flag in the key message + // structure under certain circumstances. It turned out it is best to always call ToASCII twice. + ToASCII(Message.CharCode, (Message.KeyData shr 16) and 7, KeyState, PChar(@Buffer), 0); + + case CharCode of + VK_F2: + if (Shift = []) and Assigned(FFocusedNode) and CanEdit(FFocusedNode, FFocusedColumn) then + begin + FEditColumn := FFocusedColumn; + DoEdit; + end; + VK_ADD: + if not (tsIncrementalSearching in FStates) then + begin + if ssCtrl in Shift then begin// When changing this code review issue #781 + if ((toReverseFullExpandHotKey in TreeOptions.MiscOptions) and (Shift = [ssCtrl])) xor (Shift = [ssCtrl, ssShift]) then + FullExpand + else if Shift = [ssCtrl] then + FHeader.AutoFitColumns + end + else if Shift = [] then begin + if Assigned(FFocusedNode) and not (vsExpanded in FFocusedNode.States) then + ToggleNode(FFocusedNode); + end// if Shift = [] + else + DoStateChange([tsIncrementalSearchPending]); + end;//if not (tsIncrementalSearching in FStates) + VK_SUBTRACT: + if not (tsIncrementalSearching in FStates) then + begin + if ssCtrl in Shift then + if (toReverseFullExpandHotKey in TreeOptions.MiscOptions) xor (ssShift in Shift) then + FullCollapse + else + FHeader.RestoreColumns + else + if Assigned(FFocusedNode) and (vsExpanded in FFocusedNode.States) then + ToggleNode(FFocusedNode); + end else + DoStateChange([tsIncrementalSearchPending]); + VK_MULTIPLY: + if not (tsIncrementalSearching in FStates) then begin - // Consider bidi mode here. In RTL context does left alignment actually mean right alignment - // and vice versa. - ChangeBiDiModeAlignment(Alignment); - - case Alignment of - taCenter: - begin - Indent := (ImageOffset - TextWidth) div 2; - if Offset < Indent then - Include(HitInfo.HitPositions, hiOnItemLeft) - else - if Offset < Indent + TextWidth then - Include(HitInfo.HitPositions, hiOnItemLabel) - else - Include(HitInfo.HitPositions, hiOnItemRight); - end; - taRightJustify: - begin - Indent := ImageOffset - TextWidth; - if Offset < Indent then - Include(HitInfo.HitPositions, hiOnItemLeft) - else - Include(HitInfo.HitPositions, hiOnItemLabel); - end; - else // taLeftJustify - if Offset > TextWidth then - Include(HitInfo.HitPositions, hiOnItemRight) + if Assigned(FFocusedNode) then + FullExpand(FFocusedNode); + end + else + DoStateChange([tsIncrementalSearchPending]); + VK_DIVIDE: + if not (tsIncrementalSearching in FStates) then + begin + if Assigned(FFocusedNode) then + FullCollapse(FFocusedNode); + end + else + DoStateChange([tsIncrementalSearchPending]); + VK_ESCAPE: // cancel actions currently in progress + begin + if IsMouseSelecting then + begin + DoStateChange([], [tsDrawSelecting, tsDrawSelPending]); + Invalidate; + end + else + if IsEditing then + CancelEditNode; + end; + VK_SPACE: + if (toCheckSupport in FOptions.MiscOptions) and Assigned(FFocusedNode) and + (FFocusedNode.CheckType <> ctNone) then + begin + NewCheckState := DetermineNextCheckState(FFocusedNode.CheckType, GetCheckState(FFocusedNode)); + if DoChecking(FFocusedNode, NewCheckState) then + begin + if SelectedCount > 1 then + SetCheckStateForAll(NewCheckState, True) else - Include(HitInfo.HitPositions, hiOnItemLabel); + DoCheckClick(FFocusedNode, NewCheckState); + end; + end + else + DoStateChange([tsIncrementalSearchPending]); + VK_F1: + if Assigned(FOnGetHelpContext) then + begin + Context := 0; + if Assigned(FFocusedNode) then + begin + Node := FFocusedNode; + // Traverse the tree structure up to the root. + repeat + FOnGetHelpContext(Self, Node, IfThen(FFocusedColumn > NoColumn, FFocusedColumn, 0), Context); + Node := Node.Parent; + until (Node = FRoot) or (Context <> 0); + end; + + // If no help context could be found try the tree's one or its parent's contexts. + ParentControl := Self; + while Assigned(ParentControl) and (Context = 0) do + begin + Context := ParentControl.HelpContext; + ParentControl := ParentControl.Parent; end; + if Context <> 0 then + Application.HelpContext(Context); end; + VK_APPS: + if Assigned(FFocusedNode) then + begin + R := GetDisplayRect(FFocusedNode, FFocusedColumn, True); + Offset := DoGetNodeWidth(FFocusedNode, FFocusedColumn); + if FFocusedColumn >= 0 then + begin + if Offset > FHeader.Columns[FFocusedColumn].Width then + Offset := FHeader.Columns[FFocusedColumn].Width; + end + else + begin + if Offset > ClientWidth then + Offset := ClientWidth; + end; + DoPopupMenu(FFocusedNode, FFocusedColumn, Point(R.Left + Offset div 2, (R.Top + R.Bottom) div 2)); + end + else + DoPopupMenu(nil, FFocusedColumn, Point(-1, -1)); + Ord('a'), Ord('A'): + if ssCtrl in Shift then + SelectAll(True) + else + DoStateChange([tsIncrementalSearchPending]); + else + begin + // Use the key for incremental search. + // Since we are dealing with Unicode all the time there should be a more sophisticated way + // of checking for valid characters for incremental search. + // This is available but would require to include a significant amount of Unicode character + // properties, so we stick with the simple space check. + if ((Shift * [ssCtrl, ssAlt] = []) or ((Shift * [ssCtrl, ssAlt] = [ssCtrl, ssAlt]))) and (CharCode >= 32) then + DoStateChange([tsIncrementalSearchPending]); end; end; end; @@ -19258,9328 +9108,10200 @@ procedure TBaseVirtualTree.DetermineHitPositionRTL(var HitInfo: THitInfo; Offset //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DetermineLineImageAndSelectLevel(Node: PVirtualNode; var LineImage: TLineImage): Integer; - -// This method is used during paint cycles and initializes an array of line type IDs. These IDs are used to paint -// the tree lines in front of the given node. -// Additionally an initial count of selected parents is determined and returned which is used for specific painting. - -var - X: Integer; - Indent: Integer; - Run: PVirtualNode; +procedure TBaseVirtualTree.WMKeyUp(var Message: TWMKeyUp); begin - Result := 0; - if toShowRoot in FOptions.PaintOptions then - X := 1 - else - X := 0; - Run := Node; - // Determine indentation level of top node. - while Run.Parent <> FRoot do - begin - Inc(X); - Run := Run.Parent; - // Count selected nodes (FRoot is never selected). - if vsSelected in Run.States then - Inc(Result); - end; - - // Set initial size of line index array, this will automatically initialized all entries to ltNone. - SetLength(LineImage, X); - Indent := X - 1; - - // Only use lines if requested. - if (toShowTreeLines in FOptions.PaintOptions) and - (not (toHideTreeLinesIfThemed in FOptions.PaintOptions) or not (tsUseThemes in FStates)) then - begin - if toChildrenAbove in FOptions.PaintOptions then - begin - Dec(X); - if not HasVisiblePreviousSibling(Node) then - begin - if (Node.Parent <> FRoot) or HasVisibleNextSibling(Node) then - LineImage[X] := ltBottomRight - else - LineImage[X] := ltRight; - end - else - if (Node.Parent = FRoot) and (not HasVisibleNextSibling(Node)) then - LineImage[X] := ltTopRight - else - LineImage[X] := ltTopDownRight; - - // Now go up to the root to determine the rest. - Run := Node.Parent; - while Run <> FRoot do - begin - Dec(X); - if HasVisiblePreviousSibling(Run) then - LineImage[X] := ltTopDown - else - LineImage[X] := ltNone; - - Run := Run.Parent; - end; - end - else - begin - // Start over parent traversal if necessary. - Run := Node; - - if Run.Parent <> FRoot then - begin - // The very last image (the one immediately before the item label) is different. - if HasVisibleNextSibling(Run) then - LineImage[X - 1] := ltTopDownRight - else - LineImage[X - 1] := ltTopRight; - Run := Run.Parent; - - // Now go up all parents. - repeat - if Run.Parent = FRoot then - Break; - Dec(X); - if HasVisibleNextSibling(Run) then - LineImage[X - 1] := ltTopDown - else - LineImage[X - 1] := ltNone; - Run := Run.Parent; - until False; - end; + inherited; - // Prepare root level. Run points at this stage to a top level node. - if (toShowRoot in FOptions.PaintOptions) and ((toShowTreeLines in FOptions.PaintOptions) and - (not (toHideTreeLinesIfThemed in FOptions.PaintOptions) or not (tsUseThemes in FStates))) then - begin - // Is the top node a root node? - if Run = Node then - begin - // First child gets the bottom-right bitmap if it isn't also the only child. - if IsFirstVisibleChild(FRoot, Run) then - // Is it the only child? - if IsLastVisibleChild(FRoot, Run) then - LineImage[0] := ltRight - else - LineImage[0] := ltBottomRight - else - // real last child - if IsLastVisibleChild(FRoot, Run) then - LineImage[0] := ltTopRight - else - LineImage[0] := ltTopDownRight; - end - else - begin - // No, top node is not a top level node. So we need different painting. - if HasVisibleNextSibling(Run) then - LineImage[0] := ltTopDown - else - LineImage[0] := ltNone; - end; - end; - end; + case Message.CharCode of + VK_TAB: + EnsureNodeFocused(); // Always select a node if the control gets the focus via TAB key, #237 end; - - if (tsUseExplorerTheme in FStates) and HasChildren[Node] and (Indent >= 0) - and not ((vsAllChildrenHidden in Node.States) and (toAutoHideButtons in TreeOptions.AutoOptions)) then - LineImage[Indent] := ltNone; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DetermineNextCheckState(CheckType: TCheckType; CheckState: TCheckState): TCheckState; +procedure TBaseVirtualTree.WMKillFocus(var Msg: TWMKillFocus); -// Determines the next check state in case the user click the check image or pressed the space key. +var + Form: TCustomForm; + Control: TWinControl; + Pos: TSmallPoint; + Unknown: IUnknown; begin - case CheckType of - ctTriStateCheckBox, - ctButton, - ctCheckBox: - begin - Result := CheckState.GetToggled(); - end;//ctCheckbox - ctRadioButton: - Result := csCheckedNormal; - else - Result := csMixedNormal; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- + inherited; -function TBaseVirtualTree.DetermineScrollDirections(X, Y: Integer): TScrollDirections; + // Remove hint if shown currently. + if tsHint in Self.FStates then + Application.CancelHint; -// Determines which direction the client area must be scrolled depending on the given position. + // Stop wheel panning if active. + StopWheelPanning; -begin - Result:= []; + // Don't let any timer continue if the tree is no longer the active control (except change timers). + StopTimer(ExpandTimer); + StopTimer(EditTimer); + StopTimer(HeaderTimer); + StopTimer(ScrollTimer); + StopTimer(SearchTimer); + FSearchBuffer := ''; + FLastSearchNode := nil; - if CanAutoScroll then - begin - // Calculation for wheel panning/scrolling is a bit different to normal auto scroll. - if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then - begin - if (X - FLastClickPos.X) < -8 then - Include(Result, sdLeft); - if (X - FLastClickPos.X) > 8 then - Include(Result, sdRight); + DoStateChange([], [tsScrollPending, tsScrolling, tsEditPending, tsLeftButtonDown, tsRightButtonDown, + tsMiddleButtonDown, tsOLEDragPending, tsVCLDragPending, tsIncrementalSearching, tsNodeHeightTrackPending, + tsNodeHeightTracking]); - if (Y - FLastClickPos.Y) < -8 then - Include(Result, sdUp); - if (Y - FLastClickPos.Y) > 8 then - Include(Result, sdDown); - end - else - begin - if (X < Integer(FDefaultNodeHeight)) and (FEffectiveOffsetX <> 0) then - Include(Result, sdLeft); - if (ClientWidth + FEffectiveOffsetX < Integer(FRangeX)) and (X > ClientWidth - Integer(FDefaultNodeHeight)) then - Include(Result, sdRight); + if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.PaintOptions) then + Invalidate + else + if Assigned(FFocusedNode) then + InvalidateNode(FFocusedNode); - if (Y < Integer(FDefaultNodeHeight)) and (FOffsetY <> 0) then - Include(Result, sdUp); - if (ClientHeight - FOffsetY < Integer(FRangeY)) and (Y > ClientHeight - Integer(FDefaultNodeHeight)) then - Include(Result, sdDown); + // Workaround for wrapped non-VCL controls (like TWebBrowser), which do not use VCL mechanisms and + // leave the ActiveControl property in the wrong state, which causes trouble when the control is refocused. + Form := GetParentForm(Self); + if Assigned(Form) and (Form.ActiveControl = Self) then + begin + Cardinal(Pos) := GetMessagePos; + Control := FindVCLWindow(SmallPointToPoint(Pos)); + // Every control derived from TOleControl has potentially the focus problem. In order to avoid including + // the OleCtrls unit (which will, among others, include Variants), which would allow to test for the TOleControl + // class, the IOleClientSite interface is used for the test, which is supported by TOleControl and a good indicator. + if Assigned(Control) and Control.GetInterface(IOleClientSite, Unknown) then + Form.ActiveControl := nil; - // Since scrolling during dragging is not handled via the timer we do a check here whether the auto - // scroll timeout already has elapsed or not. - if (Result <> []) and - ((Assigned(FDragManager) and DragManager.IsDropTarget) or - (FindDragTarget(Point(X, Y), False) = Self)) then - begin - if FDragScrollStart = 0 then - FDragScrollStart := timeGetTime; - // Reset any scroll direction to avoid scroll in the case the user is dragging and the auto scroll time has not - // yet elapsed. - if ((Int64(timeGetTime) - FDragScrollStart) < FAutoScrollDelay) then - Result := []; - end; - end; + // For other classes the active control should not be modified. Otherwise you need two clicks to select it. end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoAdvancedHeaderDraw(var PaintInfo: THeaderPaintInfo; const Elements: THeaderPaintElements); +procedure TBaseVirtualTree.WMLButtonDblClk(var Message: TWMLButtonDblClk); + +var + HitInfo: THitInfo; begin - if Assigned(FOnAdvancedHeaderDraw) then - FOnAdvancedHeaderDraw(FHeader, PaintInfo, Elements); + DoStateChange([tsLeftDblClick]); + try + // get information about the hit, before calling inherited, is this may change the scroll postion and so the node under the mouse would chnage and would no longer be the one the user actually clicked + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + HandleMouseDblClick(Message, HitInfo); + // Call inherited after doing our standard handling, as the event handler may close the form or re-fill the control, so our clicked node would be no longer valid. + // Our standard handling does not do that. + inherited; + // #909 + // if we show a modal form in the HandleMouseDblClick(), the mouse capture wont be released + if csCaptureMouse in ControlStyle then MouseCapture := False; + finally + DoStateChange([], [tsLeftDblClick]); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoAfterCellPaint(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellRect: TRect); +procedure TBaseVirtualTree.WMLButtonDown(var Message: TWMLButtonDown); + +var + HitInfo: THitInfo; begin - if Assigned(FOnAfterCellPaint) then - FOnAfterCellPaint(Self, Canvas, Node, Column, CellRect); + DoStateChange([tsLeftButtonDown]); + inherited; + + // get information about the hit + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + HandleMouseDown(Message, HitInfo); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoAfterItemErase(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect); +procedure TBaseVirtualTree.WMLButtonUp(var Message: TWMLButtonUp); + +var + HitInfo: THitInfo; begin - if Assigned(FOnAfterItemErase) then - FOnAfterItemErase(Self, Canvas, Node, ItemRect); + DoStateChange([], [tsLeftButtonDown, tsNodeHeightTracking, tsNodeHeightTrackPending]); + + // get information about the hit + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + HandleMouseUp(Message, HitInfo); + + inherited; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoAfterItemPaint(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect); +procedure TBaseVirtualTree.WMMButtonDblClk(var Message: TWMMButtonDblClk); + +var + HitInfo: THitInfo; begin - if Assigned(FOnAfterItemPaint) then - FOnAfterItemPaint(Self, Canvas, Node, ItemRect); + DoStateChange([tsMiddleDblClick]); + inherited; + + // get information about the hit + if toMiddleClickSelect in FOptions.SelectionOptions then + begin + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + HandleMouseDblClick(Message, HitInfo); + end; + DoStateChange([], [tsMiddleDblClick]); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoAfterPaint(Canvas: TCanvas); +procedure TBaseVirtualTree.WMMButtonDown(var Message: TWMMButtonDown); + +var + HitInfo: THitInfo; begin - if Assigned(FOnAfterPaint) then - FOnAfterPaint(Self, Canvas); + DoStateChange([tsMiddleButtonDown]); + + if FHeader.States = [] then + begin + inherited; + + // Start wheel panning or scrolling if not already active, allowed and scrolling is useful at all. + if (toWheelPanning in FOptions.MiscOptions) and ([tsWheelScrolling, tsWheelPanning] * FStates = []) and + ((Integer(FRangeX) > ClientWidth) or (Integer(FRangeY) > ClientHeight)) then + begin + FLastClickPos := SmallPointToPoint(Message.Pos); + StartWheelPanning(FLastClickPos); + end + else + begin + StopWheelPanning; + + // Get information about the hit. + if toMiddleClickSelect in FOptions.SelectionOptions then + begin + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + HandleMouseDown(Message, HitInfo); + end; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoAutoScroll(X, Y: Integer); +procedure TBaseVirtualTree.WMMButtonUp(var Message: TWMMButtonUp); + +var + HitInfo: THitInfo; begin - FScrollDirections := DetermineScrollDirections(X, Y); + DoStateChange([], [tsMiddleButtonDown]); - if FStates * [tsWheelPanning, tsWheelScrolling] = [] then + // If wheel panning/scrolling is active and the mouse has not yet been moved then the user starts wheel auto scrolling. + // Indicate this by removing the panning flag. Otherwise (the mouse has moved meanwhile) stop panning. + if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then begin - if FScrollDirections = [] then - begin - if ((FStates * [tsScrollPending, tsScrolling]) <> []) then - begin - StopTimer(ScrollTimer); - DoStateChange([], [tsScrollPending, tsScrolling]); - end; - end + if tsWheelScrolling in FStates then + DoStateChange([], [tsWheelPanning]) else + StopWheelPanning; + end + else + if FHeader.States = [] then begin - // start auto scroll if not yet done - if (FStates * [tsScrollPending, tsScrolling]) = [] then + inherited; + + // get information about the hit + if toMiddleClickSelect in FOptions.SelectionOptions then begin - DoStateChange([tsScrollPending]); - SetTimer(Handle, ScrollTimer, FAutoScrollDelay, nil); + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + HandleMouseUp(Message, HitInfo); end; end; - end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoBeforeDrag(Node: PVirtualNode; Column: TColumnIndex): Boolean; +procedure TBaseVirtualTree.WMNCCalcSize(var Message: TWMNCCalcSize); begin - Result := False; - if Assigned(FOnDragAllowed) then - FOnDragAllowed(Self, Node, Column, Result); + inherited; + + with FHeader do + if hoVisible in FHeader.Options then + with Message.CalcSize_Params^ do + Inc(rgrc[0].Top, Height); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoBeforeCellPaint(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; - CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect); +procedure TBaseVirtualTree.WMNCDestroy(var Message: TWMNCDestroy); -var - UpdateRect: TRect; +// Used to release a reference of the drag manager. This is the only reliable way we get notified about +// window destruction, because of the automatic release of a window if its parent window is freed. begin - if Assigned(FOnBeforeCellPaint) then - begin - if CellPaintMode = cpmGetContentMargin then - begin - // Prevent drawing if we are only about to get the margin. As this also clears the update rect we need to save it. - GetUpdateRect(Handle, UpdateRect, False); - SetUpdateState(True); - end; + InterruptValidation; - Canvas.Font := Self.Font; // Fixes issue #298 - FOnBeforeCellPaint(Self, Canvas, Node, Column, CellPaintMode, CellRect, ContentRect); + StopTimer(ChangeTimer); + StopTimer(StructureChangeTimer); - if CellPaintMode = cpmGetContentMargin then - SetUpdateState(False); - end; + if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.MiscOptions) then + RevokeDragDrop(Handle); + + // Clean up other stuff. + DeleteObject(FDottedBrush); + FDottedBrush := 0; + inherited; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoBeforeItemErase(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect; var Color: TColor; - var EraseAction: TItemEraseAction); +procedure TBaseVirtualTree.WMNCHitTest(var Message: TWMNCHitTest); begin - if Assigned(FOnBeforeItemErase) then - FOnBeforeItemErase(Self, Canvas, Node, ItemRect, Color, EraseAction); + inherited; + if (hoVisible in FHeader.Options) and + FHeader.InHeader(ScreenToClient(SmallPointToPoint(Message.Pos))) then + Message.Result := HTBORDER; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoBeforeItemPaint(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect): Boolean; -begin - // By default custom draw will not be used, so the tree handles drawing the node. - Result := False; - if Assigned(FOnBeforeItemPaint) then - FOnBeforeItemPaint(Self, Canvas, Node, ItemRect, Result); +procedure TBaseVirtualTree.WMNCPaint(var Message: TWMNCPaint); + +var + DC: HDC; + R: TRect; + Flags: DWORD; + ExStyle: Integer; + TempRgn: HRGN; + BorderWidth, + BorderHeight: Integer; + +begin + if tsUseThemes in FStates then + begin + // If theming is enabled and the client edge border is set for the window then prevent the default window proc + // from painting the old border to avoid flickering. + ExStyle := GetWindowLong(Handle, GWL_EXSTYLE); + if (ExStyle and WS_EX_CLIENTEDGE) <> 0 then + begin + GetWindowRect(Handle, R); + // Determine width of the client edge. + BorderWidth := GetSystemMetrics(SM_CXEDGE); + BorderHeight := GetSystemMetrics(SM_CYEDGE); + InflateRect(R, -BorderWidth, -BorderHeight); + TempRgn := CreateRectRgnIndirect(R); + // Exclude the border from the message region if there is one. Otherwise just use the inflated + // window area region. + if Message.Rgn <> 1 then + CombineRgn(TempRgn, Message.Rgn, TempRgn, RGN_AND); + DefWindowProc(Handle, Message.Msg, WPARAM(TempRgn), 0); + DeleteObject(TempRgn); + end + else + DefaultHandler(Message); + end + else + DefaultHandler(Message); + + Flags := DCX_CACHE or DCX_CLIPSIBLINGS or DCX_WINDOW or DCX_VALIDATE; + + if (Message.Rgn = 1) then + DC := GetDCEx(Handle, 0, Flags) + else + DC := GetDCEx(Handle, Message.Rgn, Flags or DCX_INTERSECTRGN); + + if DC <> 0 then + try + OriginalWMNCPaint(DC); + finally + ReleaseDC(Handle, DC); + end; + if (((tsUseThemes in FStates) and not VclStyleEnabled) or (VclStyleEnabled and (seBorder in StyleElements))) then + StyleServices.PaintBorder(Self, False) + else + if (VclStyleEnabled and not (seBorder in StyleElements)) then + TStyleManager.SystemStyle.PaintBorder(Self, False) end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoBeforePaint(Canvas: TCanvas); - +procedure TBaseVirtualTree.WMPaint(var Message: TWMPaint); +var + DC: HDC; begin - if Assigned(FOnBeforePaint) then - FOnBeforePaint(Self, Canvas); -end; - -//---------------------------------------------------------------------------------------------------------------------- + if tsVCLDragging in FStates then + ImageList_DragShowNolock(False); + if csPaintCopy in ControlState then + FUpdateRect := ClientRect + else + GetUpdateRect(Handle, FUpdateRect, True); -function TBaseVirtualTree.DoCancelEdit(): Boolean; + inherited; -// Called when the current edit action or a pending edit must be cancelled. + if tsVCLDragging in FStates then + ImageList_DragShowNolock(True); -begin - StopTimer(EditTimer); - DoStateChange([], [tsEditPending]); - Result := (tsEditing in FStates) and FEditLink.CancelEdit; - if Result then + if hoVisible in FHeader.Options then begin - DoStateChange([], [tsEditing]); - if Assigned(FOnEditCancelled) then - FOnEditCancelled(Self, FEditColumn); - if not (csDestroying in ComponentState) then - FEditLink := nil; - TrySetFocus(); - end; + DC := GetDCEx(Handle, 0, DCX_CACHE or DCX_CLIPSIBLINGS or DCX_WINDOW or DCX_VALIDATE); + if DC <> 0 then + try + FHeader.Columns.PaintHeader(DC, FHeaderRect, -FEffectiveOffsetX); + finally + ReleaseDC(Handle, DC); + end; + end;//if header visible end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoCanEdit(Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); +procedure TBaseVirtualTree.WMPaste(var Message: TWMPaste); begin - if Assigned(FOnEditing) then - FOnEditing(Self, Node, Column, Allowed); + PasteFromClipboard; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoCanSplitterResizeNode(P: TPoint; Node: PVirtualNode; Column: TColumnIndex; - var Allowed: Boolean); - -begin - if Assigned(FOnCanSplitterResizeNode) then - FOnCanSplitterResizeNode(Self, P, Node, Column, Allowed); -end; - -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.WMPrint(var Message: TWMPrint); -procedure TBaseVirtualTree.DoChange(Node: PVirtualNode); +// This message is sent to request that the tree draws itself to a given device context. This includes not only +// the client area but also the non-client area (header!). begin - StopTimer(ChangeTimer); - if Assigned(FOnChange) then - FOnChange(Self, Node); + // Draw only if the window is visible or visibility is not required. + if ((Message.Flags and PRF_CHECKVISIBLE) = 0) or IsWindowVisible(Handle) then + Header.Columns.PaintHeader(Message.DC, FHeaderRect, -FEffectiveOffsetX); - // This is a good place to reset the cached node. This is the same as the node passed in here. - // This is necessary to allow descendants to override this method and get the node then. - DoStateChange([], [tsChangePending]); - FLastChangedNode := nil; + inherited; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoCheckClick(Node: PVirtualNode; NewCheckState: TCheckState); +procedure TBaseVirtualTree.WMPrintClient(var Message: TWMPrintClient); + +var + Window: TRect; + Target: TPoint; + Canvas: TCanvas; begin - if ChangeCheckState(Node, NewCheckState) then + // Draw only if the window is visible or visibility is not required. + if ((Message.Flags and PRF_CHECKVISIBLE) = 0) or IsWindowVisible(Handle) then begin - DoChecked(Node); - if SyncCheckstateWithSelection[Node] then - begin - // selection should follow check state - if (NewCheckState = csCheckedNormal) then - Selected[node] := true - else - Selected[node] := false; - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- + // Determine area of the entire tree to be displayed in the control. + Window := ClientRect; + Target := Window.TopLeft; -procedure TBaseVirtualTree.DoChecked(Node: PVirtualNode); + // The Window rectangle is given in client coordinates. We have to convert it into + // a sliding window of the tree image. + OffsetRect(Window, FEffectiveOffsetX, -FOffsetY); -begin - if Assigned(FOnChecked) then - FOnChecked(Self, Node); - if Assigned(FAccessibleItem) then - NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); + Canvas := TCanvas.Create; + try + Canvas.Handle := Message.DC; + PaintTree(Canvas, Window, Target, [poBackground, poDrawFocusRect, poDrawDropMark, poDrawSelection, poGridLines]); + finally + Canvas.Handle := 0; + Canvas.Free; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoChecking(Node: PVirtualNode; var NewCheckState: TCheckState): Boolean; +procedure TBaseVirtualTree.WMRButtonDblClk(var Message: TWMRButtonDblClk); -// Determines if a node is allowed to change its check state to NewCheckState. +var + HitInfo: THitInfo; begin - if (toReadOnly in FOptions.MiscOptions) or (vsDisabled in Node.States) then - Result := False - else + DoStateChange([tsRightDblClick]); + inherited; + + // get information about the hit + if toMiddleClickSelect in FOptions.SelectionOptions then begin - Result := True; - if Assigned(FOnChecking) then - FOnChecking(Self, Node, NewCheckState, Result); + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + HandleMouseDblClick(Message, HitInfo); end; + DoStateChange([], [tsRightDblClick]); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoCollapsed(Node: PVirtualNode); -var - lFirstSelected: PVirtualNode; - lParent: PVirtualNode; -begin - if Assigned(FOnCollapsed) then - FOnCollapsed(Self, Node); - - if Assigned(FAccessibleItem) then - NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); - - if (toAlwaysSelectNode in TreeOptions.SelectionOptions) then - begin - // Select the next visible parent if the currently selected node gets invisible due to a collapse - // This makes the VT behave more like the Win32 custom TreeView control - // This makes only sense no no multi selection is allowed and if there is a selected node at all - lFirstSelected := GetFirstSelected(); - if Assigned(lFirstSelected) and not FullyVisible[lFirstSelected] then - begin - lParent := GetVisibleParent(lFirstSelected); - Selected[lFirstSelected] := False; - Selected[lParent] := True; - end;//if - //if there is (still) no selected node, then use FNextNodeToSelect to select one - if SelectedCount = 0 then - EnsureNodeSelected(); - end;//if -end; - -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.WMRButtonDown(var Message: TWMRButtonDown); -function TBaseVirtualTree.DoCollapsing(Node: PVirtualNode): Boolean; +var + HitInfo: THitInfo; + RemoveSynchMode: Boolean; // Needed to restore tsSynchMode correctly begin - Result := True; - if Assigned(FOnCollapsing) then - FOnCollapsing(Self, Node, Result); -end; - -//---------------------------------------------------------------------------------------------------------------------- + DoStateChange([tsRightButtonDown]); -procedure TBaseVirtualTree.DoColumnClick(Column: TColumnIndex; Shift: TShiftState); + if FHeader.States = [] then + begin + inherited; -begin - if Assigned(FOnColumnClick) then - FOnColumnClick(Self, Column, Shift); + // get information about the hit + if toRightClickSelect in FOptions.SelectionOptions then + begin + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + // Go temporarily into sync mode to avoid a delayed change event for the node when selecting. #679 + RemoveSynchMode := not (tsSynchMode in FStates); + Include(FStates, tsSynchMode); + HandleMouseDown(Message, HitInfo); + if RemoveSynchMode then + Exclude(FStates, tsSynchMode); + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoColumnDblClick(Column: TColumnIndex; Shift: TShiftState); - -begin - if Assigned(FOnColumnDblClick) then - FOnColumnDblClick(Self, Column, Shift); -end; - -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.WMRButtonUp(var Message: TWMRButtonUp); -procedure TBaseVirtualTree.DoColumnResize(Column: TColumnIndex); +// handle right click selection and node specific popup menu var - R: TRect; - Run: PVirtualNode; + HitInfo: THitInfo; begin - if not (csLoading in ComponentState) and HandleAllocated then -begin - // Reset all vsHeightMeasured flags if we are in multiline mode. - Run := GetFirstInitialized; - while Assigned(Run) do - begin - if vsMultiline in Run.States then - Exclude(Run.States, vsHeightMeasured); - Run := GetNextInitialized(Run); - end; + DoStateChange([], [tsPopupMenuShown, tsRightButtonDown]); - UpdateHorizontalScrollBar(True); - if Column > NoColumn then - begin - // Invalidate client area from the current column all to the right (or left in RTL mode). - R := ClientRect; - if not (toAutoSpanColumns in FOptions.AutoOptions) then - if UseRightToLeftAlignment then - R.Right := FHeader.Columns[Column].Left + FHeader.Columns[Column].Width + ComputeRTLOffset - else - R.Left := FHeader.Columns[Column].Left; - InvalidateRect(Handle, @R, False); - FHeader.Invalidate(FHeader.Columns[Column], True); + if FHeader.States = [] then + begin + Application.CancelHint; + + if IsMouseSelecting and Assigned(PopupMenu) then + begin + // Reset selection state already here, before the inherited handler opens the default menu. + DoStateChange([], [tsDrawSelecting, tsDrawSelPending]); + Invalidate; end; - if [hsColumnWidthTracking, hsResizing] * FHeader.States = [hsColumnWidthTracking] then - UpdateWindow(Handle); - if not (IsUpdating) then - UpdateDesigner; // design time only + inherited; - if Assigned(FOnColumnResize) and not (hsResizing in FHeader.States) then - FOnColumnResize(FHeader, Column); + // get information about the hit + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - // If the tree is currently in edit state then notify edit link. - if tsEditing in FStates then - UpdateEditBounds; + if toRightClickSelect in FOptions.SelectionOptions then + HandleMouseUp(Message, HitInfo); + + if not Assigned(PopupMenu) then + DoPopupMenu(HitInfo.HitNode, HitInfo.HitColumn, Point(Message.XPos, Message.YPos)); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoColumnVisibilityChanged(const Column: TColumnIndex; Visible: Boolean); - // Triggers the OnColumnVisibilityChanged event. -begin - if Assigned(OnColumnVisibilityChanged) then - OnColumnVisibilityChanged(Self, Column, Visible); -end; +procedure TBaseVirtualTree.WMSetCursor(var Message: TWMSetCursor); -//---------------------------------------------------------------------------------------------------------------------- +// Sets the hot node mouse cursor for the tree. Cursor changes for the header are handled in Header.HandleMessage. -function TBaseVirtualTree.DoCompare(Node1, Node2: PVirtualNode; Column: TColumnIndex): Integer; +var + NewCursor: TCursor; + HitInfo: THitInfo; + P: TPoint; + Node: PVirtualNode; begin - Result := 0; - if Assigned(FOnCompareNodes) then - FOnCompareNodes(Self, Node1, Node2, Column, Result); -end; + with Message do + begin + // Feature: design-time header #415 + // Allow header to handle cursor and return control's default if it did nothing + if (CursorWnd = Handle) and + ([tsWheelPanning, tsWheelScrolling] * FStates = []) then + begin + if not TVTHeaderCracker(FHeader).HandleMessage(TMessage(Message)) then + begin + // Apply own cursors only if there is no global cursor set. + if Screen.Cursor = crDefault then + begin + // node resizing and hot tracking - for run-time only + if not (csDesigning in ComponentState) then + begin + NewCursor := crDefault; + if (toNodeHeightResize in FOptions.MiscOptions) then + begin + GetCursorPos(P); + P := ScreenToClient(P); + GetHitTestInfoAt(P.X, P.Y, True, HitInfo); + if (hiOnItem in HitInfo.HitPositions) and + ([hiUpperSplitter, hiLowerSplitter] * HitInfo.HitPositions <> []) then + begin + if hiUpperSplitter in HitInfo.HitPositions then + Node := GetPreviousVisible(HitInfo.HitNode, True) + else + Node := HitInfo.HitNode; -//---------------------------------------------------------------------------------------------------------------------- + if CanSplitterResizeNode(P, Node, HitInfo.HitColumn) then + NewCursor := crVertSplit; + end; + end; -function TBaseVirtualTree.DoCreateDataObject: IDataObject; + if (NewCursor = crDefault) then + if (toHotTrack in FOptions.PaintOptions) and Assigned(FCurrentHotNode) and (FHotCursor <> crDefault) then + NewCursor := FHotCursor + else + NewCursor := Cursor; -begin - Result := nil; - if Assigned(FOnCreateDataObject) then - FOnCreateDataObject(Self, Result); + DoGetCursor(NewCursor); + end + else + NewCursor := Cursor; + Winapi.Windows.SetCursor(Screen.Cursors[NewCursor]); + Message.Result := 1; + end + else + inherited; + end; + end + else + inherited; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoCreateDragManager: IVTDragManager; +procedure TBaseVirtualTree.WMSetFocus(var Msg: TWMSetFocus); begin - Result := nil; - if Assigned(FOnCreateDragManager) then - FOnCreateDragManager(Self, Result); + inherited; + if (FSelectionCount > 0) or not (toGhostedIfUnfocused in FOptions.PaintOptions) then + Invalidate; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoCreateEditor(Node: PVirtualNode; Column: TColumnIndex): IVTEditLink; +procedure TBaseVirtualTree.WMSize(var Message: TWMSize); begin - Result := nil; - if Assigned(FOnCreateEditor) then - FOnCreateEditor(Self, Node, Column, Result); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.DoDragging(P: TPoint); - -// Initiates finally the drag'n drop operation and returns after DD is finished. - - //--------------- local function -------------------------------------------- + inherited; - function GetDragOperations: Integer; + // Need to update scroll bars here. This will cause a recursion because of the change of the client area + // when changing a scrollbar. Usually this is no problem since with the second level recursion no change of the + // window size happens (the same values for the scrollbars are set, which shouldn't cause a window size change). + // Appearently, this applies not to all systems, however. + if HandleAllocated and ([tsSizing, tsWindowCreating] * FStates = []) and (ClientHeight > 0) then + try + DoStateChange([tsSizing]); + // This call will invalidate the entire non-client area which needs recalculation on resize. + TVTHeaderCracker(FHeader).RescaleHeader; + TVTHeaderCracker(FHeader).UpdateSpringColumns; + UpdateScrollBars(True); - begin - if FDragOperations = [] then - Result := DROPEFFECT_COPY or DROPEFFECT_MOVE or DROPEFFECT_LINK - else - begin - Result := 0; - if doCopy in FDragOperations then - Result := Result or DROPEFFECT_COPY; - if doLink in FDragOperations then - Result := Result or DROPEFFECT_LINK; - if doMove in FDragOperations then - Result := Result or DROPEFFECT_MOVE; - end; + if (tsEditing in FStates) and not FHeader.UseColumns then + UpdateEditBounds; + finally + DoStateChange([], [tsSizing]); end; +end; - //--------------- end local function ---------------------------------------- - -var - AllowedEffects: Integer; - DragObject: TDragObject; +//---------------------------------------------------------------------------------------------------------------------- - DataObject: IDataObject; +procedure TBaseVirtualTree.WMThemeChanged(var Message: TMessage); begin - DataObject := nil; - // Dragging is dragging, nothing else. - DoCancelEdit; - - if Assigned(FCurrentHotNode) then - begin - InvalidateNode(FCurrentHotNode); - FCurrentHotNode := nil; - end; - // Select the focused node if not already done. - if Assigned(FFocusedNode) and not (vsSelected in FFocusedNode.States) then - begin - InternalAddToSelection(FFocusedNode, False); - InvalidateNode(FFocusedNode); - end; - - UpdateWindow(Handle); - - // Keep a list of all currently selected nodes as this list might change, - // but we have probably to delete currently selected nodes. - FDragSelection := GetSortedSelection(True); - try - DoStateChange([tsOLEDragging], [tsOLEDragPending, tsClearPending]); + inherited; - // An application might create a drag object like used during VCL dd. This is not required for OLE dd but - // required as parameter. - DragObject := nil; - DoStartDrag(DragObject); - DragObject.Free; + if StyleServices.Enabled and (toThemeAware in TreeOptions.PaintOptions) then + DoStateChange([tsUseThemes]) + else + DoStateChange([], [tsUseThemes]); - DataObject := DragManager.DataObject; - PrepareDragImage(P, DataObject); + // Updating the visuals here will not work correctly. Therefore we postpone + // the update by using a timer. + if not FChangingTheme then + SetTimer(Handle, ThemeChangedTimer, ThemeChangedTimerDelay, nil); + FChangingTheme := False; +end; - FLastDropMode := dmOnNode; - // Don't forget to initialize the result. It might never be touched. - FLastDragEffect := DROPEFFECT_NONE; - AllowedEffects := GetDragOperations; - try - DragAndDrop(AllowedEffects, DataObject, FLastDragEffect); - DragManager.ForceDragLeave; - finally - GetCursorPos(P); - P := ScreenToClient(P); - DoEndDrag(Self, P.X, P.Y); +//---------------------------------------------------------------------------------------------------------------------- - FDragImage.EndDrag; +procedure TBaseVirtualTree.WMTimer(var Message: TWMTimer); - // Finish the operation. - if (FLastDragEffect = DROPEFFECT_MOVE) and (toAutoDeleteMovedNodes in TreeOptions.AutoOptions) then - begin - // The operation was a move so delete the previously selected nodes. - DeleteSelectedNodes; - end; +// centralized timer handling happens here - DoStateChange([], [tsOLEDragging]); +begin + with Message do + begin + case TimerID of + ExpandTimer: + DoDragExpand; + EditTimer: + DoEdit; + ScrollTimer: + begin + if tsScrollPending in FStates then + begin + Application.CancelHint; + // Scroll delay has elapsed, set to normal scroll interval now. + SetTimer(Handle, ScrollTimer, FAutoScrollInterval, nil); + DoStateChange([tsScrolling], [tsScrollPending]); + end; + DoTimerScroll; + end; + ChangeTimer: + if tsChangePending in FStates then // see issue #602 + DoChange(FLastChangedNode); + StructureChangeTimer: + DoStructureChange(FLastStructureChangeNode, FLastStructureChangeReason); + SearchTimer: + begin + // When this event triggers then the user did not pressed any key for the specified timeout period. + // Hence incremental searching is stopped. + DoStateChange([], [tsIncrementalSearching]); + StopTimer(SearchTimer); + FSearchBuffer := ''; + FLastSearchNode := nil; + end; + ThemeChangedTimer: + begin + StopTimer(ThemeChangedTimer); + RecreateWnd; + end; end; - finally - FDragSelection := nil; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoDragExpand; - -var - SourceTree: TBaseVirtualTree; - -begin - StopTimer(ExpandTimer); - if Assigned(FDropTargetNode) and (vsHasChildren in FDropTargetNode.States) and - not (vsExpanded in FDropTargetNode.States) then - begin - if Assigned(FDragManager) then - SourceTree := DragManager.DragSource - else - SourceTree := nil; - - if not DragManager.DropTargetHelperSupported and Assigned(SourceTree) then - SourceTree.FDragImage.HideDragImage; - ToggleNode(FDropTargetNode); - UpdateWindow(Handle); - if not DragManager.DropTargetHelperSupported and Assigned(SourceTree) then - SourceTree.FDragImage.ShowDragImage; - end; -end; +procedure TBaseVirtualTree.WMVScroll(var Message: TWMVScroll); -//---------------------------------------------------------------------------------------------------------------------- + //--------------- local functions ------------------------------------------- -function TBaseVirtualTree.DoDragOver(Source: TObject; Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode; - var Effect: Integer): Boolean; + function GetRealScrollPosition: Integer; -begin - Result := False; - if Assigned(FOnDragOver) then - FOnDragOver(Self, Source, Shift, State, Pt, Mode, Effect, Result); -end; + var + SI: TScrollInfo; + Code: Integer; -//---------------------------------------------------------------------------------------------------------------------- + begin + SI.cbSize := SizeOf(TScrollInfo); + SI.fMask := SIF_TRACKPOS; + Code := SB_VERT; + GetScrollInfo(Handle, Code, SI); + Result := SI.nTrackPos; + end; -procedure TBaseVirtualTree.DoDragDrop(Source: TObject; const DataObject: IDataObject; const Formats: TFormatArray; - Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode); + //--------------- end local functions --------------------------------------- begin - if Assigned(FOnDragDrop) then - FOnDragDrop(Self, Source, DataObject, Formats, Shift, Pt, Effect, Mode); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.DoBeforeDrawLineImage(Node: PVirtualNode; Level: Integer; var XPos: Integer); + case Message.ScrollCode of + SB_BOTTOM: + SetOffsetY(-Integer(FRoot.TotalHeight)); + SB_ENDSCROLL: + begin + DoStateChange([], [tsThumbTracking]); + // Avoiding to adjust the horizontal scroll position while tracking makes scrolling much smoother + // but we need to adjust the final position here then. + UpdateScrollBars(True); + // Really weird invalidation needed here (and I do it only because it happens so rarely), because + // when showing the horizontal scrollbar while scrolling down using the down arrow button, + // the button will be repainted on mouse up (at the wrong place in the far right lower corner)... + RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE or RDW_NOERASE or RDW_NOCHILDREN); + end; + SB_LINEUP: + SetOffsetY(FOffsetY + FScrollBarOptions.VerticalIncrement); + SB_LINEDOWN: + SetOffsetY(FOffsetY - FScrollBarOptions.VerticalIncrement); + SB_PAGEUP: + SetOffsetY(FOffsetY + ClientHeight); + SB_PAGEDOWN: + SetOffsetY(FOffsetY - ClientHeight); -begin - if Assigned(FOnBeforeDrawLineImage) then - FOnBeforeDrawLineImage(Self, Node, Level, XPos); + SB_THUMBPOSITION, + SB_THUMBTRACK: + begin + DoStateChange([tsThumbTracking]); + SetOffsetY(-GetRealScrollPosition); + end; + SB_TOP: + SetOffsetY(0); + end; + Message.Result := 0; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoEdit; +procedure TBaseVirtualTree.AddToSelection(Node: PVirtualNode; NotifySynced: Boolean); +var + Changed: Boolean; + RemoveSyncAfterChange: Boolean; begin - Application.CancelHint; - StopTimer(ScrollTimer); - StopTimer(EditTimer); - DoStateChange([], [tsEditPending]); - if Assigned(FFocusedNode) and not (vsDisabled in FFocusedNode.States) and - not (toReadOnly in FOptions.MiscOptions) and (FEditLink = nil) then + if not FSelectionLocked then begin - ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, not (toDisableAutoscrollOnEdit in FOptions.AutoOptions)); - FEditLink := DoCreateEditor(FFocusedNode, FEditColumn); - if Assigned(FEditLink) then + Assert(Assigned(Node), 'Node must not be nil!'); + Changed := InternalAddToSelection(Node, False); + if Changed then begin - DoStateChange([tsEditing], [tsDrawSelecting, tsDrawSelPending, tsToggleFocusedSelection, tsOLEDragPending, - tsOLEDragging, tsClearPending, tsDrawSelPending, tsScrollPending, tsScrolling]); - if FEditLink.PrepareEdit(Self, FFocusedNode, FEditColumn) then - begin - UpdateEditBounds; - // Node needs repaint because the selection rectangle and static text must disappear. - InvalidateNode(FFocusedNode); - if not FEditLink.BeginEdit then - DoStateChange([], [tsEditing]); - end - else - DoStateChange([], [tsEditing]); - if not (tsEditing in FStates) then - FEditLink := nil; + UpdateNextNodeToSelect(Node); + if (SelectedCount = 1) then + FocusedNode := Node; // if only one node is selected, make sure the focused node changes with the selected node + InvalidateNode(Node); + RemoveSyncAfterChange := NotifySynced and not (tsSynchMode in fStates); + if RemoveSyncAfterChange then + Include(FStates, tsSynchMode); + try + Change(Node); + finally + if RemoveSyncAfterChange then + Exclude(FStates, tsSynchMode); + end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoEndDrag(Target: TObject; X, Y: Integer); - -// Does some housekeeping for VCL drag'n drop; - -begin - inherited; - - DragFinished; -end; - -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.AddToSelection(const NewItems: TNodeArray; NewLength: Integer; ForceInsert: Boolean = False); -function TBaseVirtualTree.DoEndEdit: Boolean; +// Adds the given items all at once into the current selection array. NewLength is the amount of +// nodes to add (necessary to allow NewItems to be larger than the actual used entries). +// ForceInsert is True if nodes must be inserted without consideration of level select constraint or +// already set selected flags (e.g. when loading from stream). +// Note: In the case ForceInsert is True the caller is responsible for making sure the new nodes aren't already in the +// selection array! -// Called to finish a current edit action or stop the edit timer if an edit operation is pending. -// Returns True if editing was successfully ended or the control was not in edit mode -// Returns False if the control could not leave the edit mode e.g. due to an invalid value that was entered. +var + Changed: Boolean; begin - StopTimer(EditTimer); - Result := (tsEditing in FStates) and FEditLink.EndEdit; - if Result then + Changed := InternalAddToSelection(NewItems, NewLength, ForceInsert); + if Changed then begin - DoStateChange([], [tsEditing]); - FEditLink := nil; - if Assigned(FOnEdited) then - FOnEdited(Self, FFocusedNode, FEditColumn); + if NewLength = 1 then + begin + InvalidateNode(NewItems[0]); + Change(NewItems[0]); + end + else + begin + Invalidate; + Change(nil); + end; end; - DoStateChange([], [tsEditPending]); - TrySetFocus(); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoEndOperation(OperationKind: TVTOperationKind); - -begin - if Assigned(FOnEndOperation) then - FOnEndOperation(Self, OperationKind); -end; +procedure TBaseVirtualTree.AdjustPaintCellRect(var PaintInfo: TVTPaintInfo; var NextNonEmpty: TColumnIndex); -//---------------------------------------------------------------------------------------------------------------------- +// Used in descendants to modify the paint rectangle of the current column while painting a certain node. -procedure TBaseVirtualTree.DoEnter(); begin - inherited; - EnsureNodeSelected(); + // Since cells are always drawn from left to right the next column index is independent of the + // bidi mode, but not the column borders, which might change depending on the cell's content. + NextNonEmpty := FHeader.Columns.GetNextVisibleColumn(PaintInfo.Column); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoExpanded(Node: PVirtualNode); - -begin - if Assigned(FOnExpanded) then - FOnExpanded(Self, Node); - - if Assigned(FAccessibleItem) then - NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); -end; +procedure TBaseVirtualTree.AdjustPanningCursor(X, Y: Integer); -//---------------------------------------------------------------------------------------------------------------------- +// Triggered by a mouse move when wheel panning/scrolling is active. +// Loads the proper cursor which indicates into which direction scrolling is done. -function TBaseVirtualTree.DoExpanding(Node: PVirtualNode): Boolean; +var + Name: string; + NewCursor: HCURSOR; + ScrollHorizontal, + ScrollVertical: Boolean; begin - Result := True; - if Assigned(FOnExpanding) then - FOnExpanding(Self, Node, Result); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.DoFocusChange(Node: PVirtualNode; Column: TColumnIndex); + ScrollHorizontal := Integer(FRangeX) > ClientWidth; + ScrollVertical := Integer(FRangeY) > ClientHeight; -begin - if Assigned(FOnFocusChanged) then - FOnFocusChanged(Self, Node, Column); + if (Abs(X - FLastClickPos.X) < 8) and (Abs(Y - FLastClickPos.Y) < 8) then + begin + // Mouse is in the neutral zone. + if ScrollHorizontal then + begin + if ScrollVertical then + Name := 'VT_MOVEALL' + else + Name := 'VT_MOVEEW'; + end + else + Name := 'VT_MOVENS'; + end + else + begin + // One of 8 directions applies: north, north-east, east, south-east, south, south-west, west and north-west. + // Check also if scrolling in the particular direction is possible. + if ScrollVertical and ScrollHorizontal then + begin + // All directions allowed. + if X - FLastClickPos.X < -8 then + begin + // Left hand side. + if Y - FLastClickPos.Y < -8 then + Name := 'VT_MOVENW' + else + if Y - FLastClickPos.Y > 8 then + Name := 'VT_MOVESW' + else + Name := 'VT_MOVEW'; + end + else + if X - FLastClickPos.X > 8 then + begin + // Right hand side. + if Y - FLastClickPos.Y < -8 then + Name := 'VT_MOVENE' + else + if Y - FLastClickPos.Y > 8 then + Name := 'VT_MOVESE' + else + Name := 'VT_MOVEE'; + end + else + begin + // Up or down. + if Y < FLastClickPos.Y then + Name := 'VT_MOVEN' + else + Name := 'VT_MOVES'; + end; + end + else + if ScrollHorizontal then + begin + // Only horizontal movement allowed. + if X < FLastClickPos.X then + Name := 'VT_MOVEW' + else + Name := 'VT_MOVEE'; + end + else + begin + // Only vertical movement allowed. + if Y < FLastClickPos.Y then + Name := 'VT_MOVEN' + else + Name := 'VT_MOVES'; + end; + end; - if Assigned(FAccessibleItem) then + // Now load the cursor and apply it. + NewCursor := LoadCursor(HInstance, PChar(Name)); + if FPanningCursor <> NewCursor then begin - NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); - NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); - NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); - NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); - NotifyWinEvent(EVENT_OBJECT_SELECTION, Handle, OBJID_CLIENT, CHILDID_SELF); - NotifyWinEvent(EVENT_OBJECT_FOCUS, Handle, OBJID_CLIENT, CHILDID_SELF); - end; + DeleteObject(FPanningCursor); + FPanningCursor := NewCursor; + Winapi.Windows.SetCursor(FPanningCursor); + end + else + DeleteObject(NewCursor); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoFocusChanging(OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex): Boolean; - -begin - Result := (OldColumn = NewColumn) or FHeader.AllowFocus(NewColumn); - if Assigned(FOnFocusChanging) then - FOnFocusChanging(Self, OldNode, NewNode, OldColumn, NewColumn, Result); -end; - -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.AdviseChangeEvent(StructureChange: Boolean; Node: PVirtualNode; Reason: TChangeReason); -procedure TBaseVirtualTree.DoFocusNode(Node: PVirtualNode; Ask: Boolean); +// Used to register a delayed change event. If StructureChange is False then we have a selection change event (without +// a specific reason) otherwise it is a structure change. begin - if not (tsEditing in FStates) or EndEditNode then + if StructureChange then begin - if Node = FRoot then - Node := nil; - if (FFocusedNode <> Node) and (not Ask or DoFocusChanging(FFocusedNode, Node, FFocusedColumn, FFocusedColumn)) then - begin - if Assigned(FFocusedNode) then - begin - // Do automatic collapsing of last focused node if enabled. This is however only done if - // old and new focused node have a common parent node. - if (toAutoExpand in FOptions.AutoOptions) and Assigned(Node) and (Node.Parent = FFocusedNode.Parent) and - (vsExpanded in FFocusedNode.States) then - ToggleNode(FFocusedNode) - else - InvalidateNode(FFocusedNode); - end; - FFocusedNode := Node; - end; + if tsStructureChangePending in FStates then + StopTimer(StructureChangeTimer) + else + DoStateChange([tsStructureChangePending]); - // Have to scroll the node into view, even it is the same node as before. - if Assigned(FFocusedNode) then - begin - // Make sure a valid column is set if columns are used and no column has currently the focus. - if FHeader.UseColumns and (not FHeader.Columns.IsValidColumn(FFocusedColumn)) then - FFocusedColumn := FHeader.MainColumn; - // Do automatic expansion of the newly focused node if enabled. - if (toAutoExpand in FOptions.AutoOptions) and not (vsExpanded in FFocusedNode.States) then - ToggleNode(FFocusedNode); - InvalidateNode(FFocusedNode); - if (FUpdateCount = 0) and not (toDisableAutoscrollOnFocus in FOptions.AutoOptions) then - ScrollIntoView(FFocusedNode, (toCenterScrollIntoView in FOptions.SelectionOptions) and - (MouseButtonDown * FStates = []), not (toFullRowSelect in FOptions.SelectionOptions) ); - end; + FLastStructureChangeNode := Node; + if FLastStructureChangeReason = crIgnore then + FLastStructureChangeReason := Reason + else + if Reason <> crIgnore then + FLastStructureChangeReason := crAccumulated; + end + else + begin + if tsChangePending in FStates then + StopTimer(ChangeTimer) + else + DoStateChange([tsChangePending]); - // Reset range anchor if necessary. - if FSelectionCount = 0 then - ResetRangeAnchor; + FLastChangedNode := Node; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoFreeNode(Node: PVirtualNode); - -var - IntfData: IInterface; -begin - // Prevent invalid references - if Node = FLastChangedNode then - FLastChangedNode := nil; - if Node = FCurrentHotNode then - FCurrentHotNode := nil; - if Node = FDropTargetNode then - FDropTargetNode := nil; - if Node = FLastStructureChangeNode then - FLastStructureChangeNode := nil; - if Node = FFocusedNode then - FFocusedNode := nil; - if Node = FNextNodeToSelect then - UpdateNextNodeToSelect(Node); - if Node = FLastHitInfo.HitNode then - FLastHitInfo.HitNode := nil; - // fire event - if Assigned(FOnFreeNode) and ([vsInitialized, vsOnFreeNodeCallRequired] * Node.States <> []) then - FOnFreeNode(Self, Node); +function TBaseVirtualTree.AllocateInternalDataArea(Size: Cardinal): Cardinal; - if vsReleaseCallOnUserDataRequired in Node.States then - begin - // Data may have been set to nil, in which case we can't call _Release on it - IntfData := GetInterfaceFromNodeData(Node); - if Assigned(IntfData) then - IntfData._Release(); - end; +// Simple registration method to be called by each descendant to claim their internal data area. +// Result is the offset from the begin of the node to the internal data area of the calling tree class. - FreeMem(Node); - if Self.UpdateCount = 0 then - EnsureNodeSelected(); +begin + Assert((FRoot = nil) or (FRoot.ChildCount = 0), 'Internal data allocation must be done before any node is created.'); + Result := TreeNodeSize + FTotalInternalDataSize; + Inc(FTotalInternalDataSize, (Size + (SizeOf(Pointer) - 1)) and not (SizeOf(Pointer) - 1)); + InitRootNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoGetCellContentMargin(Node: PVirtualNode; Column: TColumnIndex; - CellContentMarginType: TVTCellContentMarginType = ccmtAllSides; Canvas: TCanvas = nil): TPoint; +procedure TBaseVirtualTree.Animate(Steps, Duration: Cardinal; Callback: TVTAnimationCallback; Data: Pointer); -// Determines the margins of the content rectangle caused by DoBeforeCellPaint. -// Note that shrinking the content rectangle results in positive margins whereas enlarging the content rectangle results -// in negative margins. +// This method does the calculation part of an animation as used for node toggling and hint animations. +// Steps is the maximum amount of animation steps to do and Duration determines the milliseconds the animation +// has to run. Callback is a task specific method which is called in the loop for every step and Data is simply +// something to pass on to the callback. +// The callback is called with the current step, the current step size and the Data parameter. Since the step amount +// as well as the step size are possibly adjusted during the animation, it is impossible to determine if the current +// step is the last step, even if the original step amount is known. To solve this problem the callback will be +// called after the loop has finished with a step size of 0 indicating so to execute any post processing. var - CellRect, - ContentRect: TRect; + StepSize, + RemainingTime, + RemainingSteps, + NextTimeStep, + CurrentStep, + StartTime: Cardinal; + CurrentTime: Int64; begin - Result := Point(0, 0); - - if Assigned(FOnBeforeCellPaint) then // Otherwise DoBeforeCellPaint has no effect. + if not (tsInAnimation in FStates) and (Duration > 0) then begin - if Canvas = nil then - Canvas := Self.Canvas; + DoStateChange([tsInAnimation]); + try + RemainingTime := Duration; + RemainingSteps := Steps; - // Determine then node's cell rectangle and content rectangle before calling DoBeforeCellPaint. - CellRect := GetDisplayRect(Node, Column, True); - ContentRect := CellRect; - DoBeforeCellPaint(Canvas, Node, Column, cpmGetContentMargin, CellRect, ContentRect); + // Determine the initial step size which is either 1 if the needed steps are less than the number of + // steps possible given by the duration or > 1 otherwise. + StepSize := Round(Max(1, RemainingSteps / Duration)); + RemainingSteps := RemainingSteps div StepSize; + CurrentStep := 0; - // Calculate the changes caused by DoBeforeCellPaint. - case CellContentMarginType of - ccmtAllSides: - // Calculate the width difference and high difference. - Result := Point((CellRect.Right - CellRect.Left) - (ContentRect.Right - ContentRect.Left), - (CellRect.Bottom - CellRect.Top) - (ContentRect.Bottom - ContentRect.Top)); - ccmtTopLeftOnly: - // Calculate the left margin and top margin only. - Result := Point(ContentRect.Left - CellRect.Left, ContentRect.Top - CellRect.Top); - ccmtBottomRightOnly: - // Calculate the right margin and bottom margin only. - Result := Point(CellRect.Right - ContentRect.Right, CellRect.Bottom - ContentRect.Bottom); + while (RemainingSteps > 0) and (RemainingTime > 0) and not Application.Terminated do + begin + StartTime := timeGetTime; + NextTimeStep := StartTime + RemainingTime div RemainingSteps; + if not Callback(CurrentStep, StepSize, Data) then + Break; + + // Keep duration for this step for rest calculation. + CurrentTime := timeGetTime; + // Wait until the calculated time has been reached. + while CurrentTime < NextTimeStep do + CurrentTime := timeGetTime; + + // Subtract the time this step really needed. + if RemainingTime >= CurrentTime - StartTime then + begin + Dec(RemainingTime, CurrentTime - StartTime); + Dec(RemainingSteps); + end + else + begin + RemainingTime := 0; + RemainingSteps := 0; + end; + // If the remaining time per step is less than one time step then we have to decrease the + // step count and increase the step size. + if (RemainingSteps > 0) and ((RemainingTime div RemainingSteps) < 1) then + begin + repeat + Inc(StepSize); + RemainingSteps := RemainingTime div StepSize; + until (RemainingSteps <= 0) or ((RemainingTime div RemainingSteps) >= 1); + end; + CurrentStep := Steps - RemainingSteps; + end; + + if not Application.Terminated then + Callback(0, 0, Data); + finally + DoStateChange([], [tsInAnimation]); end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoGetCursor(var Cursor: TCursor); +procedure TBaseVirtualTree.StartOperation(OperationKind: TVTOperationKind); + +// Called to indicate that a long-running operation has been started. begin - if Assigned(FOnGetCursor) then - FOnGetCursor(Self, Cursor); + Inc(FOperationCount); + if FOperationCount = 1 then + FOperationCanceled := False; + DoStartOperation(OperationKind); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoGetHeaderCursor(var Cursor: HCURSOR); - -begin - if Assigned(FOnGetHeaderCursor) then - FOnGetHeaderCursor(FHeader, Cursor); -end; +function TBaseVirtualTree.CalculateSelectionRect(X, Y: Integer): Boolean; -//---------------------------------------------------------------------------------------------------------------------- +// Recalculates old and new selection rectangle given that X, Y are new mouse coordinates. +// Returns True if there was a change since the last call. -function TBaseVirtualTree.DoGetImageIndex(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; - var Ghosted: Boolean; var Index: TImageIndex): TCustomImageList; +var + MaxValue: Integer; -// Queries the application/descendant about certain image properties for a node. -// Returns a custom image list if given by the callee, otherwise nil. -const - cTVTImageKind2String: Array [TVTImageKind] of string = ('ikNormal', 'ikSelected', 'ikState', 'ikOverlay'); begin - if (Kind = ikState) and Assigned(StateImages) then - Result := Self.StateImages - else - Result := Self.Images; - // First try the enhanced event to allow for custom image lists. - if Assigned(FOnGetImageEx) then - FOnGetImageEx(Self, Node, Kind, Column, Ghosted, Index, Result) - else if Assigned(FOnGetImage) then - FOnGetImage(Self, Node, Kind, Column, Ghosted, Index); + if tsDrawSelecting in FStates then + FLastSelRect := FNewSelRect; + FNewSelRect.BottomRight := Point(X + FEffectiveOffsetX, Y - FOffsetY); + if FNewSelRect.Right < 0 then + FNewSelRect.Right := 0; + if FNewSelRect.Bottom < 0 then + FNewSelRect.Bottom := 0; + MaxValue := ClientWidth; + if FRangeX > Cardinal(MaxValue) then + MaxValue := FRangeX; + if FNewSelRect.Right > MaxValue then + FNewSelRect.Right := MaxValue; + MaxValue := ClientHeight; + if FRangeY > Cardinal(MaxValue) then + MaxValue := FRangeY; + if FNewSelRect.Bottom > MaxValue then + FNewSelRect.Bottom := MaxValue; - Assert((Index < 0) or Assigned(Result), 'An image index was supplied for TVTImageKind.' + cTVTImageKind2String[Kind] + ' but no image list was supplied.'); - if not Assigned(Result) then - Index := -1; + Result := not CompareMem(@FLastSelRect, @FNewSelRect, SizeOf(FNewSelRect)); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoGetImageText(Node: PVirtualNode; Kind: TVTImageKind; - Column: TColumnIndex; var ImageText: string); +function TBaseVirtualTree.CanAutoScroll: Boolean; -// Queries the application/descendant about alternative image text for a node. +// Determines if auto scrolling is currently allowed. + +var + IsDropTarget: Boolean; + IsDrawSelecting: Boolean; + IsWheelPanning: Boolean; begin - if Assigned(FOnGetImageText) then - FOnGetImageText(Self, Node, Kind, Column, ImageText); + // Don't scroll the client area if the header is currently doing tracking or dragging. + // Do auto scroll only if there is a draw selection in progress or the tree is the current drop target or + // wheel panning/scrolling is active. + IsDropTarget := Assigned(FDragManager) and DragManager.IsDropTarget; + IsDrawSelecting := [tsDrawSelPending, tsDrawSelecting] * FStates <> []; + IsWheelPanning := [tsWheelPanning, tsWheelScrolling] * FStates <> []; + Result := ((toAutoScroll in FOptions.AutoOptions) or IsWheelPanning) and + (FHeader.States = []) and (IsDrawSelecting or IsDropTarget or (tsVCLDragging in FStates) or IsWheelPanning); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoGetLineStyle(var Bits: Pointer); +function TBaseVirtualTree.CanShowDragImage: Boolean; + +// Determines whether a drag image should be shown. begin - if Assigned(FOnGetLineStyle) then - FOnGetLineStyle(Self, Bits); + Result := FDragImageKind <> diNoImage; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoGetNodeHint(Node: PVirtualNode; Column: TColumnIndex; - var LineBreakStyle: TVTTooltipLineBreakStyle): string; +function TBaseVirtualTree.CanSplitterResizeNode(P: TPoint; Node: PVirtualNode; Column: TColumnIndex): Boolean; begin - Result := Hint; - LineBreakStyle := hlbDefault; + Result := (toNodeHeightResize in FOptions.MiscOptions) and Assigned(Node) and (Node <> FRoot) and + (Column > NoColumn) and (coFixed in FHeader.Columns[Column].Options); + DoCanSplitterResizeNode(P, Node, Column, Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoGetNodeTooltip(Node: PVirtualNode; Column: TColumnIndex; - var LineBreakStyle: TVTTooltipLineBreakStyle): string; +procedure TBaseVirtualTree.Change(Node: PVirtualNode); begin - Result := Hint; - LineBreakStyle := hlbDefault; + AdviseChangeEvent(False, Node, crIgnore); + + if FUpdateCount = 0 then + begin + if (FChangeDelay > 0) and HandleAllocated and not (tsSynchMode in FStates) then + SetTimer(Handle, ChangeTimer, FChangeDelay, nil) + else + DoChange(Node); + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoGetNodeExtraWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; +procedure TBaseVirtualTree.ChangeScale(M, D: Integer{$if CompilerVersion >= 31}; isDpiChange: Boolean{$ifend}); +{$if CompilerVersion < 27} +const + DefaultScalingFlags = [sfLeft, sfTop, sfWidth, sfHeight, sfFont]; // Was introduced with XE6: http://docwiki.embarcadero.com/Libraries/XE6/en/Vcl.Controls.TControl.DefaultScalingFlags +{$ifend} +var + Flags: TScalingFlags; + Run: PVirtualNode; + lNewNodeTotalHeight: Cardinal; +begin + if (toAutoChangeScale in FOptions.AutoOptions) then + begin + if (M <> D) then + begin + // It is important to evaluate the TScalingFlags before calling inherited, becuase they are differetn afterwards! + if csLoading in ComponentState then + Flags := ScalingFlags + else + Flags := DefaultScalingFlags; // Important for #677 + if (sfHeight in Flags) then begin + TVTHeaderCracker(FHeader).ChangeScale(M, D, {$if CompilerVersion >= 31}isDpiChange{$ELSE} M <> D{$ifend}); + SetDefaultNodeHeight(MulDiv(FDefaultNodeHeight, M, D)); + Indent := MulDiv(Indent, M, D); + FTextMargin := MulDiv(FTextMargin, M, D); + FMargin := MulDiv(FMargin, M, D); + FImagesMargin := MulDiv(FImagesMargin, M, D); + // Scale utility images, #796 + if FCheckImageKind = ckSystemDefault then begin + FreeAndNil(FCheckImages); + if HandleAllocated then + FCheckImages := CreateSystemImageSet(Self); + end; + UpdateHeaderRect(); + // Scale also node heights + BeginUpdate(); + try + Run := GetFirst(); + while Assigned(Run) do + begin + if vsInitialized in Run.States then + SetNodeHeight(Run, MulDiv(Run.NodeHeight, M, D)) + else // prevent initialization of non-initialzed nodes + begin + Run.NodeHeight := MulDiv(Run.NodeHeight, M, D); + // The next three lines fix issue #1000 + lNewNodeTotalHeight := MulDiv(Run.TotalHeight, M, D); + FRoot.TotalHeight := FRoot.TotalHeight + lNewNodeTotalHeight - Run.TotalHeight; // 1 EIntOverflow exception seen here in debug build in 01/2021 + Run.TotalHeight := lNewNodeTotalHeight; + end; + Run := GetNextNoInit(Run); + end; // while + finally + EndUpdate(); + end; + end;//if sfHeight + end;// if M<>D + end;//if toAutoChangeScale + inherited ChangeScale(M, D{$if CompilerVersion >= 31}, isDpiChange{$ifend}); + if (M <> D) then + PrepareBitmaps(True, False); // See issue #991 + // It is important to do this call after calling inherited, so that the Font has been updated. + AutoScale(M <> D); +end; -// Returns the pixel width of extra space occupied by node contents (for example, static text). +//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.ChangeTreeStatesAsync(EnterStates, LeaveStates: TVirtualTreeStates); begin - Result := 0; + //TODO: If this works reliable, move to TWorkerThread + if not (csDestroying in ComponentState) then + TThread.Synchronize(nil, procedure + begin + // Prevent invalid combination tsUseCache + tsValidationNeeded (#915) + if not ((tsUseCache in EnterStates) and (tsValidationNeeded in FStates + LeaveStates)) then + DoStateChange(EnterStates, LeaveStates); + if (tsValidating in FStates) and (tsValidating in LeaveStates) then + UpdateEditBounds(); + end); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoGetNodeWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; +function TBaseVirtualTree.CheckParentCheckState(Node: PVirtualNode; NewCheckState: TCheckState): Boolean; -// Returns the pixel width of a node. +// Checks all siblings of node to determine which check state Node's parent must get. + +var + CheckCount, + BoxCount: Cardinal; + PartialCheck: Boolean; + Run: PVirtualNode; begin - Result := 0; + CheckCount := 0; + BoxCount := 0; + PartialCheck := False; + Run := Node.Parent.FirstChild; + while Assigned(Run) do + begin + if Run = Node then + begin + // The given node cannot be checked because it does not yet have its new check state (as this depends + // on the outcome of this method). Instead NewCheckState is used as this contains the new state the node + // will get if this method returns True. + if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then + begin + Inc(BoxCount); + if NewCheckState.IsChecked then + Inc(CheckCount); + PartialCheck := PartialCheck or (NewCheckState = csMixedNormal); + end; + end + else + if Run.CheckType in [ctCheckBox, ctTriStateCheckBox] then + begin + Inc(BoxCount); + if GetCheckState(Run).IsChecked then + Inc(CheckCount); + PartialCheck := PartialCheck or (GetCheckState(Run) = csMixedNormal); + end; + Run := Run.NextSibling; + end; + + if (CheckCount = 0) and not PartialCheck then + NewCheckState := csUncheckedNormal + else + if CheckCount < BoxCount then + NewCheckState := csMixedNormal + else + NewCheckState := csCheckedNormal; + + Node := Node.Parent; + Result := DoChecking(Node, NewCheckState); + if Result then + begin + DoCheckClick(Node, NewCheckState); + // Recursively adjust parent of parent. + // This is already done in the function DoCheckClick() called in the above line + // We revent unnecessary upward recursion by commenting this code. + // with Node^ do + // begin + // if not (vsInitialized in Parent.States) then + // InitNode(Parent); + // if ([vsChecking, vsDisabled] * Parent.States = []) and (Parent <> FRoot) and + // (Parent.CheckType = ctTriStateCheckBox) then + // Result := CheckParentCheckState(Node, NewCheckState); + // end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoGetPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Position: TPoint): TPopupMenu; - -// Queries the application whether there is a node specific popup menu. +procedure TBaseVirtualTree.ClearTempCache; -var - Run: PVirtualNode; - AskParent: Boolean; +// make sure the temporary node cache is in a reliable state begin - Result := nil; - if Assigned(FOnGetPopupMenu) then - begin - Run := Node; - - if Assigned(Run) then - begin - AskParent := True; - repeat - FOnGetPopupMenu(Self, Run, Column, Position, AskParent, Result); - Run := Run.Parent; - until (Run = FRoot) or Assigned(Result) or not AskParent; - end - else - FOnGetPopupMenu(Self, nil, NoColumn, Position, AskParent, Result); - end; + FTempNodeCache := nil; + FTempNodeCount := 0; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoGetUserClipboardFormats(var Formats: TFormatEtcArray); +function TBaseVirtualTree.ColumnIsEmpty(Node: PVirtualNode; Column: TColumnIndex): Boolean; + +// Returns True if the given column is to be considered as being empty. This will usually be determined by +// descendants as the base tree implementation has not enough information to decide. begin - if Assigned(FOnGetUserClipboardFormats) then - FOnGetUserClipboardFormats(Self, Formats); + Result := True; + if Assigned(FOnGetCellIsEmpty) then + FOnGetCellIsEmpty(Self, Node, Column, Result); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoHeaderAddPopupItem(const Column: TColumnIndex; var Cmd: TAddPopupItemType); - -begin - if Assigned(FOnHeaderAddPopupItem) then - FOnHeaderAddPopupItem(Self, Column, Cmd); +function TBaseVirtualTree.ComputeRTLOffset(ExcludeScrollBar: Boolean): Integer; -end; +// Computes the horizontal offset needed when all columns are automatically right aligned (in RTL bidi mode). +// ExcludeScrollBar determines if the left-hand vertical scrollbar is to be included (if visible) or not. -//---------------------------------------------------------------------------------------------------------------------- +var + HeaderWidth: Integer; + ScrollBarVisible: Boolean; +begin + ScrollBarVisible := (Integer(FRangeY) > ClientHeight) and (ScrollBarOptions.ScrollBars in [ssVertical, ssBoth]); + if ScrollBarVisible then + Result := GetSystemMetrics(SM_CXVSCROLL) + else + Result := 0; -procedure TBaseVirtualTree.DoHeaderClick(const HitInfo: TVTHeaderHitInfo); + // Make everything right aligned. + HeaderWidth := FHeaderRect.Right - FHeaderRect.Left; + if Integer(FRangeX) + Result <= HeaderWidth then + Result := HeaderWidth - Integer(FRangeX); + // Otherwise take only left-hand vertical scrollbar into account. -begin - if Assigned(FOnHeaderClick) then - FOnHeaderClick(FHeader, HitInfo); + if ScrollBarVisible and ExcludeScrollBar then + Dec(Result, GetSystemMetrics(SM_CXVSCROLL)); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoHeaderDblClick(const HitInfo: TVTHeaderHitInfo); - -begin - if Assigned(FOnHeaderDblClick) then - FOnHeaderDblClick(FHeader, HitInfo); -end; +function TBaseVirtualTree.CountLevelDifference(Node1, Node2: PVirtualNode): Integer; -//---------------------------------------------------------------------------------------------------------------------- +// This method counts how many indentation levels the given nodes are apart. If both nodes have the same parent then the +// difference is 0 otherwise the result is basically GetNodeLevel(Node2) - GetNodeLevel(Node1), but with sign. +// If the result is negative then Node2 is less intended than Node1. -procedure TBaseVirtualTree.DoHeaderDragged(Column: TColumnIndex; OldPosition: TColumnPosition); +var + Level1, Level2: Integer; begin - if Assigned(FOnHeaderDragged) then - FOnHeaderDragged(FHeader, Column, OldPosition); -end; + Assert(Assigned(Node1) and Assigned(Node2), 'Both nodes must be Assigned.'); -//---------------------------------------------------------------------------------------------------------------------- + Level1 := 0; + while Node1.Parent <> FRoot do + begin + Inc(Level1); + Node1 := Node1.Parent; + end; -procedure TBaseVirtualTree.DoHeaderDraggedOut(Column: TColumnIndex; DropPosition: TPoint); + Level2 := 0; + while Node2.Parent <> FRoot do + begin + Inc(Level2); + Node2 := Node2.Parent; + end; -begin - if Assigned(FOnHeaderDraggedOut) then - FOnHeaderDraggedOut(FHeader, Column, DropPosition); + Result := Level2 - Level1; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoHeaderDragging(Column: TColumnIndex): Boolean; +function TBaseVirtualTree.CountVisibleChildren(Node: PVirtualNode): Cardinal; + +// Returns the number of visible child nodes of the given node. begin - Result := True; - if Assigned(FOnHeaderDragging) then - FOnHeaderDragging(FHeader, Column, Result); + Result := 0; + + // The node's direct children... + if vsExpanded in Node.States then + begin + // ...and their children. + Node := Node.FirstChild; + while Assigned(Node) do + begin + if vsVisible in Node.States then + Inc(Result, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); + Node := Node.NextSibling; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoHeaderDraw(Canvas: TCanvas; Column: TVirtualTreeColumn; R: TRect; Hover, Pressed: Boolean; - DropMark: TVTDropMarkMode); +procedure TBaseVirtualTree.CreateParams(var Params: TCreateParams); + +const + ScrollBar: array[TScrollStyle] of Cardinal = (0, WS_HSCROLL, WS_VSCROLL, WS_HSCROLL or WS_VSCROLL); begin - if Assigned(FOnHeaderDraw) then - FOnHeaderDraw(FHeader, Canvas, Column, R, Hover, Pressed, DropMark); + inherited CreateParams(Params); + + with Params do + begin + Style := Style or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or ScrollBar[ScrollBarOptions.ScrollBars]; + if toFullRepaintOnResize in FOptions.MiscOptions then + WindowClass.style := WindowClass.style or CS_HREDRAW or CS_VREDRAW + else + WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW); + if FBorderStyle = bsSingle then + begin + if Ctl3D then + begin + ExStyle := ExStyle or WS_EX_CLIENTEDGE; + Style := Style and not WS_BORDER; + end + else + Style := Style or WS_BORDER; + end + else + Style := Style and not WS_BORDER; + + AddBiDiModeExStyle(ExStyle); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoHeaderDrawQueryElements(var PaintInfo: THeaderPaintInfo; var Elements: THeaderPaintElements); +procedure TBaseVirtualTree.CreateWnd; + +// Initializes data which depends on a valid window handle. begin - if Assigned(FOnHeaderDrawQueryElements) then - FOnHeaderDrawQueryElements(FHeader, PaintInfo, Elements); -end; + VclStyleChanged(); // Moved here due to issue #986 + DoStateChange([tsWindowCreating]); + inherited; + DoStateChange([], [tsWindowCreating]); -//---------------------------------------------------------------------------------------------------------------------- + if not Assigned(FCheckImages) then + FCheckImages := CreateSystemImageSet(Self); -procedure TBaseVirtualTree.DoHeaderMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); + if ((StyleServices.Enabled ) and (toThemeAware in TreeOptions.PaintOptions) ) then + begin + DoStateChange([tsUseThemes]); + if (toUseExplorerTheme in FOptions.PaintOptions) and IsWinVistaOrAbove then + begin + DoStateChange([tsUseExplorerTheme]); + SetWindowTheme('explorer'); + end + else + DoStateChange([], [tsUseExplorerTheme]); + end + else + DoStateChange([], [tsUseThemes, tsUseExplorerTheme]); -begin - if Assigned(FOnHeaderMouseDown) then - FOnHeaderMouseDown(FHeader, Button, Shift, X, Y); -end; + // Because of the special recursion and update stopper when creating the window (or resizing it) + // we have to manually trigger the auto size calculation here. + if hsNeedScaling in FHeader.States then + TVTHeaderCracker(FHeader).RescaleHeader; + if hoAutoResize in FHeader.Options then + TVirtualTreeColumnsCracker(FHeader.Columns).AdjustAutoSize(InvalidColumn); -//---------------------------------------------------------------------------------------------------------------------- + PrepareBitmaps(True, True); -procedure TBaseVirtualTree.DoHeaderMouseMove(Shift: TShiftState; X, Y: Integer); + // Register tree as OLE drop target. + if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.MiscOptions) then + if not (csLoading in ComponentState) then // will be done in Loaded after all inherited settings are loaded from the DFMs + RegisterDragDrop(Handle, DragManager as IDropTarget); -begin - if Assigned(FOnHeaderMouseMove) then - FOnHeaderMouseMove(FHeader, Shift, X, Y); + UpdateScrollBars(True); + UpdateHeaderRect; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoHeaderMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); - +procedure TBaseVirtualTree.FakeReadIdent(Reader: TReader); begin - if Assigned(FOnHeaderMouseUp) then - FOnHeaderMouseUp(FHeader, Button, Shift, X, Y); + Assert(Reader.NextValue = vaIdent); + Reader.ReadIdent; end; -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.DoHotChange(Old, New: PVirtualNode); +procedure TBaseVirtualTree.DecVisibleCount; begin - if Assigned(FOnHotChange) then - FOnHotChange(Self, Old, New); + Dec(FVisibleCount); end; -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.DoIncrementalSearch(Node: PVirtualNode; const Text: string): Integer; +procedure TBaseVirtualTree.DefineProperties(Filer: TFiler); -begin - Result := 0; - if Assigned(FOnIncrementalSearch) then - FOnIncrementalSearch(Self, Node, Text, Result); -end; +// There were heavy changes in some properties during development of VT. This method helps to make migration easier +// by reading old properties manually and put them into the new properties as appropriate. +// Note: these old properties are never written again and silently disappear. +// June 2002: Meanwhile another task is done here too: working around the problem that TCollection is not streamed +// correctly when using Visual Form Inheritance (VFI). -//---------------------------------------------------------------------------------------------------------------------- +var + StoreIt: Boolean; -function TBaseVirtualTree.DoInitChildren(Node: PVirtualNode; var ChildCount: Cardinal): Boolean; -/// The function calls the OnInitChildren and returns True if the event was called; it returns False if the caller can expect that no changes have been made to ChildCount begin - if Assigned(FOnInitChildren) then + inherited; + + // The header can prevent writing columns altogether. + if TVTHeaderCracker(FHeader).CanWriteColumns then begin - FOnInitChildren(Self, Node, ChildCount); - Result := True; + // Check if we inherit from an ancestor form (Visual Form Inheritance). + StoreIt := Filer.Ancestor = nil; + // If there is an ancestor then save columns only if they are different to the base set. + if not StoreIt then + StoreIt := not FHeader.Columns.Equals(TBaseVirtualTree(Filer.Ancestor).FHeader.Columns); end else - Result := False; -end; - -//---------------------------------------------------------------------------------------------------------------------- + StoreIt := False; -procedure TBaseVirtualTree.DoInitNode(Parent, Node: PVirtualNode; var InitStates: TVirtualNodeInitStates); + Filer.DefineProperty('Columns', TVTHeaderCracker(FHeader).ReadColumns, TVTHeaderCracker(FHeader).WriteColumns, StoreIt); -begin - if Assigned(FOnInitNode) then - FOnInitNode(Self, Parent, Node, InitStates); + // #622 made old DFMs incompatible with new VTW - so the program is compiled successfully + // and then suddenly crashes at user site in runtime. + Filer.DefineProperty('CheckImageKind', FakeReadIdent, nil, false); + /// #730 removed property HintAnimation + Filer.DefineProperty('HintAnimation', FakeReadIdent, nil, false); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoKeyAction(var CharCode: Word; var Shift: TShiftState): Boolean; - -begin - Result := True; - if Assigned(FOnKeyAction) then - FOnKeyAction(Self, CharCode, Shift, Result); -end; +function TBaseVirtualTree.DetermineDropMode(const P: TPoint; var HitInfo: THitInfo; var NodeRect: TRect): TDropMode; -//---------------------------------------------------------------------------------------------------------------------- +// Determine the DropMode. -procedure TBaseVirtualTree.DoLoadUserData(Node: PVirtualNode; Stream: TStream); +var + ImageHit: Boolean; + LabelHit: Boolean; + ItemHit: Boolean; begin - if Assigned(FOnLoadNode) then - if Node = FRoot then - FOnLoadNode(Self, nil, Stream) + ImageHit := HitInfo.HitPositions * [hiOnNormalIcon, hiOnStateIcon] <> []; + LabelHit := hiOnItemLabel in HitInfo.HitPositions; + //VSOFT ======================================== + //VSOFT 5.0 chang broke our drag/drop line info. + //VSOFT CHANGE - changed back to 4.8.5 behaviour + ItemHit := (hiOnItem in HitInfo.HitPositions);{ and ((toFullRowDrag in FOptions.MiscOptions) or + (toFullRowSelect in FOptions.SelectionOptions));} + + // In report mode only direct hits of the node captions/images in the main column are accepted as hits. + if (toReportMode in FOptions.MiscOptions) and not (ItemHit or ((LabelHit or ImageHit) and + (HitInfo.HitColumn = FHeader.MainColumn))) then + HitInfo.HitNode := nil; + + if Assigned(HitInfo.HitNode) then + begin + if LabelHit or ImageHit or not (toShowDropmark in FOptions.PaintOptions) then + Result := dmOnNode else - FOnLoadNode(Self, Node, Stream); + if ((NodeRect.Top + NodeRect.Bottom) div 2) > P.Y then + Result := dmAbove + else + Result := dmBelow; + end + else + Result := dmNowhere; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoMeasureItem(TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer); - -begin - if not (vsInitialized in Node.States) then - InitNode(Node); - if Assigned(FOnMeasureItem) then - FOnMeasureItem(Self, TargetCanvas, Node, NodeHeight); -end; +procedure TBaseVirtualTree.DetermineHiddenChildrenFlag(Node: PVirtualNode); -//---------------------------------------------------------------------------------------------------------------------- +// Update the hidden children flag of the given node. -procedure TBaseVirtualTree.DoMouseEnter(); +var + Run: PVirtualNode; begin - if Assigned(FOnMouseEnter) then - FOnMouseEnter(Self); + if Node.ChildCount = 0 then + begin + if vsHasChildren in Node.States then + Exclude(Node.States, vsAllChildrenHidden) + else + Include(Node.States, vsAllChildrenHidden); + end + else + begin + // Iterate through all siblings and stop when one visible is found. + Run := Node.FirstChild; + while Assigned(Run) and not IsEffectivelyVisible[Run] do + Run := Run.NextSibling; + if Assigned(Run) then + Exclude(Node.States, vsAllChildrenHidden) + else + Include(Node.States, vsAllChildrenHidden); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoMouseLeave; +procedure TBaseVirtualTree.DetermineHiddenChildrenFlagAllNodes; + +var + Run: PVirtualNode; begin - if Assigned(FOnMouseLeave) then - FOnMouseLeave(Self); + Run := GetFirstNoInit(False); + while Assigned(Run) do + begin + DetermineHiddenChildrenFlag(Run); + Run := GetNextNoInit(Run); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoNodeCopied(Node: PVirtualNode); +procedure TBaseVirtualTree.DetermineHitPositionLTR(var HitInfo: THitInfo; Offset, Right: Integer; + Alignment: TAlignment); + +// This method determines the hit position within a node with left-to-right orientation. +var + MainColumnHit: Boolean; + lIndent, + TextWidth, + ImageOffset: Integer; + lOffsets: TVTOffsets; begin - if Assigned(FOnNodeCopied) then - FOnNodeCopied(Self, Node); -end; + MainColumnHit := HitInfo.HitColumn = FHeader.MainColumn; + GetOffsets(HitInfo.HitNode, lOffsets, ofsRightOfText, HitInfo.HitColumn); -//---------------------------------------------------------------------------------------------------------------------- + if (MainColumnHit and (Offset < lOffsets[ofsCheckbox])) then + begin + // Position is to the left of calculated indentation which can only happen for the main column. + // Check whether it corresponds to a button/checkbox. + if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then + begin + // Position of button is interpreted very generously to avoid forcing the user + // to click exactly into the 9x9 pixels area. The entire node height and one full + // indentation level is accepted as button hit. + if Offset >= lOffsets[ofsCheckbox] - Integer(FIndent) then + Include(HitInfo.HitPositions, hiOnItemButton); + if Offset > lOffsets[ofsToggleButton] then + Include(HitInfo.HitPositions, hiOnItemButtonExact); + end; + // no button hit so position is on indent + if HitInfo.HitPositions = [] then + Include(HitInfo.HitPositions, hiOnItemIndent); + end + else + begin + // The next hit positions can be: + // - on the check box + // - on the state image + // - on the normal image + // - to the left of the text area + // - on the label or + // - to the right of the text area + // (in this order). -function TBaseVirtualTree.DoNodeCopying(Node, NewParent: PVirtualNode): Boolean; + // In report mode no hit other than in the main column is possible. + if MainColumnHit or not (toReportMode in FOptions.MiscOptions) then + begin + if MainColumnHit and (Offset < lOffsets[ofsStateImage]) then + begin + HitInfo.HitPositions := [hiOnItem]; + if (HitInfo.HitNode.CheckType <> ctNone) then + Include(HitInfo.HitPositions, hiOnItemCheckBox); + end + else + begin + ImageOffset := lOffsets[ofsImage]; + if Offset < ImageOffset then + Include(HitInfo.HitPositions, hiOnStateIcon) + else + begin + ImageOffset := lOffsets[ofsLabel]; + if Offset < ImageOffset then + Include(HitInfo.HitPositions, hiOnNormalIcon) + else + begin + TextWidth := lOffsets[ofsRightOfText] - lOffsets[ofsText]; + // ImageOffset contains now the left border of the node label area. This is used to calculate the + // correct alignment in the column. -begin - Result := True; - if Assigned(FOnNodeCopying) then - FOnNodeCopying(Self, Node, NewParent, Result); + // Check if the text can be aligned at all. This is only possible if there is enough room + // in the remaining text rectangle. + if TextWidth > Right - ImageOffset then + Include(HitInfo.HitPositions, hiOnItemLabel) + else + begin + case Alignment of + taCenter: + begin + lIndent := (ImageOffset + Right - TextWidth) div 2; + if Offset < lIndent then + Include(HitInfo.HitPositions, hiOnItemLeft) + else + if Offset < lIndent + TextWidth then + Include(HitInfo.HitPositions, hiOnItemLabel) + else + Include(HitInfo.HitPositions, hiOnItemRight); + end; + taRightJustify: + begin + lIndent := Right - TextWidth; + if Offset < lIndent then + Include(HitInfo.HitPositions, hiOnItemLeft) + else + Include(HitInfo.HitPositions, hiOnItemLabel); + end; + else // taLeftJustify + if Offset < ImageOffset + TextWidth then + Include(HitInfo.HitPositions, hiOnItemLabel) + else + Include(HitInfo.HitPositions, hiOnItemRight); + end; + end; + end; + end; + end; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoNodeClick(const HitInfo: THitInfo); - -begin - if Assigned(FOnNodeClick) then - FOnNodeClick(Self, HitInfo); -end; +procedure TBaseVirtualTree.DetermineHitPositionRTL(var HitInfo: THitInfo; Offset, Right: Integer; Alignment: TAlignment); -//---------------------------------------------------------------------------------------------------------------------- +// This method determines the hit position within a node with right-to-left orientation. -procedure TBaseVirtualTree.DoNodeDblClick(const HitInfo: THitInfo); +var + MainColumnHit: Boolean; + Run: PVirtualNode; + Indent, + TextWidth, + ImageOffset: Integer; begin - if Assigned(FOnNodeDblClick) then - FOnNodeDblClick(Self, HitInfo); -end; + MainColumnHit := HitInfo.HitColumn = FHeader.MainColumn; -//---------------------------------------------------------------------------------------------------------------------- + // If columns are not used or the main column is hit then the tree indentation must be considered too. + if MainColumnHit then + begin + if toFixedIndent in FOptions.PaintOptions then + Dec(Right, FIndent) + else + begin + Run := HitInfo.HitNode; + while (Run.Parent <> FRoot) do + begin + Dec(Right, FIndent); + Run := Run.Parent; + end; + if toShowRoot in FOptions.PaintOptions then + Dec(Right, FIndent); + end; + end; + + if Offset >= Right then + begin + // Position is to the right of calculated indentation which can only happen for the main column. + // Check whether it corresponds to a button/checkbox. + if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in HitInfo.HitNode.States) then + begin + // Position of button is interpreted very generously to avoid forcing the user + // to click exactly into the 9x9 pixels area. The entire node height and one full + // indentation level is accepted as button hit. + if Offset <= Right + Integer(FIndent) then + Include(HitInfo.HitPositions, hiOnItemButton); + if Offset <= Right + FPlusBM.Width then + Include(HitInfo.HitPositions, hiOnItemButtonExact); + end; + // no button hit so position is on indent + if HitInfo.HitPositions = [] then + Include(HitInfo.HitPositions, hiOnItemIndent); + end + else + begin + // The next hit positions can be: + // - on the check box + // - on the state image + // - on the normal image + // - to the left of the text area + // - on the label or + // - to the right of the text area + // (in this order). -function TBaseVirtualTree.DoNodeHeightDblClickResize(Node: PVirtualNode; Column: TColumnIndex; Shift: TShiftState; - P: TPoint): Boolean; + // In report mode no hit other than in the main column is possible. + if MainColumnHit or not (toReportMode in FOptions.MiscOptions) then + begin + ImageOffset := Right - FMargin; -begin - Result := True; - if Assigned(FOnNodeHeightDblClickResize) then - FOnNodeHeightDblClickResize(Self, Node, Column, Shift, P, Result); -end; + // Check support is only available for the main column. + if MainColumnHit and (toCheckSupport in FOptions.MiscOptions) and Assigned(FCheckImages) and + (HitInfo.HitNode.CheckType <> ctNone) then + Dec(ImageOffset, FCheckImages.Width + FImagesMargin); -//---------------------------------------------------------------------------------------------------------------------- + if MainColumnHit and (Offset > ImageOffset) then + begin + HitInfo.HitPositions := [hiOnItem]; + if (HitInfo.HitNode.CheckType <> ctNone) then + Include(HitInfo.HitPositions, hiOnItemCheckBox); + end + else + begin + Dec(ImageOffset, GetImageSize(HitInfo.HitNode, ikState, HitInfo.HitColumn).cx); + if Offset > ImageOffset then + Include(HitInfo.HitPositions, hiOnStateIcon) + else + begin + Dec(ImageOffset, GetImageSize(HitInfo.HitNode, ikNormal, HitInfo.HitColumn).cx); + if Offset > ImageOffset then + Include(HitInfo.HitPositions, hiOnNormalIcon) + else + begin + // ImageOffset contains now the right border of the node label area. This is used to calculate the + // correct alignment in the column. + TextWidth := DoGetNodeWidth(HitInfo.HitNode, HitInfo.HitColumn); -function TBaseVirtualTree.DoNodeHeightTracking(Node: PVirtualNode; Column: TColumnIndex; Shift: TShiftState; - var TrackPoint: TPoint; P: TPoint): Boolean; + // Check if the text can be aligned at all. This is only possible if there is enough room + // in the remaining text rectangle. + if TextWidth > ImageOffset then + Include(HitInfo.HitPositions, hiOnItemLabel) + else + begin + // Consider bidi mode here. In RTL context does left alignment actually mean right alignment + // and vice versa. + ChangeBiDiModeAlignment(Alignment); -begin - Result := True; - if Assigned(FOnNodeHeightTracking) then - FOnNodeHeightTracking(Self, Node, Column, Shift, TrackPoint, P, Result); + case Alignment of + taCenter: + begin + Indent := (ImageOffset - TextWidth) div 2; + if Offset < Indent then + Include(HitInfo.HitPositions, hiOnItemLeft) + else + if Offset < Indent + TextWidth then + Include(HitInfo.HitPositions, hiOnItemLabel) + else + Include(HitInfo.HitPositions, hiOnItemRight); + end; + taRightJustify: + begin + Indent := ImageOffset - TextWidth; + if Offset < Indent then + Include(HitInfo.HitPositions, hiOnItemLeft) + else + Include(HitInfo.HitPositions, hiOnItemLabel); + end; + else // taLeftJustify + if Offset > TextWidth then + Include(HitInfo.HitPositions, hiOnItemRight) + else + Include(HitInfo.HitPositions, hiOnItemLabel); + end; + end; + end; + end; + end; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoNodeMoved(Node: PVirtualNode); - -begin - if Assigned(FOnNodeMoved) then - FOnNodeMoved(Self, Node); -end; +function TBaseVirtualTree.DetermineLineImageAndSelectLevel(Node: PVirtualNode; var LineImage: TLineImage): Integer; -//---------------------------------------------------------------------------------------------------------------------- +// This method is used during paint cycles and initializes an array of line type IDs. These IDs are used to paint +// the tree lines in front of the given node. +// Additionally an initial count of selected parents is determined and returned which is used for specific painting. -function TBaseVirtualTree.DoNodeMoving(Node, NewParent: PVirtualNode): Boolean; +var + X: Integer; + Indent: Integer; + Run: PVirtualNode; begin - Result := True; - if Assigned(FOnNodeMoving) then - FOnNodeMoving(Self, Node, NewParent, Result); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.DoPaintBackground(Canvas: TCanvas; R: TRect): Boolean; + Result := 0; + if toShowRoot in FOptions.PaintOptions then + X := 1 + else + X := 0; + Run := Node; + // Determine indentation level of top node. + while Run.Parent <> FRoot do + begin + Inc(X); + Run := Run.Parent; + // Count selected nodes (FRoot is never selected). + if vsSelected in Run.States then + Inc(Result); + end; -begin - Result := False; - if Assigned(FOnPaintBackground) then - FOnPaintBackground(Self, Canvas, R, Result); -end; + // Set initial size of line index array, this will automatically initialized all entries to ltNone. + SetLength(LineImage, X); + Indent := X - 1; -//---------------------------------------------------------------------------------------------------------------------- + // Only use lines if requested. + if (toShowTreeLines in FOptions.PaintOptions) and + (not (toHideTreeLinesIfThemed in FOptions.PaintOptions) or not (tsUseThemes in FStates)) then + begin + if toChildrenAbove in FOptions.PaintOptions then + begin + Dec(X); + if not HasVisiblePreviousSibling(Node) then + begin + if (Node.Parent <> FRoot) or HasVisibleNextSibling(Node) then + LineImage[X] := ltBottomRight + else + LineImage[X] := ltRight; + end + else + if (Node.Parent = FRoot) and (not HasVisibleNextSibling(Node)) then + LineImage[X] := ltTopRight + else + LineImage[X] := ltTopDownRight; -procedure TBaseVirtualTree.DoPaintDropMark(Canvas: TCanvas; Node: PVirtualNode; R: TRect); + // Now go up to the root to determine the rest. + Run := Node.Parent; + while Run <> FRoot do + begin + Dec(X); + if HasVisiblePreviousSibling(Run) then + LineImage[X] := ltTopDown + else + LineImage[X] := ltNone; -// draws the drop mark into the given rectangle -// Note: Changed properties of the given canvas should be reset to their previous values. + Run := Run.Parent; + end; + end + else + begin + // Start over parent traversal if necessary. + Run := Node; -var - SaveBrushColor: TColor; - SavePenStyle: TPenStyle; + if Run.Parent <> FRoot then + begin + // The very last image (the one immediately before the item label) is different. + if HasVisibleNextSibling(Run) then + LineImage[X - 1] := ltTopDownRight + else + LineImage[X - 1] := ltTopRight; + Run := Run.Parent; -begin - if FLastDropMode in [dmAbove, dmBelow] then - with Canvas do - begin - SavePenStyle := Pen.Style; - Pen.Style := psClear; - SaveBrushColor := Brush.Color; - Brush.Color := FColors.DropMarkColor; + // Now go up all parents. + repeat + if Run.Parent = FRoot then + Break; + Dec(X); + if HasVisibleNextSibling(Run) then + LineImage[X - 1] := ltTopDown + else + LineImage[X - 1] := ltNone; + Run := Run.Parent; + until False; + end; - if FLastDropMode = dmAbove then + // Prepare root level. Run points at this stage to a top level node. + if (toShowRoot in FOptions.PaintOptions) and ((toShowTreeLines in FOptions.PaintOptions) and + (not (toHideTreeLinesIfThemed in FOptions.PaintOptions) or not (tsUseThemes in FStates))) then begin - Polygon([Point(R.Left + 2, R.Top), - Point(R.Right - 2, R.Top), - Point(R.Right - 2, R.Top + 6), - Point(R.Right - 6, R.Top + 2), - Point(R.Left + 6 , R.Top + 2), - Point(R.Left + 2, R.Top + 6) - ]); - end - else - Polygon([Point(R.Left + 2, R.Bottom - 1), - Point(R.Right - 2, R.Bottom - 1), - Point(R.Right - 2, R.Bottom - 8), - Point(R.Right - 7, R.Bottom - 3), - Point(R.Left + 7 , R.Bottom - 3), - Point(R.Left + 2, R.Bottom - 8) - ]); - Brush.Color := SaveBrushColor; - Pen.Style := SavePenStyle; + // Is the top node a root node? + if Run = Node then + begin + // First child gets the bottom-right bitmap if it isn't also the only child. + if IsFirstVisibleChild(FRoot, Run) then + // Is it the only child? + if IsLastVisibleChild(FRoot, Run) then + LineImage[0] := ltRight + else + LineImage[0] := ltBottomRight + else + // real last child + if IsLastVisibleChild(FRoot, Run) then + LineImage[0] := ltTopRight + else + LineImage[0] := ltTopDownRight; + end + else + begin + // No, top node is not a top level node. So we need different painting. + if HasVisibleNextSibling(Run) then + LineImage[0] := ltTopDown + else + LineImage[0] := ltNone; + end; + end; end; + end; + + if (tsUseExplorerTheme in FStates) and HasChildren[Node] and (Indent >= 0) + and not ((vsAllChildrenHidden in Node.States) and (toAutoHideButtons in TreeOptions.AutoOptions)) then + LineImage[Indent] := ltNone; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoPaintNode(var PaintInfo: TVTPaintInfo); +function TBaseVirtualTree.DetermineNextCheckState(CheckType: TCheckType; CheckState: TCheckState): TCheckState; + +// Determines the next check state in case the user click the check image or pressed the space key. begin + case CheckType of + ctTriStateCheckBox, + ctButton, + ctCheckBox: + begin + Result := CheckState.GetToggled(); + end;//ctCheckbox + ctRadioButton: + Result := csCheckedNormal; + else + Result := csMixedNormal; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Position: TPoint); - -// Support for node dependent popup menus. +function TBaseVirtualTree.DetermineScrollDirections(X, Y: Integer): TScrollDirections; -var - Menu: TPopupMenu; +// Determines which direction the client area must be scrolled depending on the given position. begin - Menu := DoGetPopupMenu(Node, Column, Position); + Result:= []; - if Assigned(Menu) then + if CanAutoScroll then begin - DoStateChange([tsPopupMenuShown]); - StopTimer(EditTimer); - Menu.PopupComponent := Self; - with ClientToScreen(Position) do - Menu.Popup(X, Y); + // Calculation for wheel panning/scrolling is a bit different to normal auto scroll. + if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then + begin + if (X - FLastClickPos.X) < -8 then + Include(Result, sdLeft); + if (X - FLastClickPos.X) > 8 then + Include(Result, sdRight); + + if (Y - FLastClickPos.Y) < -8 then + Include(Result, sdUp); + if (Y - FLastClickPos.Y) > 8 then + Include(Result, sdDown); + end + else + begin + if (X < Integer(FDefaultNodeHeight)) and (FEffectiveOffsetX <> 0) then + Include(Result, sdLeft); + if (ClientWidth + FEffectiveOffsetX < Integer(FRangeX)) and (X > ClientWidth - Integer(FDefaultNodeHeight)) then + Include(Result, sdRight); + + if (Y < Integer(FDefaultNodeHeight)) and (FOffsetY <> 0) then + Include(Result, sdUp); + if (ClientHeight - FOffsetY < Integer(FRangeY)) and (Y > ClientHeight - Integer(FDefaultNodeHeight)) then + Include(Result, sdDown); + + // Since scrolling during dragging is not handled via the timer we do a check here whether the auto + // scroll timeout already has elapsed or not. + if (Result <> []) and + ((Assigned(FDragManager) and DragManager.IsDropTarget) or + (FindDragTarget(Point(X, Y), False) = Self)) then + begin + if FDragScrollStart = 0 then + FDragScrollStart := timeGetTime; + // Reset any scroll direction to avoid scroll in the case the user is dragging and the auto scroll time has not + // yet elapsed. + if ((Int64(timeGetTime) - FDragScrollStart) < FAutoScrollDelay) then + Result := []; + end; + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoRemoveFromSelection(Node: PVirtualNode); +procedure TBaseVirtualTree.DoAdvancedHeaderDraw(var PaintInfo: THeaderPaintInfo; const Elements: THeaderPaintElements); begin - if Assigned(FOnRemoveFromSelection) then - FOnRemoveFromSelection(Self, Node); + if Assigned(FOnAdvancedHeaderDraw) then + FOnAdvancedHeaderDraw(FHeader, PaintInfo, Elements); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoRenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium; - ForClipboard: Boolean): HRESULT; +procedure TBaseVirtualTree.DoAfterCellPaint(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellRect: TRect); begin - Result := E_FAIL; - if Assigned(FOnRenderOLEData) then - FOnRenderOLEData(Self, FormatEtcIn, Medium, ForClipboard, Result); + if Assigned(FOnAfterCellPaint) then + FOnAfterCellPaint(Self, Canvas, Node, Column, CellRect); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoReset(Node: PVirtualNode); +procedure TBaseVirtualTree.DoAfterItemErase(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect); begin - if Assigned(FOnResetNode) then - FOnResetNode(Self, Node); + if Assigned(FOnAfterItemErase) then + FOnAfterItemErase(Self, Canvas, Node, ItemRect); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoSaveUserData(Node: PVirtualNode; Stream: TStream); +procedure TBaseVirtualTree.DoAfterItemPaint(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect); begin - if Assigned(FOnSaveNode) then - if Node = FRoot then - FOnSaveNode(Self, nil, Stream) - else - FOnSaveNode(Self, Node, Stream); + if Assigned(FOnAfterItemPaint) then + FOnAfterItemPaint(Self, Canvas, Node, ItemRect); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoScroll(DeltaX, DeltaY: Integer); +procedure TBaseVirtualTree.DoAfterPaint(Canvas: TCanvas); begin - if Assigned(FOnScroll) then - FOnScroll(Self, DeltaX, DeltaY); + if Assigned(FOnAfterPaint) then + FOnAfterPaint(Self, Canvas); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoSetOffsetXY(Value: TPoint; Options: TScrollUpdateOptions; ClipRect: PRect = nil): Boolean; - -// Actual offset setter used to scroll the client area, update scroll bars and invalidating the header (all optional). -// Returns True if the offset really changed otherwise False is returned. - -var - DeltaX: Integer; - DeltaY: Integer; - DWPStructure: HDWP; - I: Integer; - P: TPoint; - R: TRect; +procedure TBaseVirtualTree.DoAutoScroll(X, Y: Integer); begin - // Range check, order is important here. - if Value.X < (ClientWidth - Integer(FRangeX)) then - Value.X := ClientWidth - Integer(FRangeX); - if Value.X > 0 then - Value.X := 0; - DeltaX := Value.X - FOffsetX; - if UseRightToLeftAlignment then - DeltaX := -DeltaX; - if Value.Y < (ClientHeight - Integer(FRangeY)) then - Value.Y := ClientHeight - Integer(FRangeY); - if Value.Y > 0 then - Value.Y := 0; - DeltaY := Value.Y - FOffsetY; + FScrollDirections := DetermineScrollDirections(X, Y); - Result := (DeltaX <> 0) or (DeltaY <> 0); - if Result then + if FStates * [tsWheelPanning, tsWheelScrolling] = [] then begin - FOffsetX := Value.X; - FOffsetY := Value.Y; - Result := True; - - if tsHint in Self.FStates then - Application.CancelHint; - if FUpdateCount = 0 then + if FScrollDirections = [] then begin - // The drag image from VCL controls need special consideration. - if tsVCLDragging in FStates then - ImageList_DragShowNolock(False); - - if (suoScrollClientArea in Options) and not (tsToggling in FStates) then + if ((FStates * [tsScrollPending, tsScrolling]) <> []) then begin - // Have to invalidate the entire window if there's a background. - if (toShowBackground in FOptions.PaintOptions) and Assigned(FBackground.Graphic) then - begin - // Since we don't use ScrollWindow here we have to move all client windows ourselves. - DWPStructure := BeginDeferWindowPos(ControlCount); - for I := 0 to ControlCount - 1 do - if Controls[I] is TWinControl then - begin - with Controls[I] as TWinControl do - DWPStructure := DeferWindowPos(DWPStructure, Handle, 0, Left + DeltaX, Top + DeltaY, 0, 0, - SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOSIZE); - if DWPStructure = 0 then - Break; - end; - if DWPStructure <> 0 then - EndDeferWindowPos(DWPStructure); - InvalidateRect(Handle, nil, False); - end - else - begin - if (DeltaX <> 0) and (Header.Columns.GetVisibleFixedWidth > 0) then - begin - // When fixed columns exists we have to scroll separately horizontally and vertically. - // Horizontally is scroll only the client area not occupied by fixed columns and - // vertically entire client area (or clipping area if one exists). - R := ClientRect; - R.Left := Header.Columns.GetVisibleFixedWidth; - - ScrollWindow(Handle, DeltaX, 0, @R, @R); - if DeltaY <> 0 then - ScrollWindow(Handle, 0, DeltaY, ClipRect, ClipRect); - end - else - ScrollWindow(Handle, DeltaX, DeltaY, ClipRect, ClipRect); - end; + StopTimer(ScrollTimer); + DoStateChange([], [tsScrollPending, tsScrolling]); end; - - if suoUpdateNCArea in Options then + end + else + begin + // start auto scroll if not yet done + if (FStates * [tsScrollPending, tsScrolling]) = [] then begin - if DeltaX <> 0 then - begin - UpdateHorizontalScrollBar(suoRepaintScrollBars in Options); - if (suoRepaintHeader in Options) and (hoVisible in FHeader.Options) then - FHeader.Invalidate(nil); - if not (tsSizing in FStates) and (FScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssHorizontal, System.UITypes.TScrollStyle.ssBoth]) then - UpdateVerticalScrollBar(suoRepaintScrollBars in Options); - end; - - if (DeltaY <> 0) and ([tsThumbTracking, tsSizing] * FStates = []) then - begin - UpdateVerticalScrollBar(suoRepaintScrollBars in Options); - if not (FHeader.UseColumns or IsMouseSelecting) and - (FScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssHorizontal, System.UITypes.TScrollStyle.ssBoth]) then - UpdateHorizontalScrollBar(suoRepaintScrollBars in Options); - end; + DoStateChange([tsScrollPending]); + SetTimer(Handle, ScrollTimer, FAutoScrollDelay, nil); end; - - if tsVCLDragging in FStates then - ImageList_DragShowNolock(True); end; + end; +end; - // Finally update "hot" node if hot tracking is activated - GetCursorPos(P); - P := ScreenToClient(P); - if PtInRect(ClientRect, P) then - HandleHotTrack(P.X, P.Y); +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoBeforeDrag(Node: PVirtualNode; Column: TColumnIndex): Boolean; + +begin + Result := False; + if Assigned(FOnDragAllowed) then + FOnDragAllowed(Self, Node, Column, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoBeforeCellPaint(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; + CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect); + +var + UpdateRect: TRect; + +begin + if Assigned(FOnBeforeCellPaint) then + begin + if CellPaintMode = cpmGetContentMargin then + begin + // Prevent drawing if we are only about to get the margin. As this also clears the update rect we need to save it. + GetUpdateRect(Handle, UpdateRect, False); + SetUpdateState(True); + end; - DoScroll(DeltaX, DeltaY); + Canvas.Font := Self.Font; // Fixes issue #298 + FOnBeforeCellPaint(Self, Canvas, Node, Column, CellPaintMode, CellRect, ContentRect); + + if CellPaintMode = cpmGetContentMargin then + SetUpdateState(False); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoShowScrollBar(Bar: Integer; Show: Boolean); +procedure TBaseVirtualTree.DoBeforeItemErase(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect; var Color: TColor; + var EraseAction: TItemEraseAction); begin - ShowScrollBar(Handle, Bar, Show); - if Assigned(FOnShowScrollBar) then - FOnShowScrollBar(Self, Bar, Show); + if Assigned(FOnBeforeItemErase) then + FOnBeforeItemErase(Self, Canvas, Node, ItemRect, Color, EraseAction); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoStartDrag(var DragObject: TDragObject); +function TBaseVirtualTree.DoBeforeItemPaint(Canvas: TCanvas; Node: PVirtualNode; ItemRect: TRect): Boolean; begin - inherited; - - // Check if the application created an own drag object. This is needed to pass the correct source in - // OnDragOver and OnDragDrop. - if Assigned(DragObject) then - DoStateChange([tsUserDragObject]); + // By default custom draw will not be used, so the tree handles drawing the node. + Result := False; + if Assigned(FOnBeforeItemPaint) then + FOnBeforeItemPaint(Self, Canvas, Node, ItemRect, Result); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoStartOperation(OperationKind: TVTOperationKind); +procedure TBaseVirtualTree.DoBeforePaint(Canvas: TCanvas); begin - if Assigned(FOnStartOperation) then - FOnStartOperation(Self, OperationKind); + if Assigned(FOnBeforePaint) then + FOnBeforePaint(Self, Canvas); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoStateChange(Enter: TVirtualTreeStates; Leave: TVirtualTreeStates = []); +function TBaseVirtualTree.DoCancelEdit(): Boolean; -var - ActualEnter, - ActualLeave: TVirtualTreeStates; +// Called when the current edit action or a pending edit must be cancelled. begin - if Assigned(FOnStateChange) then + StopTimer(EditTimer); + DoStateChange([], [tsEditPending]); + Result := (tsEditing in FStates) and FEditLink.CancelEdit; + if Result then begin - ActualEnter := Enter - FStates; - ActualLeave := FStates * Leave; - if (ActualEnter + ActualLeave) <> [] then - FOnStateChange(Self, Enter, Leave); + DoStateChange([], [tsEditing]); + if Assigned(FOnEditCancelled) then + FOnEditCancelled(Self, FEditColumn); + if not (csDestroying in ComponentState) then + FEditLink := nil; + TrySetFocus(); end; - FStates := FStates + Enter - Leave; - Assert(FStates * [tsUseCache, tsValidationNeeded] <> [tsUseCache, tsValidationNeeded], 'Invalid state. tsUseCache and tsValidationNeeded are mutually exclusive and must not be set at the same time'); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoStructureChange(Node: PVirtualNode; Reason: TChangeReason); +procedure TBaseVirtualTree.DoCanEdit(Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); begin - StopTimer(StructureChangeTimer); - if Assigned(FOnStructureChange) then - FOnStructureChange(Self, Node, Reason); - - // This is a good place to reset the cached node and reason. These are the same as the values passed in here. - // This is necessary to allow descendants to override this method and get them. - DoStateChange([], [tsStructureChangePending]); - FLastStructureChangeNode := nil; - FLastStructureChangeReason := crIgnore; + if Assigned(FOnEditing) then + FOnEditing(Self, Node, Column, Allowed); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoTimerScroll; +procedure TBaseVirtualTree.DoCanSplitterResizeNode(P: TPoint; Node: PVirtualNode; Column: TColumnIndex; + var Allowed: Boolean); -var - P, - ClientP: TPoint; - InRect, - Panning: Boolean; - R, - ClipRect: TRect; - DeltaX, - DeltaY: Integer; +begin + if Assigned(FOnCanSplitterResizeNode) then + FOnCanSplitterResizeNode(Self, P, Node, Column, Allowed); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoChange(Node: PVirtualNode); begin - GetCursorPos(P); - R := ClientRect; - ClipRect := R; - MapWindowPoints(Handle, 0, R, 2); - InRect := PtInRect(R, P); - ClientP := ScreenToClient(P); - Panning := [tsWheelPanning, tsWheelScrolling] * FStates <> []; + StopTimer(ChangeTimer); + if Assigned(FOnChange) then + FOnChange(Self, Node); - if IsMouseSelecting or InRect or Panning then + // This is a good place to reset the cached node. This is the same as the node passed in here. + // This is necessary to allow descendants to override this method and get the node then. + DoStateChange([], [tsChangePending]); + FLastChangedNode := nil; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoCheckClick(Node: PVirtualNode; NewCheckState: TCheckState); + +begin + if ChangeCheckState(Node, NewCheckState) then begin - DeltaX := 0; - DeltaY := 0; - if sdUp in FScrollDirections then + DoChecked(Node); + if SyncCheckstateWithSelection[Node] then begin - if Panning then - DeltaY := FLastClickPos.Y - ClientP.Y - 8 + // selection should follow check state + if (NewCheckState = csCheckedNormal) then + Selected[node] := true else - if InRect then - DeltaY := Min(FScrollBarOptions.VerticalIncrement, ClientHeight) - else - DeltaY := Min(FScrollBarOptions.VerticalIncrement, ClientHeight) * Abs(R.Top - P.Y); - if FOffsetY = 0 then - Exclude(FScrollDirections, sdUp); + Selected[node] := false; end; + end; +end; - if sdDown in FScrollDirections then - begin - if Panning then - DeltaY := FLastClickPos.Y - ClientP.Y + 8 - else - if InRect then - DeltaY := -Min(FScrollBarOptions.VerticalIncrement, ClientHeight) - else - DeltaY := -Min(FScrollBarOptions.VerticalIncrement, ClientHeight) * Abs(P.Y - R.Bottom); - if (ClientHeight - FOffsetY) = Integer(FRangeY) then - Exclude(FScrollDirections, sdDown); - end; +//---------------------------------------------------------------------------------------------------------------------- - if sdLeft in FScrollDirections then +procedure TBaseVirtualTree.DoChecked(Node: PVirtualNode); + +begin + if Assigned(FOnChecked) then + FOnChecked(Self, Node); + if Assigned(FAccessibleItem) then + NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoChecking(Node: PVirtualNode; var NewCheckState: TCheckState): Boolean; + +// Determines if a node is allowed to change its check state to NewCheckState. + +begin + if (toReadOnly in FOptions.MiscOptions) or (vsDisabled in Node.States) then + Result := False + else + begin + Result := True; + if Assigned(FOnChecking) then + FOnChecking(Self, Node, NewCheckState, Result); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoCollapsed(Node: PVirtualNode); +var + lFirstSelected: PVirtualNode; + lParent: PVirtualNode; +begin + if Assigned(FOnCollapsed) then + FOnCollapsed(Self, Node); + + if Assigned(FAccessibleItem) then + NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); + + if (toAlwaysSelectNode in TreeOptions.SelectionOptions) then + begin + // Select the next visible parent if the currently selected node gets invisible due to a collapse + // This makes the VT behave more like the Win32 custom TreeView control + // This makes only sense no no multi selection is allowed and if there is a selected node at all + lFirstSelected := GetFirstSelected(); + if Assigned(lFirstSelected) and not FullyVisible[lFirstSelected] then begin - if Panning then - DeltaX := FLastClickPos.X - ClientP.X - 8 - else - if InRect then - DeltaX := FScrollBarOptions.HorizontalIncrement - else - DeltaX := FScrollBarOptions.HorizontalIncrement * Abs(R.Left - P.X); - if FEffectiveOffsetX = 0 then - Exclude(FScrollDirections, sdleft); - end; + lParent := GetVisibleParent(lFirstSelected); + Selected[lFirstSelected] := False; + Selected[lParent] := True; + end;//if + //if there is (still) no selected node, then use FNextNodeToSelect to select one + if SelectedCount = 0 then + EnsureNodeSelected(); + end;//if +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoCollapsing(Node: PVirtualNode): Boolean; + +begin + Result := True; + if Assigned(FOnCollapsing) then + FOnCollapsing(Self, Node, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoColumnClick(Column: TColumnIndex; Shift: TShiftState); + +begin + if Assigned(FOnColumnClick) then + FOnColumnClick(Self, Column, Shift); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoColumnDblClick(Column: TColumnIndex; Shift: TShiftState); + +begin + if Assigned(FOnColumnDblClick) then + FOnColumnDblClick(Self, Column, Shift); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoColumnResize(Column: TColumnIndex); + +var + R: TRect; + Run: PVirtualNode; + +begin + if not (csLoading in ComponentState) and HandleAllocated then +begin + // Reset all vsHeightMeasured flags if we are in multiline mode. + Run := GetFirstInitialized; + while Assigned(Run) do + begin + if vsMultiline in Run.States then + Exclude(Run.States, vsHeightMeasured); + Run := GetNextInitialized(Run); + end; - if sdRight in FScrollDirections then + UpdateHorizontalScrollBar(True); + if Column > NoColumn then begin - if Panning then - DeltaX := FLastClickPos.X - ClientP.X + 8 - else - if InRect then - DeltaX := -FScrollBarOptions.HorizontalIncrement + // Invalidate client area from the current column all to the right (or left in RTL mode). + R := ClientRect; + if not (toAutoSpanColumns in FOptions.AutoOptions) then + if UseRightToLeftAlignment then + R.Right := FHeader.Columns[Column].Left + FHeader.Columns[Column].Width + ComputeRTLOffset else - DeltaX := -FScrollBarOptions.HorizontalIncrement * Abs(P.X - R.Right); - - if (ClientWidth + FEffectiveOffsetX) = Integer(FRangeX) then - Exclude(FScrollDirections, sdRight); + R.Left := FHeader.Columns[Column].Left; + InvalidateRect(Handle, @R, False); + FHeader.Invalidate(FHeader.Columns[Column], True); end; + if [hsColumnWidthTracking, hsResizing] * FHeader.States = [hsColumnWidthTracking] then + UpdateWindow(Handle); - if UseRightToLeftAlignment then - DeltaX := - DeltaX; - - if IsMouseSelecting then - begin - // In order to avoid scrolling the area which needs a repaint due to the changed selection rectangle - // we limit the scroll area explicitely. - OffsetRect(ClipRect, DeltaX, DeltaY); - DoSetOffsetXY(Point(FOffsetX + DeltaX, FOffsetY + DeltaY), DefaultScrollUpdateFlags, @ClipRect); - // When selecting with the mouse then either update only the parts of the window which have been uncovered - // by the scroll operation if no change in the selection happend or invalidate and redraw the entire - // client area otherwise (to avoid the time consuming task of determining the display rectangles of every - // changed node). - if CalculateSelectionRect(ClientP.X, ClientP.Y) and HandleDrawSelection(ClientP.X, ClientP.Y) then - InvalidateRect(Handle, nil, False) - else - begin - // The selection did not change so invalidate only the part of the window which really needs an update. - // 1) Invalidate the parts uncovered by the scroll operation. Add another offset range, we have to - // scroll only one stripe but have to update two. - OffsetRect(ClipRect, DeltaX, DeltaY); - SubtractRect(ClipRect, ClientRect, ClipRect); - InvalidateRect(Handle, @ClipRect, False); + if not (IsUpdating) then + UpdateDesigner; // design time only - // 2) Invalidate the selection rectangles. - UnionRect(ClipRect, OrderRect(FNewSelRect), OrderRect(FLastSelRect)); - OffsetRect(ClipRect, FOffsetX, FOffsetY); - InvalidateRect(Handle, @ClipRect, False); - end; - end - else - begin - // Scroll only if there is no drag'n drop in progress. Drag'n drop scrolling is handled in DragOver. - if ((FDragManager = nil) or not DragManager.IsDropTarget) and ((DeltaX <> 0) or (DeltaY <> 0)) then - DoSetOffsetXY(Point(FOffsetX + DeltaX, FOffsetY + DeltaY), DefaultScrollUpdateFlags, nil); - end; - UpdateWindow(Handle); + if Assigned(FOnColumnResize) and not (hsResizing in FHeader.States) then + FOnColumnResize(FHeader, Column); - if (FScrollDirections = []) and ([tsWheelPanning, tsWheelScrolling] * FStates = []) then - begin - StopTimer(ScrollTimer); - DoStateChange([], [tsScrollPending, tsScrolling]); - end; + // If the tree is currently in edit state then notify edit link. + if tsEditing in FStates then + UpdateEditBounds; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DoUpdating(State: TVTUpdateState); +procedure TBaseVirtualTree.DoColumnVisibilityChanged(const Column: TColumnIndex; Visible: Boolean); + // Triggers the OnColumnVisibilityChanged event. +begin + if Assigned(OnColumnVisibilityChanged) then + OnColumnVisibilityChanged(Self, Column, Visible); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoCompare(Node1, Node2: PVirtualNode; Column: TColumnIndex): Integer; begin - if Assigned(FOnUpdating) then - FOnUpdating(Self, State); + Result := 0; + if Assigned(FOnCompareNodes) then + FOnCompareNodes(Self, Node1, Node2, Column, Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DoValidateCache(): Boolean; +function TBaseVirtualTree.DoCreateDataObject: IDataObject; -// This method fills the cache, which is used to speed up searching for nodes. -// The strategy is simple: Take the current number of visible nodes and distribute evenly a number of marks -// (which are stored in FPositionCache) so that iterating through the tree doesn't cost too much time. -// If there are less than 'CacheThreshold' nodes in the tree then the cache remains empty. -// Result is True if the cache was filled without interruption, otherwise False. -// Note: You can adjust the maximum number of nodes between two cache entries by changing CacheThreshold. +begin + Result := nil; + if Assigned(FOnCreateDataObject) then + FOnCreateDataObject(Self, Result); +end; -var - EntryCount, - CurrentTop, - Index: Cardinal; - CurrentNode, - Temp: PVirtualNode; +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoCreateDragManager: IVTDragManager; begin - EntryCount := 0; - if not (tsStopValidation in FStates) then - begin - if FStartIndex = 0 then - FPositionCache := nil; + Result := nil; + if Assigned(FOnCreateDragManager) then + FOnCreateDragManager(Self, Result); +end; - EntryCount := CalculateCacheEntryCount; - SetLength(FPositionCache, EntryCount); - if FStartIndex > EntryCount then - FStartIndex := EntryCount; +//---------------------------------------------------------------------------------------------------------------------- - // Optimize validation by starting with FStartIndex if set. - if (FStartIndex > 0) and Assigned(FPositionCache[FStartIndex - 1].Node) then - begin - // Index is the current entry in FPositionCache. - Index := FStartIndex - 1; - // Running term for absolute top value. - CurrentTop := FPositionCache[Index].AbsoluteTop; - // Running node pointer. - CurrentNode := FPositionCache[Index].Node; - end +function TBaseVirtualTree.DoCreateEditor(Node: PVirtualNode; Column: TColumnIndex): IVTEditLink; + +begin + Result := nil; + if Assigned(FOnCreateEditor) then + FOnCreateEditor(Self, Node, Column, Result); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoDragging(P: TPoint); + +// Initiates finally the drag'n drop operation and returns after DD is finished. + + //--------------- local function -------------------------------------------- + + function GetDragOperations: Integer; + + begin + if FDragOperations = [] then + Result := DROPEFFECT_COPY or DROPEFFECT_MOVE or DROPEFFECT_LINK else begin - // Index is the current entry in FPositionCache. - Index := 0; - // Running term for absolute top value. - CurrentTop := 0; - // Running node pointer. - CurrentNode := GetFirstVisibleNoInit(nil, True); + Result := 0; + if doCopy in FDragOperations then + Result := Result or DROPEFFECT_COPY; + if doLink in FDragOperations then + Result := Result or DROPEFFECT_LINK; + if doMove in FDragOperations then + Result := Result or DROPEFFECT_MOVE; end; + end; - // EntryCount serves as counter for processed nodes here. This value can always start at 0 as - // the validation either starts also at index 0 or an index which is always a multiple of CacheThreshold - // and EntryCount is only used with modulo CacheThreshold. - EntryCount := 0; - if Assigned(CurrentNode) then - begin - while not (tsStopValidation in FStates) do - begin - // If the cache is full then stop the loop. - if (Integer(Index) >= Length(FPositionCache)) then - Break; - if (EntryCount mod CacheThreshold) = 0 then - begin - // New cache entry to set up. - with FPositionCache[Index] do - begin - Node := CurrentNode; // 2 EAccessViolation seen here in TreeSize V4.3.1, 1 in V4.4.0 (Write of address 00000000) - AbsoluteTop := CurrentTop; - end; - Inc(Index); - end; + //--------------- end local function ---------------------------------------- - Inc(CurrentTop, NodeHeight[CurrentNode]); - // Advance to next visible node. - Temp := GetNextVisibleNoInit(CurrentNode, True); - // If there is no further node then stop the loop. - if (Temp = nil) then // CHANGED: 17.09.2013 - Veit Zimmermann - Break; // CHANGED: 17.09.2013 - Veit Zimmermann +var + AllowedEffects: Integer; + DragObject: TDragObject; - CurrentNode := Temp; - Inc(EntryCount); - end; - end; - // Finalize the position cache so no nil entry remains there. - if not (tsStopValidation in FStates) and (Integer(Index) <= High(FPositionCache)) then - begin - SetLength(FPositionCache, Index + 1); - with FPositionCache[Index] do + DataObject: IDataObject; + +begin + DataObject := nil; + // Dragging is dragging, nothing else. + DoCancelEdit; + + if Assigned(FCurrentHotNode) then + begin + InvalidateNode(FCurrentHotNode); + FCurrentHotNode := nil; + end; + // Select the focused node if not already done. + if Assigned(FFocusedNode) and not (vsSelected in FFocusedNode.States) then + begin + InternalAddToSelection(FFocusedNode, False); + InvalidateNode(FFocusedNode); + end; + + UpdateWindow(Handle); + + // Keep a list of all currently selected nodes as this list might change, + // but we have probably to delete currently selected nodes. + FDragSelection := GetSortedSelection(True); + try + DoStateChange([tsOLEDragging], [tsOLEDragPending, tsClearPending]); + + // An application might create a drag object like used during VCL dd. This is not required for OLE dd but + // required as parameter. + DragObject := nil; + DoStartDrag(DragObject); + DragObject.Free; + + DataObject := DragManager.DataObject; + PrepareDragImage(P, DataObject); + + FLastDropMode := dmOnNode; + // Don't forget to initialize the result. It might never be touched. + FLastDragEffect := DROPEFFECT_NONE; + AllowedEffects := GetDragOperations; + try + DragAndDrop(AllowedEffects, DataObject, FLastDragEffect); + DragManager.ForceDragLeave; + finally + GetCursorPos(P); + P := ScreenToClient(P); + DoEndDrag(Self, P.X, P.Y); + + FDragImage.EndDrag; + + // Finish the operation. + if (FLastDragEffect = DROPEFFECT_MOVE) and (toAutoDeleteMovedNodes in TreeOptions.AutoOptions) then begin - Node := CurrentNode; - AbsoluteTop := CurrentTop; + // The operation was a move so delete the previously selected nodes. + DeleteSelectedNodes; end; + + DoStateChange([], [tsOLEDragging]); end; + finally + FDragSelection := nil; end; +end; - Result := (EntryCount > 0) and not (tsStopValidation in FStates); +//---------------------------------------------------------------------------------------------------------------------- - // In variable node height mode it might have happend that some or all of the nodes have been adjusted in their - // height. During validation updates of the scrollbars is disabled so let's do this here. - if Result and (toVariableNodeHeight in FOptions.MiscOptions) then +procedure TBaseVirtualTree.DoDragExpand; + +var + SourceTree: TBaseVirtualTree; + +begin + StopTimer(ExpandTimer); + if Assigned(FDropTargetNode) and (vsHasChildren in FDropTargetNode.States) and + not (vsExpanded in FDropTargetNode.States) then begin - TThread.Queue(nil, procedure begin UpdateScrollBars(True) end); + if Assigned(FDragManager) then + SourceTree := DragManager.DragSource + else + SourceTree := nil; + + if not DragManager.DropTargetHelperSupported and Assigned(SourceTree) then + SourceTree.FDragImage.HideDragImage; + ToggleNode(FDropTargetNode); + UpdateWindow(Handle); + if not DragManager.DropTargetHelperSupported and Assigned(SourceTree) then + SourceTree.FDragImage.ShowDragImage; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DragAndDrop(AllowedEffects: Dword; const DataObject: IDataObject; var DragEffect: Integer); -var - lDragEffect: DWord; // required for type compatibility with SHDoDragDrop +function TBaseVirtualTree.DoDragOver(Source: TObject; Shift: TShiftState; State: TDragState; Pt: TPoint; Mode: TDropMode; + var Effect: Integer): Boolean; + begin - if IsWinVistaOrAbove then - begin - lDragEffect := DWord(DragEffect); - SHDoDragDrop(Self.Handle, DataObject, nil, AllowedEffects, lDragEffect); // supports drag hints on Windows Vista and later - DragEffect := Integer(lDragEffect); - end - else - Winapi.ActiveX.DoDragDrop(DataObject, DragManager as IDropSource, AllowedEffects, DragEffect); - end; + Result := False; + if Assigned(FOnDragOver) then + FOnDragOver(Self, Source, Shift, State, Pt, Mode, Effect, Result); +end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DragCanceled; - -// Does some housekeeping for VCL drag'n drop; +procedure TBaseVirtualTree.DoDragDrop(Source: TObject; const DataObject: IDataObject; const Formats: TFormatArray; + Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode); begin - inherited; - - DragFinished; + if Assigned(FOnDragDrop) then + FOnDragDrop(Self, Source, DataObject, Formats, Shift, Pt, Effect, Mode); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DragDrop(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint; - var Effect: Integer): HResult; +procedure TBaseVirtualTree.DoBeforeDrawLineImage(Node: PVirtualNode; Level: Integer; var XPos: Integer); -var - Shift: TShiftState; - EnumFormat: IEnumFormatEtc; - Fetched: Integer; - OLEFormat: TFormatEtc; - Formats: TFormatArray; +begin + if Assigned(FOnBeforeDrawLineImage) then + FOnBeforeDrawLineImage(Self, Node, Level, XPos); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoEdit; begin - StopTimer(ExpandTimer); + Application.CancelHint; StopTimer(ScrollTimer); - DoStateChange([], [tsScrollPending, tsScrolling]); - Formats := nil; - - // Ask explicitly again whether the action is allowed. Otherwise we may accept a drop which is intentionally not - // allowed but cannot be prevented by the application because when the tree was scrolling while dropping - // no DragOver event is created by the OLE subsystem. - Result := DragOver(DragManager.DragSource, KeyState, dsDragMove, Pt, Effect); - try - if (Result <> NOERROR) or ((Effect and not DROPEFFECT_SCROLL) = DROPEFFECT_NONE) then - Result := E_FAIL - else - begin - try - Shift := KeysToShiftState(KeyState); - if tsRightButtonDown in FStates then - Include(Shift, ssRight) - else if tsMiddleButtonDown in FStates then - Include(Shift, ssMiddle) - else - Include(Shift, ssLeft); - Pt := ScreenToClient(Pt); - // Determine which formats we can get and pass them along with the data object to the drop handler. - Result := DataObject.EnumFormatEtc(DATADIR_GET, EnumFormat); - if Failed(Result) then - Abort; - Result := EnumFormat.Reset; - if Failed(Result) then - Abort; - // create a list of available formats - while EnumFormat.Next(1, OLEFormat, @Fetched) = S_OK do - begin - SetLength(Formats, Length(Formats) + 1); - Formats[High(Formats)] := OLEFormat.cfFormat; - end; - DoDragDrop(DragManager.DragSource, DataObject, Formats, Shift, Pt, Effect, FLastDropMode); - except - // An unhandled exception here leaks memory. - Application.HandleException(Self); - Result := E_UNEXPECTED; - end; - end; - finally - if Assigned(FDropTargetNode) then + StopTimer(EditTimer); + DoStateChange([], [tsEditPending]); + if Assigned(FFocusedNode) and not (vsDisabled in FFocusedNode.States) and + not (toReadOnly in FOptions.MiscOptions) and (FEditLink = nil) then + begin + ScrollIntoView(FFocusedNode, toCenterScrollIntoView in FOptions.SelectionOptions, not (toDisableAutoscrollOnEdit in FOptions.AutoOptions)); + FEditLink := DoCreateEditor(FFocusedNode, FEditColumn); + if Assigned(FEditLink) then begin - InvalidateNode(FDropTargetNode); - FDropTargetNode := nil; + DoStateChange([tsEditing], [tsDrawSelecting, tsDrawSelPending, tsToggleFocusedSelection, tsOLEDragPending, + tsOLEDragging, tsClearPending, tsDrawSelPending, tsScrollPending, tsScrolling]); + if FEditLink.PrepareEdit(Self, FFocusedNode, FEditColumn) then + begin + UpdateEditBounds; + // Node needs repaint because the selection rectangle and static text must disappear. + InvalidateNode(FFocusedNode); + if not FEditLink.BeginEdit then + DoStateChange([], [tsEditing]); + end + else + DoStateChange([], [tsEditing]); + if not (tsEditing in FStates) then + FEditLink := nil; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DragEnter(KeyState: Integer; Pt: TPoint; var Effect: Integer): HResult; - -// callback routine for the drop target interface +procedure TBaseVirtualTree.DoEndDrag(Target: TObject; X, Y: Integer); -var - Shift: TShiftState; - Accept: Boolean; - R: TRect; - HitInfo: THitInfo; +// Does some housekeeping for VCL drag'n drop; begin - try - // Determine acceptance of drag operation and reset scroll start time. - FDragScrollStart := 0; - - Shift := KeysToShiftState(KeyState); - if tsLeftButtonDown in FStates then - Include(Shift, ssLeft); - if tsMiddleButtonDown in FStates then - Include(Shift, ssMiddle); - if tsRightButtonDown in FStates then - Include(Shift, ssRight); - Pt := ScreenToClient(Pt); - Effect := SuggestDropEffect(DragManager.DragSource, Shift, Pt, Effect); - Accept := DoDragOver(DragManager.DragSource, Shift, dsDragEnter, Pt, FLastDropMode, Effect); - if not Accept then - Effect := DROPEFFECT_NONE - else - begin - // Set initial drop target node and drop mode. - GetHitTestInfoAt(Pt.X, Pt.Y, True, HitInfo); - if Assigned(HitInfo.HitNode) then - begin - FDropTargetNode := HitInfo.HitNode; - R := GetDisplayRect(HitInfo.HitNode, FHeader.MainColumn, False); - if (hiOnItemLabel in HitInfo.HitPositions) or ((hiOnItem in HitInfo.HitPositions) and - ((toFullRowDrag in FOptions.MiscOptions) or (toFullRowSelect in FOptions.SelectionOptions)))then - FLastDropMode := dmOnNode - else - if ((R.Top + R.Bottom) div 2) > Pt.Y then - FLastDropMode := dmAbove - else - FLastDropMode := dmBelow; - end - else - FLastDropMode := dmNowhere; - end; + inherited; - // If the drag source is a virtual tree then we know how to control the drag image - // and can show it even if the source is not the target tree. - // This is only necessary if we cannot use the drag image helper interfaces. - if not DragManager.DropTargetHelperSupported and Assigned(DragManager.DragSource) then - DragManager.DragSource.FDragImage.ShowDragImage; - Result := NOERROR; - except - Result := E_UNEXPECTED; - end; + DragFinished; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DragFinished; +function TBaseVirtualTree.DoEndEdit: Boolean; -// Called by DragCancelled or EndDrag to make up for the still missing mouse button up messages. -// These are important for such important things like popup menus. +// Called to finish a current edit action or stop the edit timer if an edit operation is pending. +// Returns True if editing was successfully ended or the control was not in edit mode +// Returns False if the control could not leave the edit mode e.g. due to an invalid value that was entered. -var - P: TPoint; +begin + StopTimer(EditTimer); + Result := (tsEditing in FStates) and FEditLink.EndEdit; + if Result then + begin + DoStateChange([], [tsEditing]); + FEditLink := nil; + if Assigned(FOnEdited) then + FOnEdited(Self, FFocusedNode, FEditColumn); + end; + DoStateChange([], [tsEditPending]); + TrySetFocus(); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoEndOperation(OperationKind: TVTOperationKind); begin - if [tsOLEDragging, tsVCLDragPending, tsVCLDragging, tsVCLDragFinished] * FStates = [] then - Exit; + if Assigned(FOnEndOperation) then + FOnEndOperation(Self, OperationKind); +end; - DoStateChange([], [tsVCLDragPending, tsVCLDragging, tsUserDragObject, tsVCLDragFinished]); +//---------------------------------------------------------------------------------------------------------------------- - GetCursorPos(P); - P := ScreenToClient(P); - if tsRightButtonDown in FStates then - Perform(WM_RBUTTONUP, 0, LPARAM(Integer(PointToSmallPoint(P)))) - else - if tsMiddleButtonDown in FStates then - Perform(WM_MBUTTONUP, 0, LPARAM(Integer(PointToSmallPoint(P)))) - else - Perform(WM_LBUTTONUP, 0, LPARAM(Integer(PointToSmallPoint(P)))); +procedure TBaseVirtualTree.DoEnter(); +begin + inherited; + EnsureNodeSelected(); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DragLeave; - -var - Effect: Integer; +procedure TBaseVirtualTree.DoExpanded(Node: PVirtualNode); begin - StopTimer(ExpandTimer); - - if not DragManager.DropTargetHelperSupported and Assigned(DragManager.DragSource) then - DragManager.DragSource.FDragImage.HideDragImage; - - if Assigned(FDropTargetNode) then - begin - InvalidateNode(FDropTargetNode); - FDropTargetNode := nil; - end; - UpdateWindow(Handle); + if Assigned(FOnExpanded) then + FOnExpanded(Self, Node); - Effect := 0; - DoDragOver(nil, [], dsDragLeave, Point(0, 0), FLastDropMode, Effect); + if Assigned(FAccessibleItem) then + NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.DragOver(Source: TObject; KeyState: Integer; DragState: TDragState; Pt: TPoint; - var Effect: Integer): HResult; +function TBaseVirtualTree.DoExpanding(Node: PVirtualNode): Boolean; -// callback routine for the drop target interface +begin + Result := True; + if Assigned(FOnExpanding) then + FOnExpanding(Self, Node, Result); +end; -var - Shift: TShiftState; - Accept, - DragImageWillMove, - WindowScrolled: Boolean; - OldR, R: TRect; - NewDropMode: TDropMode; - HitInfo: THitInfo; - DragPos: TPoint; - Tree: TBaseVirtualTree; - LastNode: PVirtualNode; - DeltaX, - DeltaY: Integer; - ScrollOptions: TScrollUpdateOptions; +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoFocusChange(Node: PVirtualNode; Column: TColumnIndex); begin - if not DragManager.DropTargetHelperSupported and (Source is TBaseVirtualTree) then - begin - Tree := Source as TBaseVirtualTree; - ScrollOptions := [suoUpdateNCArea]; - end - else + if Assigned(FOnFocusChanged) then + FOnFocusChanged(Self, Node, Column); + + if Assigned(FAccessibleItem) then begin - Tree := nil; - ScrollOptions := DefaultScrollUpdateFlags; + NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); + NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); + NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); + NotifyWinEvent(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); + NotifyWinEvent(EVENT_OBJECT_SELECTION, Handle, OBJID_CLIENT, CHILDID_SELF); + NotifyWinEvent(EVENT_OBJECT_FOCUS, Handle, OBJID_CLIENT, CHILDID_SELF); end; +end; - try - DragPos := Pt; - Pt := ScreenToClient(Pt); +//---------------------------------------------------------------------------------------------------------------------- - // Check if we have to scroll the client area. - FScrollDirections := DetermineScrollDirections(Pt.X, Pt.Y); - DeltaX := 0; - DeltaY := 0; - if FScrollDirections <> [] then - begin - // Determine amount to scroll. - if sdUp in FScrollDirections then - begin - DeltaY := Min(FScrollBarOptions.VerticalIncrement, ClientHeight); - if FOffsetY = 0 then - Exclude(FScrollDirections, sdUp); - end; - if sdDown in FScrollDirections then - begin - DeltaY := -Min(FScrollBarOptions.VerticalIncrement, ClientHeight); - if (ClientHeight - FOffsetY) = Integer(FRangeY) then - Exclude(FScrollDirections, sdDown); - end; - if sdLeft in FScrollDirections then - begin - DeltaX := FScrollBarOptions.HorizontalIncrement; - if FEffectiveOffsetX = 0 then - Exclude(FScrollDirections, sdleft); - end; - if sdRight in FScrollDirections then - begin - DeltaX := -FScrollBarOptions.HorizontalIncrement; - if (ClientWidth + FEffectiveOffsetX) = Integer(FRangeX) then - Exclude(FScrollDirections, sdRight); - end; - WindowScrolled := DoSetOffsetXY(Point(FOffsetX + DeltaX, FOffsetY + DeltaY), ScrollOptions, nil); - end - else - WindowScrolled := False; +function TBaseVirtualTree.DoFocusChanging(OldNode, NewNode: PVirtualNode; OldColumn, NewColumn: TColumnIndex): Boolean; - // Determine acceptance of drag operation as well as drag target. - Shift := KeysToShiftState(KeyState); - if tsLeftButtonDown in FStates then - Include(Shift, ssLeft); - if tsMiddleButtonDown in FStates then - Include(Shift, ssMiddle); - if tsRightButtonDown in FStates then - Include(Shift, ssRight); - GetHitTestInfoAt(Pt.X, Pt.Y, True, HitInfo); +begin + Result := (OldColumn = NewColumn) or FHeader.AllowFocus(NewColumn); + if Assigned(FOnFocusChanging) then + FOnFocusChanging(Self, OldNode, NewNode, OldColumn, NewColumn, Result); +end; - if Assigned(HitInfo.HitNode) then - R := GetDisplayRect(HitInfo.HitNode, NoColumn, False) - else - R := Rect(0, 0, 0, 0); - NewDropMode := DetermineDropMode(Pt, HitInfo, R); +//---------------------------------------------------------------------------------------------------------------------- - if Assigned(Tree) then - DragImageWillMove := Tree.FDragImage.WillMove(DragPos) - else - DragImageWillMove := False; +procedure TBaseVirtualTree.DoFocusNode(Node: PVirtualNode; Ask: Boolean); - if (HitInfo.HitNode <> FDropTargetNode) or (FLastDropMode <> NewDropMode) then +begin + if not (tsEditing in FStates) or EndEditNode then + begin + if Node = FRoot then + Node := nil; + if (FFocusedNode <> Node) and (not Ask or DoFocusChanging(FFocusedNode, Node, FFocusedColumn, FFocusedColumn)) then begin - // Something in the tree will change. This requires to update the screen and/or the drag image. - FLastDropMode := NewDropMode; - if HitInfo.HitNode <> FDropTargetNode then - begin - StopTimer(ExpandTimer); - // The last target node is needed for the rectangle determination but must already be set for - // the recapture call, hence it must be stored somewhere. - LastNode := FDropTargetNode; - FDropTargetNode := HitInfo.HitNode; - // In order to show a selection rectangle a column must be focused. - if FFocusedColumn <= NoColumn then - FFocusedColumn := FHeader.MainColumn; - - if Assigned(LastNode) and Assigned(FDropTargetNode) then - begin - // Optimize the case that the selection moved between two nodes. - OldR := GetDisplayRect(LastNode, NoColumn, False); - UnionRect(R, R, OldR); - if Assigned(Tree) then - begin - if WindowScrolled then - UpdateWindowAndDragImage(Tree, ClientRect, True, not DragImageWillMove) - else - UpdateWindowAndDragImage(Tree, R, False, not DragImageWillMove); - end - else - InvalidateRect(Handle, @R, False); - end - else - begin - if Assigned(LastNode) then - begin - // Repaint last target node. - OldR := GetDisplayRect(LastNode, NoColumn, False); - if Assigned(Tree) then - begin - if WindowScrolled then - UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove) - else - UpdateWindowAndDragImage(Tree, OldR, False, not DragImageWillMove); - end - else - InvalidateRect(Handle, @OldR, False); - end - else - begin - if Assigned(Tree) then - begin - if WindowScrolled then - UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove) - else - UpdateWindowAndDragImage(Tree, R, False, not DragImageWillMove); - end - else - InvalidateRect(Handle, @R, False); - end; - end; - - // Start auto expand timer if necessary. - if (toAutoDropExpand in FOptions.AutoOptions) and Assigned(FDropTargetNode) and - (vsHasChildren in FDropTargetNode.States) then - SetTimer(Handle, ExpandTimer, FAutoExpandDelay, nil); - end - else + if Assigned(FFocusedNode) then begin - // Only the drop mark position changed so invalidate the current drop target node. - if Assigned(Tree) then - begin - if WindowScrolled then - UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove) - else - UpdateWindowAndDragImage(Tree, R, False, not DragImageWillMove); - end + // Do automatic collapsing of last focused node if enabled. This is however only done if + // old and new focused node have a common parent node. + if (toAutoExpand in FOptions.AutoOptions) and Assigned(Node) and (Node.Parent = FFocusedNode.Parent) and + (vsExpanded in FFocusedNode.States) then + ToggleNode(FFocusedNode) else - InvalidateRect(Handle, @R, False); + InvalidateNode(FFocusedNode); end; - end - else - begin - // No change in the current drop target or drop mode. This might still mean horizontal or vertical scrolling. - if Assigned(Tree) and ((DeltaX <> 0) or (DeltaY <> 0)) then - UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove); + FFocusedNode := Node; end; - Update; - - if Assigned(Tree) and DragImageWillMove then - Tree.FDragImage.DragTo(DragPos, False); + // Have to scroll the node into view, even it is the same node as before. + if Assigned(FFocusedNode) then + begin + // Make sure a valid column is set if columns are used and no column has currently the focus. + if FHeader.UseColumns and (not FHeader.Columns.IsValidColumn(FFocusedColumn)) then + FFocusedColumn := FHeader.MainColumn; + // Do automatic expansion of the newly focused node if enabled. + if (toAutoExpand in FOptions.AutoOptions) and not (vsExpanded in FFocusedNode.States) then + ToggleNode(FFocusedNode); + InvalidateNode(FFocusedNode); + if (FUpdateCount = 0) and not (toDisableAutoscrollOnFocus in FOptions.AutoOptions) then + ScrollIntoView(FFocusedNode, (toCenterScrollIntoView in FOptions.SelectionOptions) and + (MouseButtonDown * FStates = []), not (toFullRowSelect in FOptions.SelectionOptions) ); + end; - Effect := SuggestDropEffect(Source, Shift, Pt, Effect); - Accept := DoDragOver(Source, Shift, DragState, Pt, FLastDropMode, Effect); - if not Accept then - Effect := DROPEFFECT_NONE; - if WindowScrolled then - Effect := Effect or Integer(DROPEFFECT_SCROLL); - Result := NOERROR; - except - Result := E_UNEXPECTED; + // Reset range anchor if necessary. + if FSelectionCount = 0 then + ResetRangeAnchor; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DrawDottedHLine(const PaintInfo: TVTPaintInfo; Left, Right, Top: Integer); - -// Draws a horizontal line with alternating pixels (this style is not supported for pens under Win9x). +procedure TBaseVirtualTree.DoFreeNode(Node: PVirtualNode); var - R: TRect; - + IntfData: IInterface; begin - with PaintInfo, Canvas do + // Prevent invalid references + if Node = FLastChangedNode then + FLastChangedNode := nil; + if Node = FCurrentHotNode then + FCurrentHotNode := nil; + if Node = FDropTargetNode then + FDropTargetNode := nil; + if Node = FLastStructureChangeNode then + FLastStructureChangeNode := nil; + if Node = FFocusedNode then + FFocusedNode := nil; + if Node = FNextNodeToSelect then + UpdateNextNodeToSelect(Node); + if Node = FLastHitInfo.HitNode then + FLastHitInfo.HitNode := nil; + // fire event + if Assigned(FOnFreeNode) and ([vsInitialized, vsOnFreeNodeCallRequired] * Node.States <> []) then + FOnFreeNode(Self, Node); + + if vsReleaseCallOnUserDataRequired in Node.States then begin - Brush.Color := FColors.BackGroundColor; - R := Rect(Min(Left, Right), Top, Max(Left, Right) + 1, Top + 1); - Winapi.Windows.FillRect(Handle, R, FDottedBrush - ); + // Data may have been set to nil, in which case we can't call _Release on it + IntfData := GetInterfaceFromNodeData(Node); + if Assigned(IntfData) then + IntfData._Release(); end; + + FreeMem(Node); + if Self.UpdateCount = 0 then + EnsureNodeSelected(); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DrawDottedVLine(const PaintInfo: TVTPaintInfo; Top, Bottom, Left: Integer; UseSelectedBkColor: Boolean = False); +function TBaseVirtualTree.DoGetCellContentMargin(Node: PVirtualNode; Column: TColumnIndex; + CellContentMarginType: TVTCellContentMarginType = ccmtAllSides; Canvas: TCanvas = nil): TPoint; -// Draws a horizontal line with alternating pixels (this style is not supported for pens under Win9x). +// Determines the margins of the content rectangle caused by DoBeforeCellPaint. +// Note that shrinking the content rectangle results in positive margins whereas enlarging the content rectangle results +// in negative margins. var - R: TRect; + CellRect, + ContentRect: TRect; begin - with PaintInfo, Canvas do + Result := Point(0, 0); + + if Assigned(FOnBeforeCellPaint) then // Otherwise DoBeforeCellPaint has no effect. begin - if UseSelectedBkColor then - begin - if Focused or (toPopupMode in FOptions.PaintOptions) then - Brush.Color := FColors.FocusedSelectionColor - else - Brush.Color := FColors.UnfocusedSelectionColor; - end - else - Brush.Color := FColors.BackGroundColor; - R := Rect(Left, Min(Top, Bottom), Left + 1, Max(Top, Bottom) + 1); - Winapi.Windows.FillRect(Handle, R, FDottedBrush); + if Canvas = nil then + Canvas := Self.Canvas; + + // Determine then node's cell rectangle and content rectangle before calling DoBeforeCellPaint. + CellRect := GetDisplayRect(Node, Column, True); + ContentRect := CellRect; + DoBeforeCellPaint(Canvas, Node, Column, cpmGetContentMargin, CellRect, ContentRect); + + // Calculate the changes caused by DoBeforeCellPaint. + case CellContentMarginType of + ccmtAllSides: + // Calculate the width difference and high difference. + Result := Point((CellRect.Right - CellRect.Left) - (ContentRect.Right - ContentRect.Left), + (CellRect.Bottom - CellRect.Top) - (ContentRect.Bottom - ContentRect.Top)); + ccmtTopLeftOnly: + // Calculate the left margin and top margin only. + Result := Point(ContentRect.Left - CellRect.Left, ContentRect.Top - CellRect.Top); + ccmtBottomRightOnly: + // Calculate the right margin and bottom margin only. + Result := Point(CellRect.Right - ContentRect.Right, CellRect.Bottom - ContentRect.Bottom); + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.EndOperation(OperationKind: TVTOperationKind); +procedure TBaseVirtualTree.DoGetCursor(var Cursor: TCursor); -// Called to indicate that a long-running operation has finished. +begin + if Assigned(FOnGetCursor) then + FOnGetCursor(Self, Cursor); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoGetHeaderCursor(var Cursor: HCURSOR); begin - Assert(FOperationCount > 0, 'EndOperation must not be called when no operation in progress.'); - Dec(FOperationCount); - DoEndOperation(OperationKind); + if Assigned(FOnGetHeaderCursor) then + FOnGetHeaderCursor(FHeader, Cursor); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.EnsureNodeFocused(); +function TBaseVirtualTree.DoGetImageIndex(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; + var Ghosted: Boolean; var Index: TImageIndex): TCustomImageList; + +// Queries the application/descendant about certain image properties for a node. +// Returns a custom image list if given by the callee, otherwise nil. +const + cTVTImageKind2String: Array [TVTImageKind] of string = ('ikNormal', 'ikSelected', 'ikState', 'ikOverlay'); begin - if FocusedNode = nil then - FocusedNode := Self.GetFirstSelected(); - if FocusedNode = nil then - FocusedNode := Self.GetFirstVisible(); + if (Kind = ikState) and Assigned(StateImages) then + Result := Self.StateImages + else + Result := Self.Images; + // First try the enhanced event to allow for custom image lists. + if Assigned(FOnGetImageEx) then + FOnGetImageEx(Self, Node, Kind, Column, Ghosted, Index, Result) + else if Assigned(FOnGetImage) then + FOnGetImage(Self, Node, Kind, Column, Ghosted, Index); + + Assert((Index < 0) or Assigned(Result), 'An image index was supplied for TVTImageKind.' + cTVTImageKind2String[Kind] + ' but no image list was supplied.'); + if not Assigned(Result) then + Index := -1; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.EnsureNodeSelected(); +procedure TBaseVirtualTree.DoGetImageText(Node: PVirtualNode; Kind: TVTImageKind; + Column: TColumnIndex; var ImageText: string); + +// Queries the application/descendant about alternative image text for a node. + begin - if (toAlwaysSelectNode in TreeOptions.SelectionOptions) and not IsEmpty then - begin - if (SelectedCount = 0) and not SelectionLocked then - begin - if not Assigned(FNextNodeToSelect) then - begin - FNextNodeToSelect := GetFirstVisible; - // Avoid selecting a disabled node, see #954 - while Assigned(FNextNodeToSelect) and IsDisabled[FNextNodeToSelect] do - FNextNodeToSelect := GetNextVisible(FNextNodeToSelect); - end; - Selected[FNextNodeToSelect] := True; - Self.ScrollIntoView(Self.GetFirstSelected, False); - end;// if nothing selected - EnsureNodeFocused(); - end;//if toAlwaysSelectNode + if Assigned(FOnGetImageText) then + FOnGetImageText(Self, Node, Kind, Column, ImageText); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.FindNodeInSelection(P: PVirtualNode; var Index: Integer; LowBound, - HighBound: Integer): Boolean; +procedure TBaseVirtualTree.DoGetLineStyle(var Bits: Pointer); -// Search routine to find a specific node in the selection array. -// LowBound and HighBound determine the range in which to search the node. -// Either value can be -1 to denote the maximum range otherwise LowBound must be less or equal HighBound. +begin + if Assigned(FOnGetLineStyle) then + FOnGetLineStyle(Self, Bits); +end; -var - L, H, - I: Integer; +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoGetNodeHint(Node: PVirtualNode; Column: TColumnIndex; + var LineBreakStyle: TVTTooltipLineBreakStyle): string; begin - Result := False; - L := 0; - if LowBound >= 0 then - L := LowBound; - H := FSelectionCount - 1; - if HighBound >= 0 then - H := HighBound; - while L <= H do - begin - I := (L + H) shr 1; - if PAnsiChar(FSelection[I]) < PAnsiChar(P) then - L := I + 1 - else - begin - H := I - 1; - if FSelection[I] = P then - begin - Result := True; - L := I; - end; - end; - end; - Index := L; + Result := Hint; + LineBreakStyle := hlbDefault; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.FinishChunkHeader(Stream: TStream; StartPos, EndPos: Integer); +function TBaseVirtualTree.DoGetNodeTooltip(Node: PVirtualNode; Column: TColumnIndex; + var LineBreakStyle: TVTTooltipLineBreakStyle): string; -// used while streaming out a node to finally write out the size of the chunk +begin + Result := Hint; + LineBreakStyle := hlbDefault; +end; -var - Size: Integer; +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoGetNodeExtraWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; + +// Returns the pixel width of extra space occupied by node contents (for example, static text). begin - // seek back to the second entry in the chunk header - Stream.Position := StartPos + SizeOf(Size); - // determine size of chunk without the chunk header - Size := EndPos - StartPos - SizeOf(TChunkHeader); - // write the size... - Stream.Write(Size, SizeOf(Size)); - // ... and seek to the last endposition - Stream.Position := EndPos; + Result := 0; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.FontChanged(AFont: TObject); +function TBaseVirtualTree.DoGetNodeWidth(Node: PVirtualNode; Column: TColumnIndex; Canvas: TCanvas = nil): Integer; + +// Returns the pixel width of a node. + +begin + Result := 0; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoGetPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Position: TPoint): TPopupMenu; + +// Queries the application whether there is a node specific popup menu. + +var + Run: PVirtualNode; + AskParent: Boolean; + +begin + Result := nil; + if Assigned(FOnGetPopupMenu) then + begin + Run := Node; + + if Assigned(Run) then + begin + AskParent := True; + repeat + FOnGetPopupMenu(Self, Run, Column, Position, AskParent, Result); + Run := Run.Parent; + until (Run = FRoot) or Assigned(Result) or not AskParent; + end + else + FOnGetPopupMenu(Self, nil, NoColumn, Position, AskParent, Result); + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- -// Little helper function for font changes (as they are not tracked in TBitmap/TCanvas.OnChange). +procedure TBaseVirtualTree.DoGetUserClipboardFormats(var Formats: TFormatEtcArray); begin - FFontChanged := True; - if Assigned(FOldFontChange) then - FOldFontChange(AFont); - //if not (tsPainting in TreeStates) then AutoScale(); + if Assigned(FOnGetUserClipboardFormats) then + FOnGetUserClipboardFormats(Self, Formats); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetBorderDimensions: TSize; - -// Returns the overall width of the current window border, depending on border styles. -// Note: these numbers represent the system's standards not special properties, which can be set for TWinControl -// (e.g. bevels, border width). - -var - Styles: Integer; +procedure TBaseVirtualTree.DoHeaderAddPopupItem(const Column: TColumnIndex; var Cmd: TAddPopupItemType); begin - Result.cx := 0; - Result.cy := 0; + if Assigned(FOnHeaderAddPopupItem) then + FOnHeaderAddPopupItem(Self, Column, Cmd); - Styles := GetWindowLong(Handle, GWL_STYLE); - if (Styles and WS_BORDER) <> 0 then - begin - Dec(Result.cx); - Dec(Result.cy); - end; - if (Styles and WS_THICKFRAME) <> 0 then - begin - Dec(Result.cx, GetSystemMetrics(SM_CXFIXEDFRAME)); - Dec(Result.cy, GetSystemMetrics(SM_CYFIXEDFRAME)); - end; - Styles := GetWindowLong(Handle, GWL_EXSTYLE); - if (Styles and WS_EX_CLIENTEDGE) <> 0 then - begin - Dec(Result.cx, GetSystemMetrics(SM_CXEDGE)); - Dec(Result.cy, GetSystemMetrics(SM_CYEDGE)); - end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetCheckImage(Node: PVirtualNode; ImgCheckType: TCheckType = ctNone; ImgCheckState: - TCheckState = csUncheckedNormal; ImgEnabled: Boolean = True): Integer; +procedure TBaseVirtualTree.DoHeaderClick(const HitInfo: TVTHeaderHitInfo); -// Determines the index into the check image list for the given node depending on the check type -// and enabled state. +begin + if Assigned(FOnHeaderClick) then + FOnHeaderClick(FHeader, HitInfo); +end; -const - // Four dimensional array consisting of image indices for the check type, the check state, the enabled state and the - // hot state. - CheckStateToCheckImage: array[ctCheckBox..ctButton, csUncheckedNormal..csMixedPressed, Boolean, Boolean] of Integer = ( - // ctCheckBox, ctTriStateCheckBox - ( - // csUncheckedNormal (disabled [not hot, hot], enabled [not hot, hot]) - ((ckCheckUncheckedDisabled, ckCheckUncheckedDisabled), (ckCheckUncheckedNormal, ckCheckUncheckedHot)), - // csUncheckedPressed (disabled [not hot, hot], enabled [not hot, hot]) - ((ckCheckUncheckedDisabled, ckCheckUncheckedDisabled), (ckCheckUncheckedPressed, ckCheckUncheckedPressed)), - // csCheckedNormal - ((ckCheckCheckedDisabled, ckCheckCheckedDisabled), (ckCheckCheckedNormal, ckCheckCheckedHot)), - // csCheckedPressed - ((ckCheckCheckedDisabled, ckCheckCheckedDisabled), (ckCheckCheckedPressed, ckCheckCheckedPressed)), - // csMixedNormal - ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedNormal, ckCheckMixedHot)), - // csMixedPressed - ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedPressed, ckCheckMixedPressed)) - ), - // ctRadioButton - ( - // csUncheckedNormal (disabled [not hot, hot], enabled [not hot, hot]) - ((ckRadioUncheckedDisabled, ckRadioUncheckedDisabled), (ckRadioUncheckedNormal, ckRadioUncheckedHot)), - // csUncheckedPressed (disabled [not hot, hot], enabled [not hot, hot]) - ((ckRadioUncheckedDisabled, ckRadioUncheckedDisabled), (ckRadioUncheckedPressed, ckRadioUncheckedPressed)), - // csCheckedNormal - ((ckRadioCheckedDisabled, ckRadioCheckedDisabled), (ckRadioCheckedNormal, ckRadioCheckedHot)), - // csCheckedPressed - ((ckRadioCheckedDisabled, ckRadioCheckedDisabled), (ckRadioCheckedPressed, ckRadioCheckedPressed)), - // csMixedNormal (should never appear with ctRadioButton) - ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedNormal, ckCheckMixedHot)), - // csMixedPressed (should never appear with ctRadioButton) - ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedPressed, ckCheckMixedPressed)) - ), - // ctButton - ( - // csUncheckedNormal (disabled [not hot, hot], enabled [not hot, hot]) - ((ckButtonDisabled, ckButtonDisabled), (ckButtonNormal, ckButtonHot)), - // csUncheckedPressed (disabled [not hot, hot], enabled [not hot, hot]) - ((ckButtonDisabled, ckButtonDisabled), (ckButtonPressed, ckButtonPressed)), - // csCheckedNormal - ((ckButtonDisabled, ckButtonDisabled), (ckButtonNormal, ckButtonHot)), - // csCheckedPressed - ((ckButtonDisabled, ckButtonDisabled), (ckButtonPressed, ckButtonPressed)), - // csMixedNormal (should never appear with ctButton) - ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedNormal, ckCheckMixedHot)), - // csMixedPressed (should never appear with ctButton) - ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedPressed, ckCheckMixedPressed)) - ) - ); +//---------------------------------------------------------------------------------------------------------------------- -var - IsHot: Boolean; +procedure TBaseVirtualTree.DoHeaderDblClick(const HitInfo: TVTHeaderHitInfo); begin - if Assigned(Node) then - begin - ImgCheckType := Node.CheckType; - ImgCheckState := GetCheckState(Node); - ImgEnabled := not (vsDisabled in Node.States) and Self.Enabled; - - IsHot := Node = FCurrentHotNode; - end - else - IsHot := False; + if Assigned(FOnHeaderDblClick) then + FOnHeaderDblClick(FHeader, HitInfo); +end; - if ImgCheckState.IsDisabled then begin // disabled image? - // We need to use disabled images, so map ImgCheckState value from disabled to normal, as disabled state is expressed by ImgEnabled. - ImgEnabled := False; - ImgCheckState := ImgCheckState.GetEnabled(); - end;//if +//---------------------------------------------------------------------------------------------------------------------- - if ImgCheckType = ctTriStateCheckBox then - ImgCheckType := ctCheckBox; - if IsHot and (ImgCheckState in [csCheckedNormal, csUncheckedNormal]) and (GetKeyState(VK_LBUTTON) < 0) and (hiOnItemCheckbox in FLastHitInfo.HitPositions) then - Inc(ImgCheckState); // Advance to pressed state +procedure TBaseVirtualTree.DoHeaderDragged(Column: TColumnIndex; OldPosition: TColumnPosition); - if ImgCheckType = ctNone then - Result := -1 - else - Result := CheckStateToCheckImage[ImgCheckType, ImgCheckState, ImgEnabled, IsHot]; +begin + if Assigned(FOnHeaderDragged) then + FOnHeaderDragged(FHeader, Column, OldPosition); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetColumnClass: TVirtualTreeColumnClass; +procedure TBaseVirtualTree.DoHeaderDraggedOut(Column: TColumnIndex; DropPosition: TPoint); begin - Result := TVirtualTreeColumn; + if Assigned(FOnHeaderDraggedOut) then + FOnHeaderDraggedOut(FHeader, Column, DropPosition); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetHeaderClass: TVTHeaderClass; +function TBaseVirtualTree.DoHeaderDragging(Column: TColumnIndex): Boolean; begin - Result := TVTHeader; + Result := True; + if Assigned(FOnHeaderDragging) then + FOnHeaderDragging(FHeader, Column, Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetHintWindowClass: THintWindowClass; - -// Returns the default hint window class used for the tree. Descendants can override it to use their own classes. +procedure TBaseVirtualTree.DoHeaderDraw(Canvas: TCanvas; Column: TVirtualTreeColumn; R: TRect; Hover, Pressed: Boolean; + DropMark: TVTDropMarkMode); begin - Result := TVirtualTreeHintWindow; + if Assigned(FOnHeaderDraw) then + FOnHeaderDraw(FHeader, Canvas, Column, R, Hover, Pressed, DropMark); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.GetImageIndex(var Info: TVTPaintInfo; Kind: TVTImageKind; InfoIndex: TVTImageInfoIndex); - -// Retrieves the image index and an eventual customized image list for drawing. - -var - CustomImages: TCustomImageList; +procedure TBaseVirtualTree.DoHeaderDrawQueryElements(var PaintInfo: THeaderPaintInfo; var Elements: THeaderPaintElements); begin - with Info do - begin - ImageInfo[InfoIndex].Index := -1; - ImageInfo[InfoIndex].Ghosted := False; - - CustomImages := DoGetImageIndex(Node, Kind, Column, ImageInfo[InfoIndex].Ghosted, ImageInfo[InfoIndex].Index); - if Assigned(CustomImages) then - ImageInfo[InfoIndex].Images := CustomImages - end; + if Assigned(FOnHeaderDrawQueryElements) then + FOnHeaderDrawQueryElements(FHeader, PaintInfo, Elements); end; -function TBaseVirtualTree.GetImageSize(Node: PVirtualNode; Kind: TVTImageKind = TVTImageKind.ikNormal; Column: TColumnIndex = 0; IncludePadding: Boolean = True): TSize; +//---------------------------------------------------------------------------------------------------------------------- -// Determines whether the given node has got an image of the given kind in the given column. -// Returns the size of the image, or (0,0) if no image is available -// The given node will be implicitly initialized if needed. +procedure TBaseVirtualTree.DoHeaderMouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); -var - Ghosted: Boolean; - Index: TImageIndex; - lImageList: TCustomImageList; begin - if not Assigned(OnGetImageIndexEx) and (((Kind = TVTImageKind.ikNormal) and not Assigned(fImages)) - or ((Kind = TVTImageKind.ikState) and not Assigned(fStateImages))) then - begin - Result.cx := 0; - Result.cy := 0; - end; - if not (vsInitialized in Node.States) then - InitNode(Node); - Index := -1; - Ghosted := False; - lImageList := DoGetImageIndex(Node, Kind, Column, Ghosted, Index); - if Index >= 0 then begin - if IncludePadding then - Result.cx := lImageList.Width + ScaledPixels(2) - else - Result.cx := lImageList.Width; - Result.cy := lImageList.Height; - end - else begin - Result.cx := 0; - Result.cy := 0; - end; + if Assigned(FOnHeaderMouseDown) then + FOnHeaderMouseDown(FHeader, Button, Shift, X, Y); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.IsEmpty: Boolean; +procedure TBaseVirtualTree.DoHeaderMouseMove(Shift: TShiftState; X, Y: Integer); + begin - Result := (Self.ChildCount[nil] = 0); + if Assigned(FOnHeaderMouseMove) then + FOnHeaderMouseMove(FHeader, Shift, X, Y); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNodeImageSize(Node: PVirtualNode): TSize; +procedure TBaseVirtualTree.DoHeaderMouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); - // Returns the size of an image - // Override if you need different sized images for certain nodes. begin - Result := GetImageSize(Node); + if Assigned(FOnHeaderMouseUp) then + FOnHeaderMouseUp(FHeader, Button, Shift, X, Y); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetMaxRightExtend(): Cardinal; +procedure TBaseVirtualTree.DoHotChange(Old, New: PVirtualNode); -// Determines the maximum with of the currently visible part of the tree, depending on the length -// of the node texts. This method is used for determining the horizontal scroll range if no columns are used. +begin + if Assigned(FOnHotChange) then + FOnHotChange(Self, Old, New); +end; -var - Node, - NextNode: PVirtualNode; - TopPosition: Integer; - CurrentWidth: Integer; +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoIncrementalSearch(Node: PVirtualNode; const Text: string): Integer; begin - Node := GetNodeAt(0, 0, True, TopPosition); Result := 0; - if not Assigned(Node) then - exit; - - while Assigned(Node) do - begin - if not (vsInitialized in Node.States) then - InitNode(Node); - CurrentWidth := GetOffset(TVTElement.ofsRightOfText, Node); - if Integer(Result) < (CurrentWidth) then - Result := CurrentWidth; - Inc(TopPosition, NodeHeight[Node]); - if TopPosition > Height then - Break; - - // Get next visible node and update left node position. - NextNode := GetNextVisible(Node, True); - if NextNode = nil then - Break; - Node := NextNode; - end; + if Assigned(FOnIncrementalSearch) then + FOnIncrementalSearch(Self, Node, Text, Result); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.GetNativeClipboardFormats(var Formats: TFormatEtcArray); - -// Returns the supported clipboard formats of the tree. - +function TBaseVirtualTree.DoInitChildren(Node: PVirtualNode; var ChildCount: Cardinal): Boolean; +/// The function calls the OnInitChildren and returns True if the event was called; it returns False if the caller can expect that no changes have been made to ChildCount begin - TClipboardFormatList.EnumerateFormats(TVirtualTreeClass(ClassType), Formats, FClipboardFormats); - // Ask application/descendants for self defined formats. - DoGetUserClipboardFormats(Formats); + if Assigned(FOnInitChildren) then + begin + FOnInitChildren(Self, Node, ChildCount); + Result := True; + end + else + Result := False; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetOperationCanceled; +procedure TBaseVirtualTree.DoInitNode(Parent, Node: PVirtualNode; var InitStates: TVirtualNodeInitStates); begin - Result := FOperationCanceled and (FOperationCount > 0); + if Assigned(FOnInitNode) then + FOnInitNode(Self, Parent, Node, InitStates); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetOptionsClass: TTreeOptionsClass; +function TBaseVirtualTree.DoKeyAction(var CharCode: Word; var Shift: TShiftState): Boolean; begin - Result := TCustomVirtualTreeOptions; + Result := True; + if Assigned(FOnKeyAction) then + FOnKeyAction(Self, CharCode, Shift, Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetTreeFromDataObject(const DataObject: IDataObject): TBaseVirtualTree; - -// Returns the owner/sender of the given data object by means of a special clipboard format -// or nil if the sender is in another process or no virtual tree at all. - -var - Medium: TStgMedium; - Data: PVTReference; +procedure TBaseVirtualTree.DoLoadUserData(Node: PVirtualNode; Stream: TStream); begin - Result := nil; - if Assigned(DataObject) then - begin - StandardOLEFormat.cfFormat := CF_VTREFERENCE; - if DataObject.GetData(StandardOLEFormat, Medium) = S_OK then - begin - Data := GlobalLock(Medium.hGlobal); - if Assigned(Data) then - begin - if Data.Process = GetCurrentProcessID then - Result := Data.Tree; - GlobalUnlock(Medium.hGlobal); - end; - ReleaseStgMedium(Medium); - end; - end; + if Assigned(FOnLoadNode) then + if Node = FRoot then + FOnLoadNode(Self, nil, Stream) + else + FOnLoadNode(Self, Node, Stream); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.HandleHotTrack(X, Y: Integer); - -// Updates the current "hot" node. +procedure TBaseVirtualTree.DoMeasureItem(TargetCanvas: TCanvas; Node: PVirtualNode; var NodeHeight: Integer); -var - HitInfo: THitInfo; - CheckPositions: THitPositions; - ButtonIsHit, - DoInvalidate: Boolean; - oldHotNode : PVirtualNode; begin - if not IsMouseCursorVisible then - begin - if Assigned(FCurrentHotNode) then - begin - InvalidateNode(FCurrentHotNode); - FCurrentHotNode := nil; - end; - Exit; - end;//if not IsMouseCursorVisible - - DoInvalidate := False; - oldHotNode := FCurrentHotNode; - // Get information about the hit. - GetHitTestInfoAt(X, Y, True, HitInfo); - - // Only make the new node being "hot" if its label is hit or full row selection is enabled. - CheckPositions := [hiOnItemLabel, hiOnItemCheckbox]; + if not (vsInitialized in Node.States) then + InitNode(Node); + if Assigned(FOnMeasureItem) then + FOnMeasureItem(Self, TargetCanvas, Node, NodeHeight); +end; - // If running under Windows Vista using the explorer theme hitting the buttons makes the node hot, too. - if tsUseExplorerTheme in FStates then - Include(CheckPositions, hiOnItemButtonExact); +//---------------------------------------------------------------------------------------------------------------------- - if (CheckPositions * HitInfo.HitPositions = []) and - (not (toFullRowSelect in FOptions.SelectionOptions) or (hiNowhere in HitInfo.HitPositions)) then - FCurrentHotNode := nil - else - FCurrentHotNode := HitInfo.HitNode; - if (FCurrentHotNode <> oldHotNode) or (HitInfo.HitColumn <> FCurrentHotColumn) then - begin - DoInvalidate := (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.MiscOptions) or (oldHotNode <> FCurrentHotNode); - DoHotChange(oldHotNode, HitInfo.HitNode); - if Assigned(oldHotNode) and DoInvalidate then - InvalidateNode(oldHotNode); - FCurrentHotColumn := HitInfo.HitColumn; - end; +procedure TBaseVirtualTree.DoMouseEnter(); - ButtonIsHit := (hiOnItemButtonExact in HitInfo.HitPositions); - if Assigned(HitInfo.HitNode) and ((FHotNodeButtonHit <> ButtonIsHit) or (FCurrentHotNode <> oldHotNode) or DoInvalidate) then - begin - FHotNodeButtonHit := ButtonIsHit; - InvalidateNode(HitInfo.HitNode); - end - else - if not Assigned(HitInfo.HitNode) then - FHotNodeButtonHit := False; +begin + if Assigned(FOnMouseEnter) then + FOnMouseEnter(Self); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.HandleIncrementalSearch(CharCode: Word); +procedure TBaseVirtualTree.DoMouseLeave; -var - Run, Stop: PVirtualNode; - GetNextNode: TGetNextNodeProc; - NewSearchText: string; - SingleLetter, - PreviousSearch: Boolean; // True if VK_BACK was sent. - SearchDirection: TVTSearchDirection; +begin + if Assigned(FOnMouseLeave) then + FOnMouseLeave(Self); +end; - //--------------- local functions ------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- - procedure SetupNavigation; +procedure TBaseVirtualTree.DoNodeCopied(Node: PVirtualNode); - // If the search buffer is empty then we start searching with the next node after the last one, otherwise - // we continue with the last one. Node navigation function is set up too here, to avoid frequent checks. +begin + if Assigned(FOnNodeCopied) then + FOnNodeCopied(Self, Node); +end; - var - FindNextNode: Boolean; +//---------------------------------------------------------------------------------------------------------------------- - begin - FindNextNode := (Length(FSearchBuffer) = 0) or (Run = nil) or SingleLetter or PreviousSearch; - case FIncrementalSearch of - isVisibleOnly: - if SearchDirection = sdForward then - begin - GetNextNode := GetNextVisible; - if FindNextNode then - begin - if Run = nil then - Run := GetFirstVisible(nil, True) - else - begin - Run := GetNextVisible(Run, True); - // Do wrap around. - if Run = nil then - Run := GetFirstVisible(nil, True); - end; - end; - end - else - begin - GetNextNode := GetPreviousVisible; - if FindNextNode then - begin - if Run = nil then - Run := GetLastVisible(nil, True) - else - begin - Run := GetPreviousVisible(Run, True); - // Do wrap around. - if Run = nil then - Run := GetLastVisible(nil, True); - end; - end; - end; - isInitializedOnly: - if SearchDirection = sdForward then - begin - GetNextNode := GetNextNoInit; - if FindNextNode then - begin - if Run = nil then - Run := GetFirstNoInit - else - begin - Run := GetNextNoInit(Run); - // Do wrap around. - if Run = nil then - Run := GetFirstNoInit; - end; - end; - end - else - begin - GetNextNode := GetPreviousNoInit; - if FindNextNode then - begin - if Run = nil then - Run := GetLastNoInit - else - begin - Run := GetPreviousNoInit(Run); - // Do wrap around. - if Run = nil then - Run := GetLastNoInit; - end; - end; - end; - else - // isAll - if SearchDirection = sdForward then - begin - GetNextNode := GetNext; - if FindNextNode then - begin - if Run = nil then - Run := GetFirst - else - begin - Run := GetNext(Run); - // Do wrap around. - if Run = nil then - Run := GetFirst; - end; - end; - end - else - begin - GetNextNode := GetPrevious; - if FindNextNode then - begin - if Run = nil then - Run := GetLast - else - begin - Run := GetPrevious(Run); - // Do wrap around. - if Run = nil then - Run := GetLast; - end; - end; - end; - end; - end; +function TBaseVirtualTree.DoNodeCopying(Node, NewParent: PVirtualNode): Boolean; - //--------------------------------------------------------------------------- +begin + Result := True; + if Assigned(FOnNodeCopying) then + FOnNodeCopying(Self, Node, NewParent, Result); +end; - function CodePageFromLocale(Language: LCID): Integer; +//---------------------------------------------------------------------------------------------------------------------- - // Determines the code page for a given locale. - // Unfortunately there is no easier way than this, currently. +procedure TBaseVirtualTree.DoNodeClick(const HitInfo: THitInfo); - var - Buf: array[0..6] of Char; +begin + if Assigned(FOnNodeClick) then + FOnNodeClick(Self, HitInfo); +end; - begin - GetLocaleInfo(Language, LOCALE_IDEFAULTANSICODEPAGE, Buf, 6); - Result := StrToIntDef(Buf, GetACP); - end; +//---------------------------------------------------------------------------------------------------------------------- - //--------------------------------------------------------------------------- +procedure TBaseVirtualTree.DoNodeDblClick(const HitInfo: THitInfo); - function KeyUnicode(C: Char): WideChar; - // Converts the given character into its corresponding Unicode character - // depending on the active keyboard layout. - begin - Result := C; //!!!!!! - end; +begin + if Assigned(FOnNodeDblClick) then + FOnNodeDblClick(Self, HitInfo); +end; - //--------------- end local functions --------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- -var - FoundMatch: Boolean; - NewChar: WideChar; +function TBaseVirtualTree.DoNodeHeightDblClickResize(Node: PVirtualNode; Column: TColumnIndex; Shift: TShiftState; + P: TPoint): Boolean; begin - StopTimer(SearchTimer); + Result := True; + if Assigned(FOnNodeHeightDblClickResize) then + FOnNodeHeightDblClickResize(Self, Node, Column, Shift, P, Result); +end; - if FIncrementalSearch <> isNone then - begin - if CharCode <> 0 then - begin - DoStateChange([tsIncrementalSearching]); +//---------------------------------------------------------------------------------------------------------------------- - // Convert the given virtual key code into a Unicode character based on the current locale. - NewChar := KeyUnicode(Char(CharCode)); - PreviousSearch := NewChar = WideChar(VK_BACK); - // We cannot do a search with an empty search buffer. - if not PreviousSearch or (FSearchBuffer <> '') then - begin - // Determine which method to use to advance nodes and the start node to search from. - case FSearchStart of - ssAlwaysStartOver: - Run := nil; - ssFocusedNode: - Run := FFocusedNode; - else // ssLastHit - Run := FLastSearchNode; - end; +function TBaseVirtualTree.DoNodeHeightTracking(Node: PVirtualNode; Column: TColumnIndex; Shift: TShiftState; + var TrackPoint: TPoint; P: TPoint): Boolean; - // Make sure the start node corresponds to the search criterion. - if Assigned(Run) then - begin - case FIncrementalSearch of - isInitializedOnly: - if not (vsInitialized in Run.States) then - Run := nil; - isVisibleOnly: - if not FullyVisible[Run] or IsEffectivelyFiltered[Run] then - Run := nil; - end; - end; - Stop := Run; +begin + Result := True; + if Assigned(FOnNodeHeightTracking) then + FOnNodeHeightTracking(Self, Node, Column, Shift, TrackPoint, P, Result); +end; - // VK_BACK temporarily changes search direction to opposite mode. - if PreviousSearch then - begin - if SearchDirection = sdBackward then - SearchDirection := sdForward - else - SearchDirection := sdBackward; - end - else - SearchDirection := FSearchDirection; - // The "single letter mode" is used to advance quickly from node to node when pressing the same key several times. - SingleLetter := (Length(FSearchBuffer) = 1) and not PreviousSearch and (FSearchBuffer[1] = NewChar); - // However if the current hit (if there is one) would fit also with a repeated character then - // don't use single letter mode. - if SingleLetter and (DoIncrementalSearch(Run, FSearchBuffer + NewChar) = 0) then - SingleLetter := False; - SetupNavigation; - FoundMatch := False; +//---------------------------------------------------------------------------------------------------------------------- - if Assigned(Run) then - begin - if SingleLetter then - NewSearchText := FSearchBuffer - else - if PreviousSearch then - begin - SetLength(FSearchBuffer, Length(FSearchBuffer) - 1); - NewSearchText := FSearchBuffer; - end - else - NewSearchText := FSearchBuffer + NewChar; +procedure TBaseVirtualTree.DoNodeMoved(Node: PVirtualNode); - repeat - if DoIncrementalSearch(Run, NewSearchText) = 0 then - begin - FoundMatch := True; - Break; - end; +begin + if Assigned(FOnNodeMoved) then + FOnNodeMoved(Self, Node); +end; - // Advance to next node if we have not found a match. - Run := GetNextNode(Run); - // Do wrap around start or end of tree. - if (Run <> Stop) and (Run = nil) then - SetupNavigation; - until Run = Stop; - end; +//---------------------------------------------------------------------------------------------------------------------- - if FoundMatch then - begin - ClearSelection; - FSearchBuffer := NewSearchText; - FLastSearchNode := Run; - FocusedNode := Run; - Selected[Run] := True; - FLastSearchNode := Run; - end - else - // Play an acoustic signal if nothing could be found but don't beep if only the currently - // focused node matches. - if Assigned(Run) and (DoIncrementalSearch(Run, NewSearchText) <> 0) then - Beep; - end; - end; +function TBaseVirtualTree.DoNodeMoving(Node, NewParent: PVirtualNode): Boolean; - // Restart search timeout interval. - SetTimer(Handle, SearchTimer, FSearchTimeout, nil); - end; +begin + Result := True; + if Assigned(FOnNodeMoving) then + FOnNodeMoving(Self, Node, NewParent, Result); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitInfo: THitInfo); - -var - Node: PVirtualNode; - MayEdit: Boolean; +function TBaseVirtualTree.DoPaintBackground(Canvas: TCanvas; R: TRect): Boolean; begin - MayEdit := not (tsEditing in FStates) and (toEditOnDblClick in FOptions.MiscOptions); - if tsEditPending in FStates then - begin - StopTimer(EditTimer); - DoStateChange([], [tsEditPending]); - end; + Result := False; + if Assigned(FOnPaintBackground) then + FOnPaintBackground(Self, Canvas, R, Result); +end; - if not (tsEditing in FStates) or DoEndEdit then - begin - if HitInfo.HitColumn = FHeader.Columns.FClickIndex then - DoColumnDblClick(HitInfo.HitColumn, KeysToShiftState(Message.Keys)); +//---------------------------------------------------------------------------------------------------------------------- - if HitInfo.HitNode <> nil then - DoNodeDblClick(HitInfo); +procedure TBaseVirtualTree.DoPaintDropMark(Canvas: TCanvas; Node: PVirtualNode; R: TRect); - Node := nil; - if (hiOnItem in HitInfo.HitPositions) and (HitInfo.HitColumn > NoColumn) and - (coFixed in FHeader.Columns[HitInfo.HitColumn].Options) then - begin - if hiUpperSplitter in HitInfo.HitPositions then - Node := GetPreviousVisible(HitInfo.HitNode, True) - else - if hiLowerSplitter in HitInfo.HitPositions then - Node := HitInfo.HitNode; - end; +// draws the drop mark into the given rectangle +// Note: Changed properties of the given canvas should be reset to their previous values. - if Assigned(Node) and (Node <> FRoot) and (toNodeHeightDblClickResize in FOptions.MiscOptions) then +var + SaveBrushColor: TColor; + SavePenStyle: TPenStyle; + +begin + if FLastDropMode in [dmAbove, dmBelow] then + with Canvas do begin - if DoNodeHeightDblClickResize(Node, HitInfo.HitColumn, KeysToShiftState(Message.Keys), Point(Message.XPos, Message.YPos)) then - begin - SetNodeHeight(Node, FDefaultNodeHeight); - UpdateWindow(Handle); - MayEdit := False; - end; - end - else - if hiOnItemCheckBox in HitInfo.HitPositions then + SavePenStyle := Pen.Style; + Pen.Style := psClear; + SaveBrushColor := Brush.Color; + Brush.Color := FColors.DropMarkColor; + + if FLastDropMode = dmAbove then begin - HandleCheckboxClick(HitInfo.HitNode, Message.Keys); - MayEdit := False; - end// if hiOnItemCheckBox + Polygon([Point(R.Left + 2, R.Top), + Point(R.Right - 2, R.Top), + Point(R.Right - 2, R.Top + 6), + Point(R.Right - 6, R.Top + 2), + Point(R.Left + 6 , R.Top + 2), + Point(R.Left + 2, R.Top + 6) + ]); + end else - begin - if hiOnItemButton in HitInfo.HitPositions then - begin - ToggleNode(HitInfo.HitNode); - MayEdit := False; - end - else - begin - if toToggleOnDblClick in FOptions.MiscOptions then - begin - if ((([hiOnItemButton, hiOnItemLabel, hiOnNormalIcon, hiOnStateIcon] * HitInfo.HitPositions) <> []) or - ((toFullRowSelect in FOptions.SelectionOptions) and Assigned(HitInfo.HitNode))) then - begin - ToggleNode(HitInfo.HitNode); - MayEdit := False; - end; - end; - end; - end; - end; - - if MayEdit and Assigned(FFocusedNode) and (FFocusedNode = HitInfo.HitNode) and - (FFocusedColumn = HitInfo.HitColumn) and CanEdit(FFocusedNode, HitInfo.HitColumn) then - begin - DoStateChange([tsEditPending]); - FEditColumn := FFocusedColumn; - SetTimer(Handle, EditTimer, 0, nil); - end; + Polygon([Point(R.Left + 2, R.Bottom - 1), + Point(R.Right - 2, R.Bottom - 1), + Point(R.Right - 2, R.Bottom - 8), + Point(R.Right - 7, R.Bottom - 3), + Point(R.Left + 7 , R.Bottom - 3), + Point(R.Left + 2, R.Bottom - 8) + ]); + Brush.Color := SaveBrushColor; + Pen.Style := SavePenStyle; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.HandleCheckboxClick(pHitNode: PVirtualNode; pKeys: LongInt); -var - NewCheckState: TCheckState; +procedure TBaseVirtualTree.DoPaintNode(var PaintInfo: TVTPaintInfo); + begin - NewCheckState := DetermineNextCheckState(pHitNode.CheckType, pHitNode.CheckState); - if (ssLeft in KeysToShiftState(pKeys)) and DoChecking(pHitNode, NewCheckState) then - begin - if (Self.SelectedCount > 1) and (Selected[pHitNode]) and not (toSyncCheckboxesWithSelection in TreeOptions.SelectionOptions) then - SetCheckStateForAll(NewCheckState, True) - else - DoCheckClick(pHitNode, NewCheckState); - end;//if ssLeft end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: THitInfo); +procedure TBaseVirtualTree.DoPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Position: TPoint); -// centralized mouse button down handling +// Support for node dependent popup menus. var - LastFocused: PVirtualNode; - Column: TColumnIndex; - ShiftState: TShiftState; - - // helper variables to shorten boolean equations/expressions - AutoDrag, // automatic (or allowed) drag start - IsLabelHit, // the node's caption or images are hit - IsCellHit, // for grid extension or full row select (but not check box, button) - IsAnyHit, // either IsHit or IsCellHit - IsHeightTracking, // height tracking - MultiSelect, // multiselection is enabled - ShiftEmpty, // ShiftState = [] - NodeSelected: Boolean; // the new node (if any) is selected - NewColumn: Boolean; // column changed - NewNode: Boolean; // Node changed. - NeedChangeEvent: Boolean; // change event is required for selection change - CanClear: Boolean; - AltPressed: Boolean; // Pressing the Alt key enables special processing for selection. - FullRowDrag: Boolean; // Start dragging anywhere within a node's bound. - NodeRect: TRect; + Menu: TPopupMenu; - //--------------- local functions ------------------------------------------- +begin + Menu := DoGetPopupMenu(Node, Column, Position); - //Fix for issue: 310 whenever there is a need to invalidate a column, consider - //auto spanned columns if applicable - procedure invalidateWithAutoSpan(acolumn: TColumnIndex; anode: PVirtualNode); - var - NextColumn: Integer; - Dummy: TColumnIndex; + if Assigned(Menu) then begin - if (not FHeader.UseColumns) or (not (toAutoSpanColumns in FOptions.AutoOptions)) - or (acolumn = FHeader.MainColumn) then - begin - //no need to find auto spanned next columns - InvalidateColumn(acolumn); - exit; - end; - //invalidate auto spanned columns too - with FHeader.Columns do //standard loop for auto span - begin - NextColumn := acolumn; - repeat - InvalidateColumn(NextColumn); - Dummy := GetNextVisibleColumn(NextColumn); - if (Dummy = InvalidColumn) or - not ColumnIsEmpty(anode, Dummy) - or - (Items[Dummy].BidiMode <> bdLeftToRight) then - Break; - NextColumn := Dummy; - until False; - end; + DoStateChange([tsPopupMenuShown]); + StopTimer(EditTimer); + Menu.PopupComponent := Self; + with ClientToScreen(Position) do + Menu.Popup(X, Y); end; +end; - //--------------- end local functions --------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoRemoveFromSelection(Node: PVirtualNode); begin - if IsEmpty then - Exit; // Nothing to do - if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then - begin - StopWheelPanning; - Exit; - end; + if Assigned(FOnRemoveFromSelection) then + FOnRemoveFromSelection(Self, Node); +end; - if tsEditPending in FStates then - begin - StopTimer(EditTimer); - DoStateChange([], [tsEditPending]); - end; +//---------------------------------------------------------------------------------------------------------------------- - FLastHitInfo := HitInfo; // Save for later use in OnNodeClick event, see issue #692 - if (tsEditing in FStates) then begin - if not DoEndEdit then - exit; - // Repeat the hit test as an OnEdited event might got triggered that could modify the tree. - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - end;//if tsEditing +function TBaseVirtualTree.DoRenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium; + ForClipboard: Boolean): HRESULT; - // Focus change. Don't use the SetFocus method as this does not work for MDI Winapi.Windows. - if not Focused and CanFocus then - begin - Winapi.Windows.SetFocus(Handle); - // Repeat the hit test as an OnExit event might got triggered that could modify the tree. - GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); - end; +begin + Result := E_FAIL; + if Assigned(FOnRenderOLEData) then + FOnRenderOLEData(Self, FormatEtcIn, Medium, ForClipboard, Result); +end; - // Keep clicked column in case the application needs it. - FHeader.Columns.FClickIndex := HitInfo.HitColumn; +//---------------------------------------------------------------------------------------------------------------------- - // Change column only if we have hit the node label. - if (hiOnItemLabel in HitInfo.HitPositions) or - (toFullRowSelect in FOptions.SelectionOptions) or - (toGridExtensions in FOptions.MiscOptions) then - begin - NewColumn := FFocusedColumn <> HitInfo.HitColumn; - if toExtendedFocus in FOptions.SelectionOptions then - Column := HitInfo.HitColumn +procedure TBaseVirtualTree.DoReset(Node: PVirtualNode); + +begin + if Assigned(FOnResetNode) then + FOnResetNode(Self, Node); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoSaveUserData(Node: PVirtualNode; Stream: TStream); + +begin + if Assigned(FOnSaveNode) then + if Node = FRoot then + FOnSaveNode(Self, nil, Stream) else - Column := FHeader.MainColumn; - end - else - begin - NewColumn := False; - Column := FFocusedColumn; - end; + FOnSaveNode(Self, Node, Stream); +end; - if NewColumn and not FHeader.AllowFocus(Column) then - begin - NewColumn := False; - Column := FFocusedColumn; - end; +//---------------------------------------------------------------------------------------------------------------------- - NewNode := FFocusedNode <> HitInfo.HitNode; +procedure TBaseVirtualTree.DoScroll(DeltaX, DeltaY: Integer); - // Translate keys and filter out shift and control key. - ShiftState := KeysToShiftState(Message.Keys) * [ssShift, ssCtrl, ssAlt]; - if ssAlt in ShiftState then - begin - AltPressed := True; - // Remove the Alt key from the shift state. It is not meaningful there. - Exclude(ShiftState, ssAlt); - end - else - AltPressed := False; +begin + if Assigned(FOnScroll) then + FOnScroll(Self, DeltaX, DeltaY); +end; - // Various combinations determine what states the tree enters now. - // We initialize shorthand variables to avoid the following expressions getting too large - // and to avoid repeative expensive checks. - IsLabelHit := not AltPressed and not (toSimpleDrawSelection in FOptions.SelectionOptions) and - ((hiOnItemLabel in HitInfo.HitPositions) or (hiOnNormalIcon in HitInfo.HitPositions)); +//---------------------------------------------------------------------------------------------------------------------- - IsCellHit := not AltPressed and not IsLabelHit and Assigned(HitInfo.HitNode) and - ([hiOnItemButton, hiOnItemCheckBox, hiNoWhere] * HitInfo.HitPositions = []) and - ((toFullRowSelect in FOptions.SelectionOptions) or - ((toGridExtensions in FOptions.MiscOptions) and (HitInfo.HitColumn > NoColumn))); +function TBaseVirtualTree.DoSetOffsetXY(Value: TPoint; Options: TScrollUpdateOptions; ClipRect: PRect = nil): Boolean; - IsAnyHit := IsLabelHit or IsCellHit; - MultiSelect := toMultiSelect in FOptions.SelectionOptions; - ShiftEmpty := ShiftState = []; - NodeSelected := IsAnyHit and (vsSelected in HitInfo.HitNode.States); +// Actual offset setter used to scroll the client area, update scroll bars and invalidating the header (all optional). +// Returns True if the offset really changed otherwise False is returned. - // Determine the Drag behavior. - if MultiSelect and not (toDisableDrawSelection in FOptions.SelectionOptions) then +var + DeltaX: Integer; + DeltaY: Integer; + DWPStructure: HDWP; + I: Integer; + P: TPoint; + R: TRect; + +begin + // Range check, order is important here. + if Value.X < (ClientWidth - Integer(FRangeX)) then + Value.X := ClientWidth - Integer(FRangeX); + if Value.X > 0 then + Value.X := 0; + DeltaX := Value.X - FOffsetX; + if UseRightToLeftAlignment then + DeltaX := -DeltaX; + if Value.Y < (ClientHeight - Integer(FRangeY)) then + Value.Y := ClientHeight - Integer(FRangeY); + if Value.Y > 0 then + Value.Y := 0; + DeltaY := Value.Y - FOffsetY; + + Result := (DeltaX <> 0) or (DeltaY <> 0); + if Result then begin - // We have MultiSelect and want to draw a selection rectangle. - // We will start a full row drag only in case a label was hit, - // otherwise a multi selection will start. - FullRowDrag := (toFullRowDrag in FOptions.MiscOptions) and IsCellHit and - not (hiNowhere in HitInfo.HitPositions) and - (NodeSelected or (hiOnItemLabel in HitInfo.HitPositions) or (hiOnNormalIcon in HitInfo.HitPositions)); - end - else // No MultiSelect, hence we can start a drag anywhere in the row. - FullRowDrag := toFullRowDrag in FOptions.MiscOptions; + FOffsetX := Value.X; + FOffsetY := Value.Y; + Result := True; - IsHeightTracking := (Message.Msg = WM_LBUTTONDOWN) and - (hiOnItem in HitInfo.HitPositions) and - ([hiUpperSplitter, hiLowerSplitter] * HitInfo.HitPositions <> []); + if tsHint in Self.FStates then + Application.CancelHint; + if FUpdateCount = 0 then + begin + // The drag image from VCL controls need special consideration. + if tsVCLDragging in FStates then + ImageList_DragShowNolock(False); + + if (suoScrollClientArea in Options) and not (tsToggling in FStates) then + begin + // Have to invalidate the entire window if there's a background. + if (toShowBackground in FOptions.PaintOptions) and Assigned(FBackground.Graphic) then + begin + // Since we don't use ScrollWindow here we have to move all client windows ourselves. + DWPStructure := BeginDeferWindowPos(ControlCount); + for I := 0 to ControlCount - 1 do + if Controls[I] is TWinControl then + begin + with Controls[I] as TWinControl do + DWPStructure := DeferWindowPos(DWPStructure, Handle, 0, Left + DeltaX, Top + DeltaY, 0, 0, + SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOSIZE); + if DWPStructure = 0 then + Break; + end; + if DWPStructure <> 0 then + EndDeferWindowPos(DWPStructure); + InvalidateRect(Handle, nil, False); + end + else + begin + if (DeltaX <> 0) and (Header.Columns.GetVisibleFixedWidth > 0) then + begin + // When fixed columns exists we have to scroll separately horizontally and vertically. + // Horizontally is scroll only the client area not occupied by fixed columns and + // vertically entire client area (or clipping area if one exists). + R := ClientRect; + R.Left := Header.Columns.GetVisibleFixedWidth; - // Dragging might be started in the inherited handler manually (which is discouraged for stability reasons) - // the test for manual mode is done below (after the focused node is set). - AutoDrag := ((DragMode = dmAutomatic) or Dragging) and (not IsCellHit or FullRowDrag); + ScrollWindow(Handle, DeltaX, 0, @R, @R); + if DeltaY <> 0 then + ScrollWindow(Handle, 0, DeltaY, ClipRect, ClipRect); + end + else + ScrollWindow(Handle, DeltaX, DeltaY, ClipRect, ClipRect); + end; + end; - // Query the application to learn if dragging may start now (if set to dmManual). - if Assigned(HitInfo.HitNode) and not AutoDrag and (DragMode = dmManual) then - AutoDrag := DoBeforeDrag(HitInfo.HitNode, Column) and (FullRowDrag or IsLabelHit); + if suoUpdateNCArea in Options then + begin + if DeltaX <> 0 then + begin + UpdateHorizontalScrollBar(suoRepaintScrollBars in Options); + if (suoRepaintHeader in Options) and (hoVisible in FHeader.Options) then + FHeader.Invalidate(nil); + if not (tsSizing in FStates) and (FScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssHorizontal, System.UITypes.TScrollStyle.ssBoth]) then + UpdateVerticalScrollBar(suoRepaintScrollBars in Options); + end; - // handle node height tracking - if IsHeightTracking then - begin - if hiUpperSplitter in HitInfo.HitPositions then - FHeightTrackNode := GetPreviousVisible(HitInfo.HitNode, True) - else - FHeightTrackNode := HitInfo.HitNode; + if (DeltaY <> 0) and ([tsThumbTracking, tsSizing] * FStates = []) then + begin + UpdateVerticalScrollBar(suoRepaintScrollBars in Options); + if not (FHeader.UseColumns or IsMouseSelecting) and + (FScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssHorizontal, System.UITypes.TScrollStyle.ssBoth]) then + UpdateHorizontalScrollBar(suoRepaintScrollBars in Options); + end; + end; - if CanSplitterResizeNode(Point(Message.XPos, Message.YPos), FHeightTrackNode, HitInfo.HitColumn) then - begin - FHeightTrackColumn := HitInfo.HitColumn; - NodeRect := GetDisplayRect(FHeightTrackNode, FHeightTrackColumn, False); - FHeightTrackPoint := Point(NodeRect.Left, NodeRect.Top); - DoStateChange([tsNodeHeightTrackPending]); - Exit; + if tsVCLDragging in FStates then + ImageList_DragShowNolock(True); end; - end; - // handle button clicks - if (hiOnItemButton in HitInfo.HitPositions) and (vsHasChildren in HitInfo.HitNode.States) then - begin - ToggleNode(HitInfo.HitNode); - Exit; - end; + // Finally update "hot" node if hot tracking is activated + GetCursorPos(P); + P := ScreenToClient(P); + if PtInRect(ClientRect, P) then + HandleHotTrack(P.X, P.Y); - // check event - if hiOnItemCheckBox in HitInfo.HitPositions then - begin - HandleCheckboxClick(HitInfo.HitNode, Message.Keys); - Exit; + DoScroll(DeltaX, DeltaY); end; +end; - // Keep this node's level in case we need it for constraint selection. - if (FRoot.ChildCount > 0) and ShiftEmpty or (FSelectionCount = 0) then - if Assigned(HitInfo.HitNode) then - FLastSelectionLevel := GetNodeLevelForSelectConstraint(HitInfo.HitNode) - else - FLastSelectionLevel := GetNodeLevelForSelectConstraint(GetLastVisibleNoInit(nil, True)); - - // immediate clearance - // Determine for the right mouse button if there is a popup menu. In this case and if drag'n drop is pending - // the current selection has to stay as it is. - with HitInfo, Message do - CanClear := not AutoDrag and - (not (tsRightButtonDown in FStates) or not HasPopupMenu(HitNode, HitColumn, Point(XPos, YPos))); +//---------------------------------------------------------------------------------------------------------------------- - // pending clearance - if MultiSelect and ShiftEmpty and not (hiOnItemCheckbox in HitInfo.HitPositions) and IsAnyHit and AutoDrag and - NodeSelected and not FSelectionLocked - then - DoStateChange([tsClearPending]); +procedure TBaseVirtualTree.DoShowScrollBar(Bar: Integer; Show: Boolean); - // User starts a selection with a selection rectangle. - if not (toDisableDrawSelection in FOptions.SelectionOptions) and not (IsLabelHit or FullRowDrag) and MultiSelect then - begin - SetCapture(Handle); - DoStateChange([tsDrawSelPending]); - FDrawSelShiftState := ShiftState; - FNewSelRect := Rect(Message.XPos + FEffectiveOffsetX, Message.YPos - FOffsetY, Message.XPos + FEffectiveOffsetX, - Message.YPos - FOffsetY); - FLastSelRect := Rect(0, 0, 0, 0); - end; +begin + ShowScrollBar(Handle, Bar, Show); + if Assigned(FOnShowScrollBar) then + FOnShowScrollBar(Self, Bar, Show); +end; - NeedChangeEvent := FSelectionCount >= 1; - if not FSelectionLocked and ((not (IsAnyHit or FullRowDrag) and MultiSelect and ShiftEmpty) or - (IsAnyHit and (not NodeSelected or (NodeSelected and CanClear)) and (ShiftEmpty or not MultiSelect or (tsRightButtonDown in FStates)))) then - begin - Assert(not (tsClearPending in FStates), 'Pending and direct clearance are mutual exclusive!'); +//---------------------------------------------------------------------------------------------------------------------- - // If the currently hit node was already selected then we have to reselect it again after clearing the current - // selection, but without a change event if it is the only selected node. - // The same applies if the Alt key is pressed, which allows to start drawing the selection rectangle also - // on node captions and images. Here the previous selection state does not matter, though. - if NodeSelected or (AltPressed and Assigned(HitInfo.HitNode) and (HitInfo.HitColumn = FHeader.MainColumn)) and not (hiNowhere in HitInfo.HitPositions) then - begin - InternalClearSelection; - InternalAddToSelection(HitInfo.HitNode, True); - if NeedChangeEvent then - begin - Invalidate; - Change(nil); - end; - end - else if (toAlwaysSelectNode in Self.TreeOptions.SelectionOptions) then - begin - if not (hiNowhere in HitInfo.HitPositions) then - ClearSelection(False) - else - if not (ssCtrl in ShiftState) then - DoStateChange([tsClearOnNewSelection], []); - end - else - ClearSelection(False); - end; +procedure TBaseVirtualTree.DoStartDrag(var DragObject: TDragObject); - // pending node edit - if Focused and - ((hiOnItemLabel in HitInfo.HitPositions) or ((toGridExtensions in FOptions.MiscOptions) and - (hiOnItem in HitInfo.HitPositions))) and NodeSelected and not NewColumn and ShiftEmpty and (SelectedCount = 1) then - begin - DoStateChange([tsEditPending]); - end; +begin + inherited; - if not (toDisableDrawSelection in FOptions.SelectionOptions) - and not (IsLabelHit or FullRowDrag) and (MultiSelect or (hiNowhere in HitInfo.HitPositions)) then - begin - // The original code here was moved up to fix issue #187. - // In order not to break the semantics of this procedure, we are leaving these if statements here - if not IsCellHit then begin - if NeedChangeEvent then - Change(nil); - Exit; - end; - end; + // Check if the application created an own drag object. This is needed to pass the correct source in + // OnDragOver and OnDragDrop. + if Assigned(DragObject) then + DoStateChange([tsUserDragObject]); +end; - // Keep current mouse position. - FLastClickPos := Point(Message.XPos, Message.YPos); +//---------------------------------------------------------------------------------------------------------------------- - // Handle selection and node focus change. - if (IsLabelHit or IsCellHit) and - DoFocusChanging(FFocusedNode, HitInfo.HitNode, FFocusedColumn, Column) then - begin - if NewColumn then - begin +procedure TBaseVirtualTree.DoStartOperation(OperationKind: TVTOperationKind); - if not Assigned(FFocusedNode) then - InvalidateColumn(FFocusedColumn) - else - invalidateWithAutoSpan(FFocusedColumn, FFocusedNode); //fix: issue 310 - if not Assigned(HitInfo.HitNode) then - InvalidateColumn(Column) - else - invalidateWithAutoSpan(Column, HitInfo.HitNode); //fix: issue 310 - FFocusedColumn := Column; - end; - if DragKind = dkDock then - begin - StopTimer(ScrollTimer); - DoStateChange([], [tsScrollPending, tsScrolling]); - end; - // Get the currently focused node to make multiple multi-selection blocks possible. - LastFocused := FFocusedNode; - if NewNode then - DoFocusNode(HitInfo.HitNode, False); +begin + if Assigned(FOnStartOperation) then + FOnStartOperation(Self, OperationKind); +end; - if MultiSelect and not ShiftEmpty and not (tsRightButtonDown in FStates) then - HandleClickSelection(LastFocused, HitInfo.HitNode, ShiftState, AutoDrag) - else - begin - if ShiftEmpty then - FRangeAnchor := HitInfo.HitNode; +//---------------------------------------------------------------------------------------------------------------------- - // If the hit node is not yet selected then do it now. - if not NodeSelected then - AddToSelection(HitInfo.HitNode, True); - end; +procedure TBaseVirtualTree.DoStateChange(Enter: TVirtualTreeStates; Leave: TVirtualTreeStates = []); - if NewNode or NewColumn then - begin - ScrollIntoView(FFocusedNode, False, - not (toDisableAutoscrollOnFocus in FOptions.AutoOptions) - and not (toFullRowSelect in FOptions.SelectionOptions)); +var + ActualEnter, + ActualLeave: TVirtualTreeStates; - DoFocusChange(FFocusedNode, FFocusedColumn); - end; +begin + if Assigned(FOnStateChange) then + begin + ActualEnter := Enter - FStates; + ActualLeave := FStates * Leave; + if (ActualEnter + ActualLeave) <> [] then + FOnStateChange(Self, Enter, Leave); end; + FStates := FStates + Enter - Leave; + Assert(FStates * [tsUseCache, tsValidationNeeded] <> [tsUseCache, tsValidationNeeded], 'Invalid state. tsUseCache and tsValidationNeeded are mutually exclusive and must not be set at the same time'); +end; - if (SelectedCount = 0) and NeedChangeEvent then - Change(nil); +//---------------------------------------------------------------------------------------------------------------------- - // Drag'n drop initiation - // If we lost focus in the interim the button states would be cleared in WM_KILLFOCUS. - if AutoDrag and IsAnyHit and (FStates * [tsLeftButtonDown, tsRightButtonDown, tsMiddleButtonDown] <> []) then - BeginDrag(False); +procedure TBaseVirtualTree.DoStructureChange(Node: PVirtualNode; Reason: TChangeReason); + +begin + StopTimer(StructureChangeTimer); + if Assigned(FOnStructureChange) then + FOnStructureChange(Self, Node, Reason); + + // This is a good place to reset the cached node and reason. These are the same as the values passed in here. + // This is necessary to allow descendants to override this method and get them. + DoStateChange([], [tsStructureChangePending]); + FLastStructureChangeNode := nil; + FLastStructureChangeReason := crIgnore; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.HandleMouseUp(var Message: TWMMouse; const HitInfo: THitInfo); - -// Counterpart to the mouse down handler. +procedure TBaseVirtualTree.DoTimerScroll; var - ReselectFocusedNode: Boolean; + P, + ClientP: TPoint; + InRect, + Panning: Boolean; + R, + ClipRect: TRect; + DeltaX, + DeltaY: Integer; begin - ReleaseCapture; + GetCursorPos(P); + R := ClientRect; + ClipRect := R; + MapWindowPoints(Handle, 0, R, 2); + InRect := PtInRect(R, P); + ClientP := ScreenToClient(P); + Panning := [tsWheelPanning, tsWheelScrolling] * FStates <> []; - if not (tsVCLDragPending in FStates) then + if IsMouseSelecting or InRect or Panning then begin - // reset pending or persistent states - if IsMouseSelecting then + DeltaX := 0; + DeltaY := 0; + if sdUp in FScrollDirections then begin - DoStateChange([], [tsDrawSelecting, tsDrawSelPending, tsToggleFocusedSelection, tsClearOnNewSelection]); - Invalidate; + if Panning then + DeltaY := FLastClickPos.Y - ClientP.Y - 8 + else + if InRect then + DeltaY := Min(FScrollBarOptions.VerticalIncrement, ClientHeight) + else + DeltaY := Min(FScrollBarOptions.VerticalIncrement, ClientHeight) * Abs(R.Top - P.Y); + if FOffsetY = 0 then + Exclude(FScrollDirections, sdUp); end; - if tsClearPending in FStates then + if sdDown in FScrollDirections then begin - ReselectFocusedNode := Assigned(FFocusedNode) and (vsSelected in FFocusedNode.States); - ClearSelection; - if ReselectFocusedNode then - AddToSelection(FFocusedNode, False); + if Panning then + DeltaY := FLastClickPos.Y - ClientP.Y + 8 + else + if InRect then + DeltaY := -Min(FScrollBarOptions.VerticalIncrement, ClientHeight) + else + DeltaY := -Min(FScrollBarOptions.VerticalIncrement, ClientHeight) * Abs(P.Y - R.Bottom); + if (ClientHeight - FOffsetY) = Integer(FRangeY) then + Exclude(FScrollDirections, sdDown); end; - if (tsToggleFocusedSelection in FStates) and (HitInfo.HitNode = FFocusedNode) and Assigned(HitInfo.HitNode) then //Prevent AV when dereferencing HitInfo.HitNode below, see bug #100 + if sdLeft in FScrollDirections then begin - if vsSelected in HitInfo.HitNode.States then - begin - if not (toAlwaysSelectNode in TreeOptions.SelectionOptions) or (Self.SelectedCount > 1) then - RemoveFromSelection(HitInfo.HitNode); - end + if Panning then + DeltaX := FLastClickPos.X - ClientP.X - 8 else - AddToSelection(HitInfo.HitNode, False); + if InRect then + DeltaX := FScrollBarOptions.HorizontalIncrement + else + DeltaX := FScrollBarOptions.HorizontalIncrement * Abs(R.Left - P.X); + if FEffectiveOffsetX = 0 then + Exclude(FScrollDirections, sdleft); end; - DoStateChange([], [tsOLEDragPending, tsOLEDragging, tsClearPending, tsDrawSelPending, tsToggleFocusedSelection, - tsScrollPending, tsScrolling]); - StopTimer(ScrollTimer); - - if (FHeader.Columns.FClickIndex > NoColumn) and (FHeader.Columns.FClickIndex = HitInfo.HitColumn) then - DoColumnClick(HitInfo.HitColumn, KeysToShiftState(Message.Keys)); + if sdRight in FScrollDirections then + begin + if Panning then + DeltaX := FLastClickPos.X - ClientP.X + 8 + else + if InRect then + DeltaX := -FScrollBarOptions.HorizontalIncrement + else + DeltaX := -FScrollBarOptions.HorizontalIncrement * Abs(P.X - R.Right); - if FLastHitInfo.HitNode <> nil then begin // Use THitInfo of mouse down here, see issue #692 - DoNodeClick(FLastHitInfo); - if Assigned(FLastHitInfo.HitNode) then begin - InvalidateNode(FLastHitInfo.HitNode); - FLastHitInfo.HitNode := nil; // prevent firing the event again - end;//if + if (ClientWidth + FEffectiveOffsetX) = Integer(FRangeX) then + Exclude(FScrollDirections, sdRight); end; - // handle a pending edit event - if tsEditPending in FStates then + if UseRightToLeftAlignment then + DeltaX := - DeltaX; + + if IsMouseSelecting then begin - // Is the mouse still over the same node? - if (HitInfo.HitNode = FFocusedNode) and (hiOnItem in HitInfo.HitPositions) and - (toEditOnClick in FOptions.MiscOptions) and (FFocusedColumn = HitInfo.HitColumn) and - CanEdit(FFocusedNode, HitInfo.HitColumn) then - begin - FEditColumn := FFocusedColumn; - SetTimer(Handle, EditTimer, FEditDelay, nil); - end + // In order to avoid scrolling the area which needs a repaint due to the changed selection rectangle + // we limit the scroll area explicitely. + OffsetRect(ClipRect, DeltaX, DeltaY); + DoSetOffsetXY(Point(FOffsetX + DeltaX, FOffsetY + DeltaY), DefaultScrollUpdateFlags, @ClipRect); + // When selecting with the mouse then either update only the parts of the window which have been uncovered + // by the scroll operation if no change in the selection happend or invalidate and redraw the entire + // client area otherwise (to avoid the time consuming task of determining the display rectangles of every + // changed node). + if CalculateSelectionRect(ClientP.X, ClientP.Y) and HandleDrawSelection(ClientP.X, ClientP.Y) then + InvalidateRect(Handle, nil, False) else - DoStateChange([], [tsEditPending]); + begin + // The selection did not change so invalidate only the part of the window which really needs an update. + // 1) Invalidate the parts uncovered by the scroll operation. Add another offset range, we have to + // scroll only one stripe but have to update two. + OffsetRect(ClipRect, DeltaX, DeltaY); + SubtractRect(ClipRect, ClientRect, ClipRect); + InvalidateRect(Handle, @ClipRect, False); + + // 2) Invalidate the selection rectangles. + UnionRect(ClipRect, OrderRect(FNewSelRect), OrderRect(FLastSelRect)); + OffsetRect(ClipRect, FOffsetX, FOffsetY); + InvalidateRect(Handle, @ClipRect, False); + end; + end + else + begin + // Scroll only if there is no drag'n drop in progress. Drag'n drop scrolling is handled in DragOver. + if ((FDragManager = nil) or not DragManager.IsDropTarget) and ((DeltaX <> 0) or (DeltaY <> 0)) then + DoSetOffsetXY(Point(FOffsetX + DeltaX, FOffsetY + DeltaY), DefaultScrollUpdateFlags, nil); + end; + UpdateWindow(Handle); + + if (FScrollDirections = []) and ([tsWheelPanning, tsWheelScrolling] * FStates = []) then + begin + StopTimer(ScrollTimer); + DoStateChange([], [tsScrollPending, tsScrolling]); end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.HasImage(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex): Boolean; +procedure TBaseVirtualTree.DoUpdating(State: TVTUpdateState); -// Determines whether the given node has got an image of the given kind in the given column. -// Returns True if so, otherwise False. -// The given node will be implicitly initialized if needed. +begin + if Assigned(FOnUpdating) then + FOnUpdating(Self, State); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DoValidateCache(): Boolean; + +// This method fills the cache, which is used to speed up searching for nodes. +// The strategy is simple: Take the current number of visible nodes and distribute evenly a number of marks +// (which are stored in FPositionCache) so that iterating through the tree doesn't cost too much time. +// If there are less than 'CacheThreshold' nodes in the tree then the cache remains empty. +// Result is True if the cache was filled without interruption, otherwise False. +// Note: You can adjust the maximum number of nodes between two cache entries by changing CacheThreshold. var - Ghosted: Boolean; - Index: TImageIndex; + EntryCount, + CurrentTop, + Index: Cardinal; + CurrentNode, + Temp: PVirtualNode; begin - if not (vsInitialized in Node.States) then - InitNode(Node); + EntryCount := 0; + if not (tsStopValidation in FStates) then + begin + if FStartIndex = 0 then + FPositionCache := nil; - Index := -1; - Ghosted := False; - DoGetImageIndex(Node, Kind, Column, Ghosted, Index); - Result := Index > -1; -end; + EntryCount := CalculateCacheEntryCount; + SetLength(FPositionCache, EntryCount); + if FStartIndex > EntryCount then + FStartIndex := EntryCount; -//---------------------------------------------------------------------------------------------------------------------- + // Optimize validation by starting with FStartIndex if set. + if (FStartIndex > 0) and Assigned(FPositionCache[FStartIndex - 1].Node) then + begin + // Index is the current entry in FPositionCache. + Index := FStartIndex - 1; + // Running term for absolute top value. + CurrentTop := FPositionCache[Index].AbsoluteTop; + // Running node pointer. + CurrentNode := FPositionCache[Index].Node; + end + else + begin + // Index is the current entry in FPositionCache. + Index := 0; + // Running term for absolute top value. + CurrentTop := 0; + // Running node pointer. + CurrentNode := GetFirstVisibleNoInit(nil, True); + end; -function TBaseVirtualTree.HasPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Pos: TPoint): Boolean; + // EntryCount serves as counter for processed nodes here. This value can always start at 0 as + // the validation either starts also at index 0 or an index which is always a multiple of CacheThreshold + // and EntryCount is only used with modulo CacheThreshold. + EntryCount := 0; + if Assigned(CurrentNode) then + begin + while not (tsStopValidation in FStates) do + begin + // If the cache is full then stop the loop. + if (Integer(Index) >= Length(FPositionCache)) then + Break; + if (EntryCount mod CacheThreshold) = 0 then + begin + // New cache entry to set up. + with FPositionCache[Index] do + begin + Node := CurrentNode; // 2 EAccessViolation seen here in TreeSize V4.3.1, 1 in V4.4.0 (Write of address 00000000) + AbsoluteTop := CurrentTop; + end; + Inc(Index); + end; + + Inc(CurrentTop, NodeHeight[CurrentNode]); + // Advance to next visible node. + Temp := GetNextVisibleNoInit(CurrentNode, True); + // If there is no further node then stop the loop. + if (Temp = nil) then // CHANGED: 17.09.2013 - Veit Zimmermann + Break; // CHANGED: 17.09.2013 - Veit Zimmermann + + CurrentNode := Temp; + Inc(EntryCount); + end; + end; + // Finalize the position cache so no nil entry remains there. + if not (tsStopValidation in FStates) and (Integer(Index) <= High(FPositionCache)) then + begin + SetLength(FPositionCache, Index + 1); + with FPositionCache[Index] do + begin + Node := CurrentNode; + AbsoluteTop := CurrentTop; + end; + end; + end; -// Determines whether the tree got a popup menu, either in its PopupMenu property, via the OnGetPopupMenu event or -// through inheritance. The latter case must be checked by the descendant which must override this method. + Result := (EntryCount > 0) and not (tsStopValidation in FStates); -begin - Result := Assigned(PopupMenu) or Assigned(DoGetPopupMenu(Node, Column, Pos)); + // In variable node height mode it might have happend that some or all of the nodes have been adjusted in their + // height. During validation updates of the scrollbars is disabled so let's do this here. + if Result and (toVariableNodeHeight in FOptions.MiscOptions) then + begin + TThread.Queue(nil, procedure begin UpdateScrollBars(True) end); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InitChildren(Node: PVirtualNode); - -// Initiates the initialization of the child number of the given node. - +procedure TBaseVirtualTree.DragAndDrop(AllowedEffects: Dword; const DataObject: IDataObject; var DragEffect: Integer); var - Count: Cardinal; - + lDragEffect: DWord; // required for type compatibility with SHDoDragDrop begin - if Assigned(Node) and (Node <> FRoot) and (vsHasChildren in Node.States) then + if IsWinVistaOrAbove then begin - Count := Node.ChildCount; - if DoInitChildren(Node, Count) then - begin - SetChildCount(Node, Count); - if Count = 0 then - Exclude(Node.States, vsHasChildren); - end; - end; -end; + lDragEffect := DWord(DragEffect); + SHDoDragDrop(Self.Handle, DataObject, nil, AllowedEffects, lDragEffect); // supports drag hints on Windows Vista and later + DragEffect := Integer(lDragEffect); + end + else + Winapi.ActiveX.DoDragDrop(DataObject, DragManager as IDropSource, AllowedEffects, DragEffect); + end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InitNode(Node: PVirtualNode); +procedure TBaseVirtualTree.DragCanceled; -// Initiates the initialization of the given node to allow the application to load needed data for it. +// Does some housekeeping for VCL drag'n drop; -var - InitStates: TVirtualNodeInitStates; - MustAdjustInternalVariables: Boolean; - ParentCheckState, SelfCheckState: TCheckState; begin - with Node^ do - begin - Include(States, vsInitializing); - try - InitStates := []; - if vsInitialized in States then - Include(InitStates, ivsReInit); - Include(States, vsInitialized); - if Parent = FRoot then - DoInitNode(nil, Node, InitStates) - else - DoInitNode(Parent, Node, InitStates); + inherited; - // Fix: Any parent check state must be propagated here. - // Because the CheckType is normally set in DoInitNode - // by the App. - if (Node.CheckType = ctTriStateCheckBox) and (toAutoTristateTracking in FOptions.AutoOptions) then - begin - ParentCheckState := Self.GetCheckState(Node.Parent); - SelfCheckState := Self.GetCheckState(Node); - if ((ParentCheckState = csCheckedNormal) - or (ParentCheckState = csUncheckedNormal)) - and (not SelfCheckState.IsDisabled()) - and (SelfCheckState <> ParentCheckState) - and (Parent <> FRoot) - then - SetCheckState(Node, Node.Parent.CheckState); - end - else if (toSyncCheckboxesWithSelection in TreeOptions.SelectionOptions) then - Node.CheckType := TCheckType.ctCheckBox; + DragFinished; +end; - if ivsDisabled in InitStates then - Include(States, vsDisabled); - if ivsHasChildren in InitStates then - Include(States, vsHasChildren); - if ivsSelected in InitStates then - InternalAddToSelection(Node, False); - if ivsMultiline in InitStates then - Include(States, vsMultiline); - if ivsFiltered in InitStates then - begin - MustAdjustInternalVariables := not ((ivsReInit in InitStates) and (vsFiltered in States)); +//---------------------------------------------------------------------------------------------------------------------- - Include(States, vsFiltered); +function TBaseVirtualTree.DragDrop(const DataObject: IDataObject; KeyState: Integer; Pt: TPoint; + var Effect: Integer): HResult; - if not (toShowFilteredNodes in FOptions.PaintOptions) and MustAdjustInternalVariables then - begin - AdjustTotalHeight(Node, -NodeHeight, True); - if FullyVisible[Node] then - Dec(FVisibleCount); - if FUpdateCount = 0 then - UpdateScrollBars(True); - end; - end; +var + Shift: TShiftState; + EnumFormat: IEnumFormatEtc; + Fetched: Integer; + OLEFormat: TFormatEtc; + Formats: TFormatArray; - // Expanded may already be set (when called from ReinitNode) or be set in DoInitNode, allow both. - if (vsExpanded in Node.States) xor (ivsExpanded in InitStates) then - begin - // Expand node if not yet done (this will automatically initialize child nodes). - if ivsExpanded in InitStates then - ToggleNode(Node) +begin + StopTimer(ExpandTimer); + StopTimer(ScrollTimer); + DoStateChange([], [tsScrollPending, tsScrolling]); + Formats := nil; + + // Ask explicitly again whether the action is allowed. Otherwise we may accept a drop which is intentionally not + // allowed but cannot be prevented by the application because when the tree was scrolling while dropping + // no DragOver event is created by the OLE subsystem. + Result := DragOver(DragManager.DragSource, KeyState, dsDragMove, Pt, Effect); + try + if (Result <> NOERROR) or ((Effect and not DROPEFFECT_SCROLL) = DROPEFFECT_NONE) then + Result := E_FAIL + else + begin + try + Shift := KeysToShiftState(KeyState); + if tsRightButtonDown in FStates then + Include(Shift, ssRight) + else if tsMiddleButtonDown in FStates then + Include(Shift, ssMiddle) else - // If the node already was expanded then explicitly trigger child initialization. - if vsHasChildren in Node.States then - InitChildren(Node); + Include(Shift, ssLeft); + Pt := ScreenToClient(Pt); + // Determine which formats we can get and pass them along with the data object to the drop handler. + Result := DataObject.EnumFormatEtc(DATADIR_GET, EnumFormat); + if Failed(Result) then + Abort; + Result := EnumFormat.Reset; + if Failed(Result) then + Abort; + // create a list of available formats + while EnumFormat.Next(1, OLEFormat, @Fetched) = S_OK do + begin + SetLength(Formats, Length(Formats) + 1); + Formats[High(Formats)] := OLEFormat.cfFormat; + end; + DoDragDrop(DragManager.DragSource, DataObject, Formats, Shift, Pt, Effect, FLastDropMode); + except + // An unhandled exception here leaks memory. + Application.HandleException(Self); + Result := E_UNEXPECTED; end; - finally - Exclude(States, vsInitializing); + end; + finally + if Assigned(FDropTargetNode) then + begin + InvalidateNode(FDropTargetNode); + FDropTargetNode := nil; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InternalAddFromStream(Stream: TStream; Version: Integer; Node: PVirtualNode); +function TBaseVirtualTree.DragEnter(KeyState: Integer; Pt: TPoint; var Effect: Integer): HResult; -// Loads all details for Node (including its children) from the given stream. -// Because the new nodes might be selected this method also fixes the selection array. +// callback routine for the drop target interface var - Stop: PVirtualNode; - Index: Integer; - LastTotalHeight: Cardinal; - WasFullyVisible: Boolean; + Shift: TShiftState; + Accept: Boolean; + R: TRect; + HitInfo: THitInfo; begin - Assert(Node <> FRoot, 'The root node cannot be loaded from stream.'); - - // Keep the current total height value of Node as it has already been applied - // but might change in the load and fixup code. We have to adjust that afterwards. - LastTotalHeight := Node.TotalHeight; - WasFullyVisible := FullyVisible[Node] and not IsEffectivelyFiltered[Node]; - - // Read in the new nodes. - ReadNode(Stream, Version, Node); - - // One time update of node-internal states and the global visibility counter. - // This is located here to ease and speed up the loading process. - FixupTotalCount(Node); - AdjustTotalCount(Node.Parent, Node.TotalCount - 1, True); // -1 because Node itself was already set. - FixupTotalHeight(Node); - AdjustTotalHeight(Node.Parent, Integer(Node.TotalHeight) - Integer(LastTotalHeight), True); - - // New nodes are always visible, so the visible node count has been increased already. - // If Node is now invisible we have to take back this increment and don't need to add any visible child node. - if not FullyVisible[Node] or IsEffectivelyFiltered[Node] then - begin - if WasFullyVisible then - Dec(FVisibleCount); - end - else - // It can never happen that the node is now fully visible but was not before as this would require - // that the visibility state of one of its parents has changed, which cannot happen during loading. - Inc(FVisibleCount, CountVisibleChildren(Node)); - - // Fix selection array. - ClearTempCache; - if Node = FRoot then - Stop := nil - else - Stop := Node.NextSibling; + try + // Determine acceptance of drag operation and reset scroll start time. + FDragScrollStart := 0; - if toMultiSelect in FOptions.SelectionOptions then - begin - // Add all nodes which were selected before to the current selection (unless they are already there). - while Node <> Stop do - begin - if (vsSelected in Node.States) and not FindNodeInSelection(Node, Index, 0, High(FSelection)) then - InternalCacheNode(Node); - Node := GetNextNoInit(Node); - end; - if FTempNodeCount > 0 then - AddToSelection(FTempNodeCache, FTempNodeCount, True); - ClearTempCache; - end - else // No further selected nodes allowed so delete the corresponding flag in all new nodes. - while Node <> Stop do + Shift := KeysToShiftState(KeyState); + if tsLeftButtonDown in FStates then + Include(Shift, ssLeft); + if tsMiddleButtonDown in FStates then + Include(Shift, ssMiddle); + if tsRightButtonDown in FStates then + Include(Shift, ssRight); + Pt := ScreenToClient(Pt); + Effect := SuggestDropEffect(DragManager.DragSource, Shift, Pt, Effect); + Accept := DoDragOver(DragManager.DragSource, Shift, dsDragEnter, Pt, FLastDropMode, Effect); + if not Accept then + Effect := DROPEFFECT_NONE + else begin - Exclude(Node.States, vsSelected); - Node := GetNextNoInit(Node); + // Set initial drop target node and drop mode. + GetHitTestInfoAt(Pt.X, Pt.Y, True, HitInfo); + if Assigned(HitInfo.HitNode) then + begin + FDropTargetNode := HitInfo.HitNode; + R := GetDisplayRect(HitInfo.HitNode, FHeader.MainColumn, False); + //VSOFT CHANGE - changed back to 4.8.5 behaviour + if (hiOnItemLabel in HitInfo.HitPositions) or ((hiOnItem in HitInfo.HitPositions) and + ((toFullRowDrag in FOptions.MiscOptions){ or (toFullRowSelect in FOptions.SelectionOptions)}))then + FLastDropMode := dmOnNode + else + if ((R.Top + R.Bottom) div 2) > Pt.Y then + FLastDropMode := dmAbove + else + FLastDropMode := dmBelow; + end + else + FLastDropMode := dmNowhere; end; + + // If the drag source is a virtual tree then we know how to control the drag image + // and can show it even if the source is not the target tree. + // This is only necessary if we cannot use the drag image helper interfaces. + if not DragManager.DropTargetHelperSupported and Assigned(DragManager.DragSource) then + DragManager.DragSource.FDragImage.ShowDragImage; + Result := NOERROR; + except + Result := E_UNEXPECTED; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.InternalAddToSelection(Node: PVirtualNode; ForceInsert: Boolean): Boolean; +procedure TBaseVirtualTree.DragFinished; + +// Called by DragCancelled or EndDrag to make up for the still missing mouse button up messages. +// These are important for such important things like popup menus. + var - lSingletonNodeArray: TNodeArray; + P: TPoint; + begin - Assert(Assigned(Node), 'Node must not be nil!'); - SetLength(lSingletonNodeArray, 1); - lSingletonNodeArray[0] := Node; - Result := InternalAddToSelection(lSingletonNodeArray, 1, ForceInsert); + if [tsOLEDragging, tsVCLDragPending, tsVCLDragging, tsVCLDragFinished] * FStates = [] then + Exit; + + DoStateChange([], [tsVCLDragPending, tsVCLDragging, tsUserDragObject, tsVCLDragFinished]); + + GetCursorPos(P); + P := ScreenToClient(P); + if tsRightButtonDown in FStates then + Perform(WM_RBUTTONUP, 0, LPARAM(Integer(PointToSmallPoint(P)))) + else + if tsMiddleButtonDown in FStates then + Perform(WM_MBUTTONUP, 0, LPARAM(Integer(PointToSmallPoint(P)))) + else + Perform(WM_LBUTTONUP, 0, LPARAM(Integer(PointToSmallPoint(P)))); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.InternalAddToSelection(const NewItems: TNodeArray; NewLength: Integer; - ForceInsert: Boolean): Boolean; - -// Internal version of method AddToSelection which does not trigger OnChange events +procedure TBaseVirtualTree.DragLeave; var - I, J: Integer; - CurrentEnd: Integer; - Constrained, - SiblingConstrained: Boolean; - lPreviousSelectedCount: Integer; - AddedNodesSize: Integer; - PTmpNode: PVirtualNode; + Effect: Integer; begin - lPreviousSelectedCount := FSelectionCount; - // The idea behind this code is to use a kind of reverse merge sort. QuickSort is quite fast - // and would do the job here too but has a serious problem with already sorted lists like FSelection. + StopTimer(ExpandTimer); - // current number of valid entries - AddedNodesSize := 0; + if not DragManager.DropTargetHelperSupported and Assigned(DragManager.DragSource) then + DragManager.DragSource.FDragImage.HideDragImage; - // 1) Remove already selected items, mark all other as being selected. - if ForceInsert then + if Assigned(FDropTargetNode) then begin - //Fix: For already selected node when selected, this path - //is used that didn't contain the Constraint logic. Added. - Constrained := toLevelSelectConstraint in FOptions.SelectionOptions; - if Constrained and (FLastSelectionLevel = -1) then - FLastSelectionLevel := GetNodeLevelForSelectConstraint(NewItems[0]); - AddedNodesSize := NewLength; + InvalidateNode(FDropTargetNode); + FDropTargetNode := nil; + end; + UpdateWindow(Handle); + + Effect := 0; + DoDragOver(nil, [], dsDragLeave, Point(0, 0), FLastDropMode, Effect); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.DragOver(Source: TObject; KeyState: Integer; DragState: TDragState; Pt: TPoint; + var Effect: Integer): HResult; + +// callback routine for the drop target interface + +var + Shift: TShiftState; + Accept, + DragImageWillMove, + WindowScrolled: Boolean; + OldR, R: TRect; + NewDropMode: TDropMode; + HitInfo: THitInfo; + DragPos: TPoint; + Tree: TBaseVirtualTree; + LastNode: PVirtualNode; + DeltaX, + DeltaY: Integer; + ScrollOptions: TScrollUpdateOptions; + +begin + if not DragManager.DropTargetHelperSupported and (Source is TBaseVirtualTree) then + begin + Tree := Source as TBaseVirtualTree; + ScrollOptions := [suoUpdateNCArea]; end else begin - Constrained := toLevelSelectConstraint in FOptions.SelectionOptions; - if Constrained and (FLastSelectionLevel = -1) then - FLastSelectionLevel := GetNodeLevelForSelectConstraint(NewItems[0]); - SiblingConstrained := toSiblingSelectConstraint in FOptions.SelectionOptions; - if SiblingConstrained and (FRangeAnchor = nil) then - FRangeAnchor := NewItems[0]; - - for I := 0 to NewLength - 1 do - if ([vsSelected, vsDisabled] * NewItems[I].States <> []) or - (Constrained and (Cardinal(FLastSelectionLevel) <> GetNodeLevel(NewItems[I]))) or - (SiblingConstrained and (FRangeAnchor.Parent <> NewItems[I].Parent)) - then - Inc(PAnsiChar(NewItems[I])) // mark as invalid by setting the LSB - else - Inc(AddedNodesSize); + Tree := nil; + ScrollOptions := DefaultScrollUpdateFlags; end; - I := PackArray(NewItems, NewLength); - if I > -1 then - NewLength := I; + try + DragPos := Pt; + Pt := ScreenToClient(Pt); - Result := NewLength > 0; - if Result then - begin - // 2) Sort the new item list so we can easily traverse it. - if NewLength > 1 then - QuickSort(NewItems, 0, NewLength - 1); - // 3) Make room in FSelection for the new items. - if lPreviousSelectedCount + NewLength >= Length(FSelection) then - SetLength(FSelection, lPreviousSelectedCount + NewLength); + // Check if we have to scroll the client area. + FScrollDirections := DetermineScrollDirections(Pt.X, Pt.Y); + DeltaX := 0; + DeltaY := 0; + if FScrollDirections <> [] then + begin + // Determine amount to scroll. + if sdUp in FScrollDirections then + begin + DeltaY := Min(FScrollBarOptions.VerticalIncrement, ClientHeight); + if FOffsetY = 0 then + Exclude(FScrollDirections, sdUp); + end; + if sdDown in FScrollDirections then + begin + DeltaY := -Min(FScrollBarOptions.VerticalIncrement, ClientHeight); + if (ClientHeight - FOffsetY) = Integer(FRangeY) then + Exclude(FScrollDirections, sdDown); + end; + if sdLeft in FScrollDirections then + begin + DeltaX := FScrollBarOptions.HorizontalIncrement; + if FEffectiveOffsetX = 0 then + Exclude(FScrollDirections, sdleft); + end; + if sdRight in FScrollDirections then + begin + DeltaX := -FScrollBarOptions.HorizontalIncrement; + if (ClientWidth + FEffectiveOffsetX) = Integer(FRangeX) then + Exclude(FScrollDirections, sdRight); + end; + WindowScrolled := DoSetOffsetXY(Point(FOffsetX + DeltaX, FOffsetY + DeltaY), ScrollOptions, nil); + end + else + WindowScrolled := False; - // 4) Merge in new items - J := NewLength - 1; - CurrentEnd := lPreviousSelectedCount - 1; + // Determine acceptance of drag operation as well as drag target. + Shift := KeysToShiftState(KeyState); + if tsLeftButtonDown in FStates then + Include(Shift, ssLeft); + if tsMiddleButtonDown in FStates then + Include(Shift, ssMiddle); + if tsRightButtonDown in FStates then + Include(Shift, ssRight); + GetHitTestInfoAt(Pt.X, Pt.Y, True, HitInfo); - while J >= 0 do + if Assigned(HitInfo.HitNode) then + R := GetDisplayRect(HitInfo.HitNode, NoColumn, False) + else + R := Rect(0, 0, 0, 0); + NewDropMode := DetermineDropMode(Pt, HitInfo, R); + + if Assigned(Tree) then + DragImageWillMove := Tree.DragImage.WillMove(DragPos) + else + DragImageWillMove := False; + + if (HitInfo.HitNode <> FDropTargetNode) or (FLastDropMode <> NewDropMode) then begin - // First insert all new entries which are greater than the greatest entry in the old list. - // If the current end marker is < 0 then there's nothing more to move in the selection - // array and only the remaining new items must be inserted. - if CurrentEnd >= 0 then + // Something in the tree will change. This requires to update the screen and/or the drag image. + FLastDropMode := NewDropMode; + if HitInfo.HitNode <> FDropTargetNode then begin - while (J >= 0) and (PAnsiChar(NewItems[J]) > PAnsiChar(FSelection[CurrentEnd])) do + StopTimer(ExpandTimer); + // The last target node is needed for the rectangle determination but must already be set for + // the recapture call, hence it must be stored somewhere. + LastNode := FDropTargetNode; + FDropTargetNode := HitInfo.HitNode; + // In order to show a selection rectangle a column must be focused. + if FFocusedColumn <= NoColumn then + FFocusedColumn := FHeader.MainColumn; + + if Assigned(LastNode) and Assigned(FDropTargetNode) then begin - FSelection[CurrentEnd + J + 1] := NewItems[J]; - Dec(J); + // Optimize the case that the selection moved between two nodes. + OldR := GetDisplayRect(LastNode, NoColumn, False); + UnionRect(R, R, OldR); + if Assigned(Tree) then + begin + if WindowScrolled then + UpdateWindowAndDragImage(Tree, ClientRect, True, not DragImageWillMove) + else + UpdateWindowAndDragImage(Tree, R, False, not DragImageWillMove); + end + else + InvalidateRect(Handle, @R, False); + end + else + begin + if Assigned(LastNode) then + begin + // Repaint last target node. + OldR := GetDisplayRect(LastNode, NoColumn, False); + if Assigned(Tree) then + begin + if WindowScrolled then + UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove) + else + UpdateWindowAndDragImage(Tree, OldR, False, not DragImageWillMove); + end + else + InvalidateRect(Handle, @OldR, False); + end + else + begin + if Assigned(Tree) then + begin + if WindowScrolled then + UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove) + else + UpdateWindowAndDragImage(Tree, R, False, not DragImageWillMove); + end + else + InvalidateRect(Handle, @R, False); + end; end; - // early out if nothing more needs to be copied - if J < 0 then - Break; + + // Start auto expand timer if necessary. + if (toAutoDropExpand in FOptions.AutoOptions) and Assigned(FDropTargetNode) and + (vsHasChildren in FDropTargetNode.States) then + SetTimer(Handle, ExpandTimer, FAutoExpandDelay, nil); end else begin - // insert remaining new entries at position 0 - Move(NewItems[0], FSelection[0], (J + 1) * SizeOf(Pointer)); - // nothing more to do so exit main loop - Break; + // Only the drop mark position changed so invalidate the current drop target node. + if Assigned(Tree) then + begin + if WindowScrolled then + UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove) + else + UpdateWindowAndDragImage(Tree, R, False, not DragImageWillMove); + end + else + InvalidateRect(Handle, @R, False); end; - - // find the last entry in the remaining selection list which is smaller then the largest - // entry in the remaining new items list - FindNodeInSelection(NewItems[J], I, 0, CurrentEnd); - Dec(I); - // move all entries which are greater than the greatest entry in the new items list up - // so the remaining gap travels down to where new items must be inserted - Move(FSelection[I + 1], FSelection[I + J + 2], (CurrentEnd - I) * SizeOf(Pointer)); - CurrentEnd := I; + end + else + begin + // No change in the current drop target or drop mode. This might still mean horizontal or vertical scrolling. + if Assigned(Tree) and ((DeltaX <> 0) or (DeltaY <> 0)) then + UpdateWindowAndDragImage(Tree, ClientRect, WindowScrolled, not DragImageWillMove); end; - // update selection count - Inc(FSelectionCount, AddedNodesSize); + Update; - // post process added nodes - for I := 0 to AddedNodesSize - 1 do - begin - PTmpNode := NewItems[I]; - //sync path note: on click, multi-select ctrl-click and draw selection - Include(PTmpNode.States, vsSelected); - // call on add event callbackevent - if Assigned(FOnAddToSelection) then - FOnAddToSelection(Self, PTmpNode); - if SyncCheckstateWithSelection[PTmpNode] then - checkstate[PTmpNode] := csCheckedNormal; - end; + if Assigned(Tree) and DragImageWillMove then + Tree.FDragImage.DragTo(DragPos, False); - Assert(FSelectionCount = (lPreviousSelectedCount + NewLength), 'Fixing issue #487 seems to ahve caused a problem here.') + Effect := SuggestDropEffect(Source, Shift, Pt, Effect); + Accept := DoDragOver(Source, Shift, DragState, Pt, FLastDropMode, Effect); + if not Accept then + Effect := DROPEFFECT_NONE; + if WindowScrolled then + Effect := Effect or Integer(DROPEFFECT_SCROLL); + Result := NOERROR; + except + Result := E_UNEXPECTED; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InternalCacheNode(Node: PVirtualNode); +procedure TBaseVirtualTree.DrawDottedHLine(const PaintInfo: TVTPaintInfo; Left, Right, Top: Integer); -// Adds the given node to the temporary node cache (used when collecting possibly large amounts of nodes). +// Draws a horizontal line with alternating pixels (this style is not supported for pens under Win9x). var - Len: Cardinal; + R: TRect; begin - Len := Length(FTempNodeCache); - if FTempNodeCount = Len then + with PaintInfo, Canvas do begin - if Len < 100 then - Len := 100 - else - Len := Len + Len div 10; - SetLength(FTempNodeCache, Len); + Brush.Color := FColors.BackGroundColor; + R := Rect(Min(Left, Right), Top, Max(Left, Right) + 1, Top + 1); + Winapi.Windows.FillRect(Handle, R, FDottedBrush + ); end; - FTempNodeCache[FTempNodeCount] := Node; - Inc(FTempNodeCount); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InternalClearSelection(); +procedure TBaseVirtualTree.DrawDottedVLine(const PaintInfo: TVTPaintInfo; Top, Bottom, Left: Integer; UseSelectedBkColor: Boolean = False); + +// Draws a horizontal line with alternating pixels (this style is not supported for pens under Win9x). var - Count: Integer; - lNode: PVirtualNode; + R: TRect; + begin - // It is possible that there are invalid node references in the selection array - // if the tree update is locked and changes in the structure were made. - // Handle this potentially dangerous situation by packing the selection array explicitely. - if IsUpdating then + with PaintInfo, Canvas do begin - Count := PackArray(FSelection, FSelectionCount); - if Count > -1 then + if UseSelectedBkColor then begin - FSelectionCount := Count; - SetLength(FSelection, FSelectionCount); - end; - end; - - while FSelectionCount > 0 do - begin - Dec(FSelectionCount); - lNode := FSelection[FSelectionCount]; - //sync path note: deselect when click on another or on outside area - Exclude(lNode.States, vsSelected); - if SyncCheckstateWithSelection[lNode] then - CheckState[lNode] := csUncheckedNormal; - DoRemoveFromSelection(lNode); + if Focused or (toPopupMode in FOptions.PaintOptions) then + Brush.Color := FColors.FocusedSelectionColor + else + Brush.Color := FColors.UnfocusedSelectionColor; + end + else + Brush.Color := FColors.BackGroundColor; + R := Rect(Left, Min(Top, Bottom), Left + 1, Max(Top, Bottom) + 1); + Winapi.Windows.FillRect(Handle, R, FDottedBrush); end; - ResetRangeAnchor; - FSelection := nil; - DoStateChange([], [tsClearPending]); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InternalConnectNode(Node, Destination: PVirtualNode; Target: TBaseVirtualTree; - Mode: TVTNodeAttachMode); +procedure TBaseVirtualTree.EndOperation(OperationKind: TVTOperationKind); -// Connects Node with Destination depending on Mode. -// No error checking takes place. Node as well as Destination must be valid. Node must never be a root node and -// Destination must not be a root node if Mode is amInsertBefore or amInsertAfter. +// Called to indicate that a long-running operation has finished. -var - Run: PVirtualNode; +begin + Assert(FOperationCount > 0, 'EndOperation must not be called when no operation in progress.'); + Dec(FOperationCount); + DoEndOperation(OperationKind); +end; + +//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.EnsureNodeFocused(); begin - // Keep in mind that the destination node might belong to another tree. - with Target do - begin - case Mode of - amInsertBefore: - begin - Node.PrevSibling := Destination.PrevSibling; - Destination.PrevSibling := Node; - Node.NextSibling := Destination; - Node.Parent := Destination.Parent; - Node.Index := Destination.Index; - if Node.PrevSibling = nil then - Node.Parent.FirstChild := Node - else - Node.PrevSibling.NextSibling := Node; + if FocusedNode = nil then + FocusedNode := Self.GetFirstSelected(); + if FocusedNode = nil then + FocusedNode := Self.GetFirstVisible(); +end; - // reindex all following nodes - Run := Destination; - while Assigned(Run) do - begin - Inc(Run.Index); - Run := Run.NextSibling; - end; - end; - amInsertAfter: - begin - Node.NextSibling := Destination.NextSibling; - Destination.NextSibling := Node; - Node.PrevSibling := Destination; - Node.Parent := Destination.Parent; - if Node.NextSibling = nil then - Node.Parent.LastChild := Node - else - Node.NextSibling.PrevSibling := Node; - Node.Index := Destination.Index; +//---------------------------------------------------------------------------------------------------------------------- - // reindex all following nodes - Run := Node; - while Assigned(Run) do - begin - Inc(Run.Index); - Run := Run.NextSibling; - end; - end; - amAddChildFirst: - begin - if Assigned(Destination.FirstChild) then - begin - // If there's a first child then there must also be a last child. - Destination.FirstChild.PrevSibling := Node; - Node.NextSibling := Destination.FirstChild; - Destination.FirstChild := Node; - end - else - begin - // First child node at this location. - Destination.FirstChild := Node; - Destination.LastChild := Node; - Node.NextSibling := nil; - end; - Node.PrevSibling := nil; - Node.Parent := Destination; - Node.Index := 0; - // reindex all following nodes - Run := Node.NextSibling; - while Assigned(Run) do - begin - Inc(Run.Index); - Run := Run.NextSibling; - end; - end; - amAddChildLast: - begin - if Assigned(Destination.LastChild) then - begin - // If there's a last child then there must also be a first child. - Destination.LastChild.NextSibling := Node; - Node.PrevSibling := Destination.LastChild; - Destination.LastChild := Node; - end - else - begin - // first child node at this location - Destination.FirstChild := Node; - Destination.LastChild := Node; - Node.PrevSibling := nil; - end; - Node.NextSibling := nil; - Node.Parent := Destination; - if Assigned(Node.PrevSibling) then - Node.Index := Node.PrevSibling.Index + 1 - else - Node.Index := 0; - end; +procedure TBaseVirtualTree.EnsureNodeSelected(); +begin + if (toAlwaysSelectNode in TreeOptions.SelectionOptions) and not IsEmpty then + begin + if (SelectedCount = 0) and not SelectionLocked then + begin + if not Assigned(FNextNodeToSelect) then + begin + FNextNodeToSelect := GetFirstVisible; + // Avoid selecting a disabled node, see #954 + while Assigned(FNextNodeToSelect) and IsDisabled[FNextNodeToSelect] do + FNextNodeToSelect := GetNextVisible(FNextNodeToSelect); + end; + Selected[FNextNodeToSelect] := True; + Self.ScrollIntoView(Self.GetFirstSelected, False); + end;// if nothing selected + EnsureNodeFocused(); + end;//if toAlwaysSelectNode +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.FindNodeInSelection(P: PVirtualNode; var Index: Integer; LowBound, + HighBound: Integer): Boolean; + +// Search routine to find a specific node in the selection array. +// LowBound and HighBound determine the range in which to search the node. +// Either value can be -1 to denote the maximum range otherwise LowBound must be less or equal HighBound. + +var + L, H, + I: Integer; + +begin + Result := False; + L := 0; + if LowBound >= 0 then + L := LowBound; + H := FSelectionCount - 1; + if HighBound >= 0 then + H := HighBound; + while L <= H do + begin + I := (L + H) shr 1; + if PAnsiChar(FSelection[I]) < PAnsiChar(P) then + L := I + 1 else - // amNoWhere: do nothing + begin + H := I - 1; + if FSelection[I] = P then + begin + Result := True; + L := I; + end; end; - // Remove temporary states. - Node.States := Node.States - [vsChecking, vsCutOrCopy, vsDeleting]; + end; + Index := L; +end; - if (Mode <> amNoWhere) then begin - Inc(Node.Parent.ChildCount); - Include(Node.Parent.States, vsHasChildren); - AdjustTotalCount(Node.Parent, Node.TotalCount, True); +//---------------------------------------------------------------------------------------------------------------------- - // Add the new node's height only if its parent is expanded. - if (vsExpanded in Node.Parent.States) and (vsVisible in Node.States) then begin - AdjustTotalHeight(Node.Parent, Node.TotalHeight, True); - Inc(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); - end;//if +procedure TBaseVirtualTree.FinishChunkHeader(Stream: TStream; StartPos, EndPos: Integer); - // Update the hidden children flag of the parent. - if (Node.Parent <> FRoot) then - begin - // If we have added a visible node then simply remove the all-children-hidden flag. - if IsEffectivelyVisible[Node] then - Exclude(Node.Parent.States, vsAllChildrenHidden) - else begin - // If we have added an invisible node and this is the only child node then - // make sure the all-children-hidden flag is in a determined state. - // If there were child nodes before then no action is needed. - if Node.Parent.ChildCount = 1 then - Include(Node.Parent.States, vsAllChildrenHidden); - end;//else - end; //if Node.Parent <> FRoot - end;//if Mode <> amNoWhere - end;//With +// used while streaming out a node to finally write out the size of the chunk + +var + Size: Integer; + +begin + // seek back to the second entry in the chunk header + Stream.Position := StartPos + SizeOf(Size); + // determine size of chunk without the chunk header + Size := EndPos - StartPos - SizeOf(TChunkHeader); + // write the size... + Stream.Write(Size, SizeOf(Size)); + // ... and seek to the last endposition + Stream.Position := EndPos; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.InternalData(Node: PVirtualNode): Pointer; +procedure TBaseVirtualTree.FontChanged(AFont: TObject); + +// Little helper function for font changes (as they are not tracked in TBitmap/TCanvas.OnChange). begin - Result := nil; + FFontChanged := True; + if Assigned(FOldFontChange) then + FOldFontChange(AFont); + //if not (tsPainting in TreeStates) then AutoScale(); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InternalDisconnectNode(Node: PVirtualNode; KeepFocus: Boolean; Reindex: Boolean = True; ParentClearing: Boolean = False); +function TBaseVirtualTree.GetBorderDimensions: TSize; -// Disconnects the given node from its parent and siblings. The node's pointer are not reset so they can still be used -// after return from this method (probably a very short time only!). -// If KeepFocus is True then the focused node is not reset. This is useful if the given node is reconnected to the tree -// immediately after return of this method and should stay being the focused node if it was it before. -// Note: Node must not be nil or the root node. +// Returns the overall width of the current window border, depending on border styles. +// Note: these numbers represent the system's standards not special properties, which can be set for TWinControl +// (e.g. bevels, border width). var - Parent, - Run: PVirtualNode; - Index: Integer; - AdjustHeight: Boolean; + Styles: Integer; begin - Assert(Assigned(Node) and (Node <> FRoot), 'Node must neither be nil nor the root node.'); + Result.cx := 0; + Result.cy := 0; - if (Node = FFocusedNode) and not KeepFocus then + Styles := GetWindowLong(Handle, GWL_STYLE); + if (Styles and WS_BORDER) <> 0 then begin - DoFocusNode(nil, False); - DoFocusChange(FFocusedNode, FFocusedColumn); + Dec(Result.cx); + Dec(Result.cy); + end; + if (Styles and WS_THICKFRAME) <> 0 then + begin + Dec(Result.cx, GetSystemMetrics(SM_CXFIXEDFRAME)); + Dec(Result.cy, GetSystemMetrics(SM_CYFIXEDFRAME)); + end; + Styles := GetWindowLong(Handle, GWL_EXSTYLE); + if (Styles and WS_EX_CLIENTEDGE) <> 0 then + begin + Dec(Result.cx, GetSystemMetrics(SM_CXEDGE)); + Dec(Result.cy, GetSystemMetrics(SM_CYEDGE)); end; +end; - if Node = FRangeAnchor then - ResetRangeAnchor; +//---------------------------------------------------------------------------------------------------------------------- - // Update the hidden children flag of the parent. - if (Node.Parent <> FRoot) and not (ParentClearing) then - if FUpdateCount = 0 then - DetermineHiddenChildrenFlag(Node.Parent) - else - Include(FStates, tsUpdateHiddenChildrenNeeded); +function TBaseVirtualTree.GetCheckImage(Node: PVirtualNode; ImgCheckType: TCheckType = ctNone; ImgCheckState: + TCheckState = csUncheckedNormal; ImgEnabled: Boolean = True): Integer; - if not (vsDeleting in Node.States) then +// Determines the index into the check image list for the given node depending on the check type +// and enabled state. + +const + // Four dimensional array consisting of image indices for the check type, the check state, the enabled state and the + // hot state. + CheckStateToCheckImage: array[ctCheckBox..ctButton, csUncheckedNormal..csMixedPressed, Boolean, Boolean] of Integer = ( + // ctCheckBox, ctTriStateCheckBox + ( + // csUncheckedNormal (disabled [not hot, hot], enabled [not hot, hot]) + ((ckCheckUncheckedDisabled, ckCheckUncheckedDisabled), (ckCheckUncheckedNormal, ckCheckUncheckedHot)), + // csUncheckedPressed (disabled [not hot, hot], enabled [not hot, hot]) + ((ckCheckUncheckedDisabled, ckCheckUncheckedDisabled), (ckCheckUncheckedPressed, ckCheckUncheckedPressed)), + // csCheckedNormal + ((ckCheckCheckedDisabled, ckCheckCheckedDisabled), (ckCheckCheckedNormal, ckCheckCheckedHot)), + // csCheckedPressed + ((ckCheckCheckedDisabled, ckCheckCheckedDisabled), (ckCheckCheckedPressed, ckCheckCheckedPressed)), + // csMixedNormal + ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedNormal, ckCheckMixedHot)), + // csMixedPressed + ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedPressed, ckCheckMixedPressed)) + ), + // ctRadioButton + ( + // csUncheckedNormal (disabled [not hot, hot], enabled [not hot, hot]) + ((ckRadioUncheckedDisabled, ckRadioUncheckedDisabled), (ckRadioUncheckedNormal, ckRadioUncheckedHot)), + // csUncheckedPressed (disabled [not hot, hot], enabled [not hot, hot]) + ((ckRadioUncheckedDisabled, ckRadioUncheckedDisabled), (ckRadioUncheckedPressed, ckRadioUncheckedPressed)), + // csCheckedNormal + ((ckRadioCheckedDisabled, ckRadioCheckedDisabled), (ckRadioCheckedNormal, ckRadioCheckedHot)), + // csCheckedPressed + ((ckRadioCheckedDisabled, ckRadioCheckedDisabled), (ckRadioCheckedPressed, ckRadioCheckedPressed)), + // csMixedNormal (should never appear with ctRadioButton) + ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedNormal, ckCheckMixedHot)), + // csMixedPressed (should never appear with ctRadioButton) + ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedPressed, ckCheckMixedPressed)) + ), + // ctButton + ( + // csUncheckedNormal (disabled [not hot, hot], enabled [not hot, hot]) + ((ckButtonDisabled, ckButtonDisabled), (ckButtonNormal, ckButtonHot)), + // csUncheckedPressed (disabled [not hot, hot], enabled [not hot, hot]) + ((ckButtonDisabled, ckButtonDisabled), (ckButtonPressed, ckButtonPressed)), + // csCheckedNormal + ((ckButtonDisabled, ckButtonDisabled), (ckButtonNormal, ckButtonHot)), + // csCheckedPressed + ((ckButtonDisabled, ckButtonDisabled), (ckButtonPressed, ckButtonPressed)), + // csMixedNormal (should never appear with ctButton) + ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedNormal, ckCheckMixedHot)), + // csMixedPressed (should never appear with ctButton) + ((ckCheckMixedDisabled, ckCheckMixedDisabled), (ckCheckMixedPressed, ckCheckMixedPressed)) + ) + ); + +var + IsHot: Boolean; + +begin + if Assigned(Node) then begin - // Some states are only temporary so take them out. - Node.States := Node.States - [vsChecking]; - Parent := Node.Parent; - Dec(Parent.ChildCount); - AdjustHeight := (vsExpanded in Parent.States) and (vsVisible in Node.States); - if Parent.ChildCount = 0 then - begin - Parent.States := Parent.States - [vsAllChildrenHidden, vsHasChildren]; - if (Parent <> FRoot) and (vsExpanded in Parent.States) then - Exclude(Parent.States, vsExpanded); - end; - AdjustTotalCount(Parent, -Integer(Node.TotalCount), True); - if AdjustHeight then - AdjustTotalHeight(Parent, -Integer(Node.TotalHeight), True); - if FullyVisible[Node] then - Dec(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); + ImgCheckType := Node.CheckType; + ImgCheckState := GetCheckState(Node); + ImgEnabled := not (vsDisabled in Node.States) and Self.Enabled; - if Assigned(Node.PrevSibling) then - Node.PrevSibling.NextSibling := Node.NextSibling - else - Parent.FirstChild := Node.NextSibling; + IsHot := Node = FCurrentHotNode; + end + else + IsHot := False; - if Assigned(Node.NextSibling) then - begin - Node.NextSibling.PrevSibling := Node.PrevSibling; - // Reindex all following nodes. - if Reindex then - begin - Run := Node.NextSibling; - Index := Node.Index; - while Assigned(Run) do - begin - Run.Index := Index; - Inc(Index); - Run := Run.NextSibling; - end; - end; - end - else - Parent.LastChild := Node.PrevSibling; - end; + if ImgCheckState.IsDisabled then begin // disabled image? + // We need to use disabled images, so map ImgCheckState value from disabled to normal, as disabled state is expressed by ImgEnabled. + ImgEnabled := False; + ImgCheckState := ImgCheckState.GetEnabled(); + end;//if + + if ImgCheckType = ctTriStateCheckBox then + ImgCheckType := ctCheckBox; + if IsHot and (ImgCheckState in [csCheckedNormal, csUncheckedNormal]) and (GetKeyState(VK_LBUTTON) < 0) and (hiOnItemCheckbox in FLastHitInfo.HitPositions) then + Inc(ImgCheckState); // Advance to pressed state + + if ImgCheckType = ctNone then + Result := -1 + else + Result := CheckStateToCheckImage[ImgCheckType, ImgCheckState, ImgEnabled, IsHot]; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InternalRemoveFromSelection(Node: PVirtualNode); +function TBaseVirtualTree.GetColumnClass: TVirtualTreeColumnClass; -// Special version to mark a node to be no longer in the current selection. PackArray must -// be used to remove finally those entries. +begin + Result := TVirtualTreeColumn; +end; -var - Index: Integer; +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetHeaderClass: TVTHeaderClass; begin - // Because pointers are always DWORD aligned we can simply increment all those - // which we want to have removed (see also PackArray) and still have the - // order in the list preserved. - if FindNodeInSelection(Node, Index, -1, -1) then - begin - //sync path note: deselect when overlapping drawselection is made - Exclude(Node.States, vsSelected); - if SyncCheckstateWithSelection[Node] then - Node.CheckState := csUncheckedNormal; // Avoid using SetCheckState() as it handles toSyncCheckboxesWithSelection as well. - Inc(PAnsiChar(FSelection[Index])); - DoRemoveFromSelection(Node); - AdviseChangeEvent(False, Node, crIgnore); - end; + Result := TVTHeader; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InvalidateCache; +function TBaseVirtualTree.GetHintWindowClass: THintWindowClass; -// Marks the cache as invalid. +// Returns the default hint window class used for the tree. Descendants can override it to use their own classes. begin - DoStateChange([tsValidationNeeded], [tsUseCache]); - //ChangeTreeStatesAsync([csValidationNeeded], [csUseCache]); + Result := TVirtualTreeHintWindow; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.MarkCutCopyNodes; +procedure TBaseVirtualTree.GetImageIndex(var Info: TVTPaintInfo; Kind: TVTImageKind; InfoIndex: TVTImageInfoIndex); -// Sets the vsCutOrCopy style in every currently selected but not disabled node to indicate it is -// now part of a clipboard operation. +// Retrieves the image index and an eventual customized image list for drawing. var - Nodes: TNodeArray; - I: Integer; + CustomImages: TCustomImageList; begin - Nodes := nil; - if FSelectionCount > 0 then + with Info do begin - // need the current selection sorted to exclude selected nodes which are children, grandchildren etc. of - // already selected nodes - Nodes := GetSortedSelection(False); - for I := 0 to High(Nodes) do - with Nodes[I]^ do - if not (vsDisabled in States) then - Include(States, vsCutOrCopy); + ImageInfo[InfoIndex].Index := -1; + ImageInfo[InfoIndex].Ghosted := False; + + CustomImages := DoGetImageIndex(Node, Kind, Column, ImageInfo[InfoIndex].Ghosted, ImageInfo[InfoIndex].Index); + if Assigned(CustomImages) then + ImageInfo[InfoIndex].Images := CustomImages end; end; -//---------------------------------------------------------------------------------------------------------------------- +function TBaseVirtualTree.GetImageSize(Node: PVirtualNode; Kind: TVTImageKind = TVTImageKind.ikNormal; Column: TColumnIndex = 0; IncludePadding: Boolean = True): TSize; -procedure TBaseVirtualTree.Loaded; +// Determines whether the given node has got an image of the given kind in the given column. +// Returns the size of the image, or (0,0) if no image is available +// The given node will be implicitly initialized if needed. var - LastRootCount: Cardinal; - IsReadOnly: Boolean; - + Ghosted: Boolean; + Index: TImageIndex; + lImageList: TCustomImageList; begin - inherited; - - // Call RegisterDragDrop after all visual inheritance changes to MiscOptions have been applied. - if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.MiscOptions) then - if HandleAllocated then - RegisterDragDrop(Handle, DragManager as IDropTarget); - - // If a root node count has been set during load of the tree then update its child structure now - // as this hasn't been done yet in this case. - if (tsNeedRootCountUpdate in FStates) and (FRoot.ChildCount > 0) then + if not Assigned(OnGetImageIndexEx) and (((Kind = TVTImageKind.ikNormal) and not Assigned(fImages)) + or ((Kind = TVTImageKind.ikState) and not Assigned(fStateImages))) then begin - DoStateChange([], [tsNeedRootCountUpdate]); - IsReadOnly := toReadOnly in FOptions.MiscOptions; - FOptions.InternalSetMiscOptions(FOptions.MiscOptions - [toReadOnly]); - LastRootCount := FRoot.ChildCount; - FRoot.ChildCount := 0; - BeginUpdate; - SetChildCount(FRoot, LastRootCount); - EndUpdate; - if IsReadOnly then - FOptions.InternalSetMiscOptions(FOptions.MiscOptions + [toReadOnly]); + Result.cx := 0; + Result.cy := 0; end; - - // Prevent the object inspector at design time from marking the header as being modified - // when auto resize is enabled. - Updating; - try - FHeader.UpdateMainColumn; - FHeader.Columns.FixPositions; - if toAutoBidiColumnOrdering in FOptions.AutoOptions then - FHeader.Columns.ReorderColumns(UseRightToLeftAlignment); - // Because of the special recursion and update stopper when creating the window (or resizing it) - // we have to manually trigger the auto size calculation here. - if hsNeedScaling in FHeader.States then - FHeader.RescaleHeader + if not (vsInitialized in Node.States) then + InitNode(Node); + Index := -1; + Ghosted := False; + lImageList := DoGetImageIndex(Node, Kind, Column, Ghosted, Index); + if Index >= 0 then begin + if IncludePadding then + Result.cx := lImageList.Width + ScaledPixels(2) else - FHeader.RecalculateHeader; - if hoAutoResize in FHeader.Options then - FHeader.Columns.AdjustAutoSize(InvalidColumn, True); - finally - Updated; + Result.cx := lImageList.Width; + Result.cy := lImageList.Height; + end + else begin + Result.cx := 0; + Result.cy := 0; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.MainColumnChanged; - +function TBaseVirtualTree.IsEmpty: Boolean; begin - DoCancelEdit; + Result := (Self.ChildCount[nil] = 0); +end; - if Assigned(FAccessibleItem) then - NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetNodeImageSize(Node: PVirtualNode): TSize; + + // Returns the size of an image + // Override if you need different sized images for certain nodes. +begin + Result := GetImageSize(Node); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.MouseMove(Shift: TShiftState; X, Y: Integer); +function TBaseVirtualTree.GetMaxRightExtend(): Cardinal; + +// Determines the maximum with of the currently visible part of the tree, depending on the length +// of the node texts. This method is used for determining the horizontal scroll range if no columns are used. var - R: TRect; + Node, + NextNode: PVirtualNode; + TopPosition: Integer; + CurrentWidth: Integer; begin - if tsNodeHeightTrackPending in FStates then + Node := GetNodeAt(0, 0, True, TopPosition); + Result := 0; + if not Assigned(Node) then + exit; + + while Assigned(Node) do begin - // Remove hint if shown currently. - Application.CancelHint; + if not (vsInitialized in Node.States) then + InitNode(Node); + CurrentWidth := GetOffset(TVTElement.ofsRightOfText, Node); + if Integer(Result) < (CurrentWidth) then + Result := CurrentWidth; + Inc(TopPosition, NodeHeight[Node]); + if TopPosition > Height then + Break; - // Stop wheel panning if active. - StopWheelPanning; + // Get next visible node and update left node position. + NextNode := GetNextVisible(Node, True); + if NextNode = nil then + Break; + Node := NextNode; + end; +end; - // Stop timers - StopTimer(ExpandTimer); - StopTimer(EditTimer); - StopTimer(HeaderTimer); - StopTimer(ScrollTimer); - StopTimer(SearchTimer); - FSearchBuffer := ''; - FLastSearchNode := nil; +//---------------------------------------------------------------------------------------------------------------------- - DoStateChange([tsNodeHeightTracking], [tsScrollPending, tsScrolling, tsEditPending, tsOLEDragPending, tsVCLDragPending, - tsIncrementalSearching, tsNodeHeightTrackPending]); - end; +procedure TBaseVirtualTree.GetNativeClipboardFormats(var Formats: TFormatEtcArray); - if tsDrawSelPending in FStates then - begin - // Remove current selection in case the user clicked somewhere in the window (but not a node) - // and moved the mouse. - if CalculateSelectionRect(X, Y) then - begin - InvalidateRect(Handle, @FNewSelRect, False); - UpdateWindow(Handle); - if (Abs(FNewSelRect.Right - FNewSelRect.Left) > Mouse.DragThreshold) or - (Abs(FNewSelRect.Bottom - FNewSelRect.Top) > Mouse.DragThreshold) then - begin - if tsClearPending in FStates then - begin - DoStateChange([], [tsClearPending]); - ClearSelection; - end; - DoStateChange([tsDrawSelecting], [tsDrawSelPending]); +// Returns the supported clipboard formats of the tree. - // Reset to main column for multiselection. - FocusedColumn := FHeader.MainColumn; +begin + TClipboardFormatList.EnumerateFormats(TVirtualTreeClass(ClassType), Formats, FClipboardFormats); + // Ask application/descendants for self defined formats. + DoGetUserClipboardFormats(Formats); +end; - // The current rectangle may already include some node captions. Handle this. - if HandleDrawSelection(X, Y) then - InvalidateRect(Handle, nil, False); - end; - end; - end - else - begin - if tsNodeHeightTracking in FStates then - begin - // Handle height tracking. - if DoNodeHeightTracking(FHeightTrackNode, FHeightTrackColumn, FHeader.GetShiftState, - FHeightTrackPoint, Point(X, Y)) then - begin - // Avoid negative (or zero) node heights. - if FHeightTrackPoint.Y >= Y then - Y := FHeightTrackPoint.Y + 1; - SetNodeHeight(FHeightTrackNode, Y - FHeightTrackPoint.Y); - UpdateWindow(Handle); - Exit; - end; - end; +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetOperationCanceled; + +begin + Result := FOperationCanceled and (FOperationCount > 0); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetOptionsClass: TTreeOptionsClass; + +begin + Result := TCustomVirtualTreeOptions; +end; - // If both wheel panning and auto scrolling are pending then the user moved the mouse while holding down the - // middle mouse button. This means panning is being used, hence remove the wheel scroll flag. - if [tsWheelPanning, tsWheelScrolling] * FStates = [tsWheelPanning, tsWheelScrolling] then - begin - if ((Abs(FLastClickPos.X - X) >= Mouse.DragThreshold) or (Abs(FLastClickPos.Y - Y) >= Mouse.DragThreshold)) then - DoStateChange([], [tsWheelScrolling]); - end; +//---------------------------------------------------------------------------------------------------------------------- - // Really start dragging if the mouse has been moved more than the threshold. - if (tsOLEDragPending in FStates) and - ( - ((Abs(FLastClickPos.X - X) >= FDragThreshold) and (X > 0)) or // Check >0 to fix issue #833 - ((Abs(FLastClickPos.Y - Y) >= FDragThreshold) and (Y > 0)) - ) - then - DoDragging(FLastClickPos) - else +function TBaseVirtualTree.GetTreeFromDataObject(const DataObject: IDataObject): TBaseVirtualTree; + +// Returns the owner/sender of the given data object by means of a special clipboard format +// or nil if the sender is in another process or no virtual tree at all. + +var + Medium: TStgMedium; + Data: PVTReference; + +begin + Result := nil; + if Assigned(DataObject) then + begin + StandardOLEFormat.cfFormat := CF_VTREFERENCE; + if DataObject.GetData(StandardOLEFormat, Medium) = S_OK then begin - if CanAutoScroll then - DoAutoScroll(X, Y); - if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then - AdjustPanningCursor(X, Y); - if not IsMouseSelecting then - begin - HandleHotTrack(X, Y); - inherited MouseMove(Shift, X, Y); - end - else + Data := GlobalLock(Medium.hGlobal); + if Assigned(Data) then begin - // Handle draw selection if required, but don't do the work twice if the - // auto scrolling code already cares about the selection. - if not (tsScrolling in FStates) and CalculateSelectionRect(X, Y) then - begin - // If something in the selection changed then invalidate the entire - // tree instead trying to figure out the display rects of all changed nodes. - if HandleDrawSelection(X, Y) then - InvalidateRect(Handle, nil, False) - else - begin - UnionRect(R, OrderRect(FNewSelRect), OrderRect(FLastSelRect)); - OffsetRect(R, -FEffectiveOffsetX, FOffsetY); - InvalidateRect(Handle, @R, False); - end; - UpdateWindow(Handle); - end; + if Data.Process = GetCurrentProcessID then + Result := Data.Tree; + GlobalUnlock(Medium.hGlobal); end; + ReleaseStgMedium(Medium); end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.Notification(AComponent: TComponent; Operation: TOperation); +procedure TBaseVirtualTree.HandleHotTrack(X, Y: Integer); +// Updates the current "hot" node. + +var + HitInfo: THitInfo; + CheckPositions: THitPositions; + ButtonIsHit, + DoInvalidate: Boolean; + oldHotNode : PVirtualNode; begin - if (AComponent <> Self) and (Operation = opRemove) then + if not IsMouseCursorVisible then begin - // Check for components linked to the tree. - if AComponent = FImages then + if Assigned(FCurrentHotNode) then begin - Images := nil; - if not (csDestroying in ComponentState) then - Invalidate; - end - else - if AComponent = FStateImages then - begin - StateImages := nil; - if not (csDestroying in ComponentState) then - Invalidate; - end - else - if AComponent = FCustomCheckImages then - begin - CustomCheckImages := nil; - FCheckImageKind := ckSystemDefault; - if not (csDestroying in ComponentState) then - Invalidate; - end - else - if AComponent = PopupMenu then - PopupMenu := nil - else - // Check for components linked to the header. - if Assigned(FHeader) then - begin - if AComponent = FHeader.Images then - FHeader.Images := nil - else - if AComponent = FHeader.PopupMenu then - FHeader.PopupMenu := nil; - end; + InvalidateNode(FCurrentHotNode); + FCurrentHotNode := nil; + end; + Exit; + end;//if not IsMouseCursorVisible + + DoInvalidate := False; + oldHotNode := FCurrentHotNode; + // Get information about the hit. + GetHitTestInfoAt(X, Y, True, HitInfo); + + // Only make the new node being "hot" if its label is hit or full row selection is enabled. + CheckPositions := [hiOnItemLabel, hiOnItemCheckbox]; + + // If running under Windows Vista using the explorer theme hitting the buttons makes the node hot, too. + if tsUseExplorerTheme in FStates then + Include(CheckPositions, hiOnItemButtonExact); + + if (CheckPositions * HitInfo.HitPositions = []) and + (not (toFullRowSelect in FOptions.SelectionOptions) or (hiNowhere in HitInfo.HitPositions)) then + FCurrentHotNode := nil + else + FCurrentHotNode := HitInfo.HitNode; + if (FCurrentHotNode <> oldHotNode) or (HitInfo.HitColumn <> FCurrentHotColumn) then + begin + DoInvalidate := (toHotTrack in FOptions.PaintOptions) or (toCheckSupport in FOptions.MiscOptions) or (oldHotNode <> FCurrentHotNode); + DoHotChange(oldHotNode, HitInfo.HitNode); + if Assigned(oldHotNode) and DoInvalidate then + InvalidateNode(oldHotNode); + FCurrentHotColumn := HitInfo.HitColumn; end; - inherited; + + ButtonIsHit := (hiOnItemButtonExact in HitInfo.HitPositions); + if Assigned(HitInfo.HitNode) and ((FHotNodeButtonHit <> ButtonIsHit) or (FCurrentHotNode <> oldHotNode) or DoInvalidate) then + begin + FHotNodeButtonHit := ButtonIsHit; + InvalidateNode(HitInfo.HitNode); + end + else + if not Assigned(HitInfo.HitNode) then + FHotNodeButtonHit := False; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.OriginalWMNCPaint(DC: HDC); - -// Unfortunately, the painting for the non-client area in TControl is not always correct and does also not consider -// existing clipping regions, so it has been modified here to take this into account. - -const - InnerStyles: array[TBevelCut] of Integer = (0, BDR_SUNKENINNER, BDR_RAISEDINNER, 0); - OuterStyles: array[TBevelCut] of Integer = (0, BDR_SUNKENOUTER, BDR_RAISEDOUTER, 0); - EdgeStyles: array[TBevelKind] of Integer = (0, 0, BF_SOFT, BF_FLAT); - Ctl3DStyles: array[Boolean] of Integer = (BF_MONO, 0); +procedure TBaseVirtualTree.HandleIncrementalSearch(CharCode: Word); var - RC, RW: TRect; - EdgeSize: Integer; - Size: TSize; + Run, Stop: PVirtualNode; + GetNextNode: TGetNextNodeProc; + NewSearchText: string; + SingleLetter, + PreviousSearch: Boolean; // True if VK_BACK was sent. + SearchDirection: TVTSearchDirection; -begin - if (BevelKind <> bkNone) or (BorderWidth > 0) then - begin - RC := Rect(0, 0, Width, Height); - Size := GetBorderDimensions; - InflateRect(RC, Size.cx, Size.cy); + //--------------- local functions ------------------------------------------- - RW := RC; + procedure SetupNavigation; - if BevelKind <> bkNone then - begin - DrawEdge(DC, RC, InnerStyles[BevelInner] or OuterStyles[BevelOuter], Byte(BevelEdges) or EdgeStyles[BevelKind] or - Ctl3DStyles[Ctl3D]); + // If the search buffer is empty then we start searching with the next node after the last one, otherwise + // we continue with the last one. Node navigation function is set up too here, to avoid frequent checks. - EdgeSize := 0; - if BevelInner <> bvNone then - Inc(EdgeSize, BevelWidth); - if BevelOuter <> bvNone then - Inc(EdgeSize, BevelWidth); - with TWithSafeRect(RC) do + var + FindNextNode: Boolean; + + begin + FindNextNode := (Length(FSearchBuffer) = 0) or (Run = nil) or SingleLetter or PreviousSearch; + case FIncrementalSearch of + isVisibleOnly: + if SearchDirection = sdForward then + begin + GetNextNode := GetNextVisible; + if FindNextNode then + begin + if Run = nil then + Run := GetFirstVisible(nil, True) + else + begin + Run := GetNextVisible(Run, True); + // Do wrap around. + if Run = nil then + Run := GetFirstVisible(nil, True); + end; + end; + end + else + begin + GetNextNode := GetPreviousVisible; + if FindNextNode then + begin + if Run = nil then + Run := GetLastVisible(nil, True) + else + begin + Run := GetPreviousVisible(Run, True); + // Do wrap around. + if Run = nil then + Run := GetLastVisible(nil, True); + end; + end; + end; + isInitializedOnly: + if SearchDirection = sdForward then + begin + GetNextNode := GetNextNoInit; + if FindNextNode then + begin + if Run = nil then + Run := GetFirstNoInit + else + begin + Run := GetNextNoInit(Run); + // Do wrap around. + if Run = nil then + Run := GetFirstNoInit; + end; + end; + end + else + begin + GetNextNode := GetPreviousNoInit; + if FindNextNode then + begin + if Run = nil then + Run := GetLastNoInit + else + begin + Run := GetPreviousNoInit(Run); + // Do wrap around. + if Run = nil then + Run := GetLastNoInit; + end; + end; + end; + else + // isAll + if SearchDirection = sdForward then begin - if beLeft in BevelEdges then - Inc(Left, EdgeSize); - if beTop in BevelEdges then - Inc(Top, EdgeSize); - if beRight in BevelEdges then - Dec(Right, EdgeSize); - if beBottom in BevelEdges then - Dec(Bottom, EdgeSize); + GetNextNode := GetNext; + if FindNextNode then + begin + if Run = nil then + Run := GetFirst + else + begin + Run := GetNext(Run); + // Do wrap around. + if Run = nil then + Run := GetFirst; + end; + end; + end + else + begin + GetNextNode := GetPrevious; + if FindNextNode then + begin + if Run = nil then + Run := GetLast + else + begin + Run := GetPrevious(Run); + // Do wrap around. + if Run = nil then + Run := GetLast; + end; + end; end; end; - - // Repaint only the part in the original clipping region and not yet drawn parts. - IntersectClipRect(DC, RC.Left, RC.Top, RC.Right, RC.Bottom); - - // Determine inner rectangle to exclude (RC corresponds then to the client area). - InflateRect(RC, -Integer(BorderWidth), -Integer(BorderWidth)); - - // Remove the inner rectangle. - ExcludeClipRect(DC, RC.Left, RC.Top, RC.Right, RC.Bottom); - - // Erase parts not drawn. - Brush.Color := FColors.BorderColor; - Winapi.Windows.FillRect(DC, RW, Brush.Handle); end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.Paint; -// Window paint routine. Used when the tree window needs to be updated. + //--------------------------------------------------------------------------- -var - Window: TRect; - Target: TPoint; - Temp: Integer; - Options: TVTInternalPaintOptions; - RTLOffset: Integer; + function CodePageFromLocale(Language: LCID): Integer; -begin + // Determines the code page for a given locale. + // Unfortunately there is no easier way than this, currently. - Options := [poBackground, poColumnColor, poDrawFocusRect, poDrawDropMark, poDrawSelection, poGridLines]; - if UseRightToLeftAlignment and FHeader.UseColumns then - RTLOffset := ComputeRTLOffset(True) - else - RTLOffset := 0; + var + Buf: array[0..6] of Char; - // The update rect has already been filled in WMPaint, as it is the window's update rect, which gets - // reset when BeginPaint is called (in the ancestor). - // The difference to the DC's clipbox is that it is also valid with internal paint operations used - // e.g. by the Explorer while dragging, but show window content while dragging is disabled. - if not IsRectEmpty(FUpdateRect) then begin - Temp := Header.Columns.GetVisibleFixedWidth; - if Temp = 0 then - begin - Window := FUpdateRect; - Target := Window.TopLeft; - - // The clipping rectangle is given in client coordinates of the window. We have to convert it into - // a sliding window of the tree image. - OffsetRect(Window, FEffectiveOffsetX - RTLOffset, -FOffsetY); - PaintTree(Canvas, Window, Target, Options); - end - else - begin - // First part, fixed columns - Window := ClientRect; - Window.Right := Temp; - Target := Window.TopLeft; - - OffsetRect(Window, -RTLOffset, -FOffsetY); - PaintTree(Canvas, Window, Target, Options); - - // Second part, other columns - Window := GetClientRect; - - if Temp > Window.Right then - Exit; + GetLocaleInfo(Language, LOCALE_IDEFAULTANSICODEPAGE, Buf, 6); + Result := StrToIntDef(Buf, GetACP); + end; - Window.Left := Temp; - Target := Window.TopLeft; + //--------------------------------------------------------------------------- - OffsetRect(Window, FEffectiveOffsetX - RTLOffset, -FOffsetY); - PaintTree(Canvas, Window, Target, Options); - end; + function KeyUnicode(C: Char): WideChar; + // Converts the given character into its corresponding Unicode character + // depending on the active keyboard layout. + begin + Result := C; //!!!!!! end; -end; - -//---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas; const ImageInfo: TVTImageInfo; Selected: Boolean); + //--------------- end local functions --------------------------------------- var - ForegroundColor: COLORREF; - R: TRect; - Details, lSizeDetails: TThemedElementDetails; - lSize: TSize; - Theme: HTHEME; + FoundMatch: Boolean; + NewChar: WideChar; + begin - with ImageInfo do + StopTimer(SearchTimer); + + if FIncrementalSearch <> isNone then begin - if (tsUseThemes in FStates) and (FCheckImageKind = ckSystemDefault) then + if CharCode <> 0 then begin - Details.Element := teButton; - case Index of - // ctRadioButton - 1 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedNormal); - 2 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedHot); - 3 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedPressed); - 4 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedDisabled); - 5 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedNormal); - 6 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedHot); - 7 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedPressed); - 8 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedDisabled); - // ct(TriState)CheckBox - 9 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal); - 10 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedHot); - 11 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedPressed); - 12 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedDisabled); - 13 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedNormal); - 14 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedHot); - 15 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedPressed); - 16 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedDisabled); - 17 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedNormal); - 18 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedHot); - 19 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedPressed); - 20 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedDisabled); - // ctButton - ckButtonNormal: Details := StyleServices.GetElementDetails(tbPushButtonNormal); - ckButtonHot: Details := StyleServices.GetElementDetails(tbPushButtonHot); - ckButtonPressed: Details := StyleServices.GetElementDetails(tbPushButtonPressed); - ckButtonDisabled: Details := StyleServices.GetElementDetails(tbPushButtonDisabled); - else - Details := StyleServices.GetElementDetails(tbButtonRoot); - end; - if StyleServices.IsSystemStyle {and not (Index in [ckButtonNormal..ckButtonDisabled])} then + DoStateChange([tsIncrementalSearching]); + + // Convert the given virtual key code into a Unicode character based on the current locale. + NewChar := KeyUnicode(Char(CharCode)); + PreviousSearch := NewChar = WideChar(VK_BACK); + // We cannot do a search with an empty search buffer. + if not PreviousSearch or (FSearchBuffer <> '') then begin - Theme := OpenThemeData(Handle, 'BUTTON'); - GetThemePartSize(Theme, Canvas.Handle, Details.Part, Details.State, nil, TS_TRUE, lSize); - if (Index in [ckButtonNormal..ckButtonDisabled]) then begin - lSizeDetails := StyleServices.GetElementDetails(tbCheckBoxCheckedNormal); // Size of dropdown button should be based on size of checkboxes - GetThemePartSize(Theme, Canvas.Handle, lSizeDetails.Part, lSizeDetails.State, nil, TS_TRUE, lSize); - // dropdown buttons should be slightly larger than checkboxes, see issue #887 - lSize.cx := Round(lSize.cx * 1.15); - lSize.cy := Round(lSize.cy * 1.1); + // Determine which method to use to advance nodes and the start node to search from. + case FSearchStart of + ssAlwaysStartOver: + Run := nil; + ssFocusedNode: + Run := FFocusedNode; + else // ssLastHit + Run := FLastSearchNode; end; - R := Rect(XPos, YPos, XPos + lSize.cx, YPos + lSize.cy); - if (Index in [ckButtonNormal..ckButtonDisabled]) then - R.Offset(-1, 0); // Eliminate 1 pixel border around Windows themed button - DrawThemeBackground(Theme, Canvas.Handle, Details.Part, Details.State, R, nil); - CloseThemeData(Theme); - end - else - begin - if (Index in [ckButtonNormal..ckButtonDisabled]) or not StyleServices.GetElementSize(Canvas.Handle, Details, TElementSize.esActual, lSize{$IF CompilerVersion >= 34}, CurrentPPI{$IFEND}) then begin - // radio buttons fail in RAD Studio 10 Seattle and lower, fallback to checkbox images. See issue #615 - if not StyleServices.GetElementSize(Canvas.Handle, StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal), TElementSize.esActual, lSize{$IF CompilerVersion >= 34}, CurrentPPI{$IFEND}) then - lSize := TSize.Create(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK)); - end;//if - R := Rect(XPos, YPos, XPos + lSize.cx, YPos + lSize.cy); - StyleServices.DrawElement(Canvas.Handle, Details, R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); - Canvas.Refresh; // Every time you give a Canvas.Handle away to some other code you can't control you have to call Canvas.Refresh afterwards because the Canvas object and the HDC can be out of sync. - end; - if (Index in [ckButtonNormal..ckButtonDisabled]) then begin - Canvas.Pen.Color := clGray; - // These constants have been determined by test using various themes and dpi-scalings - DrawArrow(Canvas, TScrollDirection.sdDown, Point(R.Left + Round(lSize.cx * 0.22), R.Top + Round(lSize.cy * 0.33)), Round(lSize.cx *0.28)); - end;//if - end - else - with FCheckImages do - begin - if Selected and not Ghosted then + + // Make sure the start node corresponds to the search criterion. + if Assigned(Run) then begin - if Focused or (toPopupMode in FOptions.PaintOptions) then - ForegroundColor := ColorToRGB(FColors.FocusedSelectionColor) + case FIncrementalSearch of + isInitializedOnly: + if not (vsInitialized in Run.States) then + Run := nil; + isVisibleOnly: + if not FullyVisible[Run] or IsEffectivelyFiltered[Run] then + Run := nil; + end; + end; + Stop := Run; + + // VK_BACK temporarily changes search direction to opposite mode. + if PreviousSearch then + begin + if SearchDirection = sdBackward then + SearchDirection := sdForward else - ForegroundColor := ColorToRGB(FColors.UnfocusedSelectionColor); + SearchDirection := sdBackward; end else - ForegroundColor := GetRGBColor(BlendColor); + SearchDirection := FSearchDirection; + // The "single letter mode" is used to advance quickly from node to node when pressing the same key several times. + SingleLetter := (Length(FSearchBuffer) = 1) and not PreviousSearch and (FSearchBuffer[1] = NewChar); + // However if the current hit (if there is one) would fit also with a repeated character then + // don't use single letter mode. + if SingleLetter and (DoIncrementalSearch(Run, FSearchBuffer + NewChar) = 0) then + SingleLetter := False; + SetupNavigation; + FoundMatch := False; - ImageList_DrawEx(Handle, Index, Canvas.Handle, XPos, YPos, 0, 0, GetRGBColor(BkColor), ForegroundColor, - ILD_TRANSPARENT); + if Assigned(Run) then + begin + if SingleLetter then + NewSearchText := FSearchBuffer + else + if PreviousSearch then + begin + SetLength(FSearchBuffer, Length(FSearchBuffer) - 1); + NewSearchText := FSearchBuffer; + end + else + NewSearchText := FSearchBuffer + NewChar; + + repeat + if DoIncrementalSearch(Run, NewSearchText) = 0 then + begin + FoundMatch := True; + Break; + end; + + // Advance to next node if we have not found a match. + Run := GetNextNode(Run); + // Do wrap around start or end of tree. + if (Run <> Stop) and (Run = nil) then + SetupNavigation; + until Run = Stop; + end; + + if FoundMatch then + begin + ClearSelection; + FSearchBuffer := NewSearchText; + FLastSearchNode := Run; + FocusedNode := Run; + Selected[Run] := True; + FLastSearchNode := Run; + end + else + // Play an acoustic signal if nothing could be found but don't beep if only the currently + // focused node matches. + if Assigned(Run) and (DoIncrementalSearch(Run, NewSearchText) <> 0) then + Beep; end; + end; + + // Restart search timeout interval. + SetTimer(Handle, SearchTimer, FSearchTimeout, nil); end; end; //---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.HandleMouseDblClick(var Message: TWMMouse; const HitInfo: THitInfo); -procedure TBaseVirtualTree.PaintImage(var PaintInfo: TVTPaintInfo; ImageInfoIndex: TVTImageInfoIndex; DoOverlay: Boolean); -const - Style: array[TImageType] of Cardinal = (0, ILD_MASK); var - ExtraStyle: Cardinal; - CutNode: Boolean; - PaintFocused: Boolean; - DrawEnabled: Boolean; - CustomOverlayDrawing: Boolean; // False if the built-in overloay drawing of TImageList should be used, True if custom drawing should take place. + Node: PVirtualNode; + MayEdit: Boolean; + begin - with PaintInfo do + MayEdit := not (tsEditing in FStates) and (toEditOnDblClick in FOptions.MiscOptions); + if tsEditPending in FStates then begin - CutNode := (vsCutOrCopy in Node.States) and (tsCutPending in FStates); - PaintFocused := Focused or (toGhostedIfUnfocused in FOptions.PaintOptions); + StopTimer(EditTimer); + DoStateChange([], [tsEditPending]); + end; - // Since the overlay image must be specified together with the image to draw - // it is meaningfull to retrieve it in advance. - if DoOverlay then - GetImageIndex(PaintInfo, ikOverlay, iiOverlay) - else - PaintInfo.ImageInfo[iiOverlay].Index := -1; + if not (tsEditing in FStates) or DoEndEdit then + begin + if HitInfo.HitColumn = FHeader.Columns.ClickIndex then + DoColumnDblClick(HitInfo.HitColumn, KeysToShiftState(Message.Keys)); - DrawEnabled := not (vsDisabled in Node.States) and Enabled; - with ImageInfo[ImageInfoIndex] do + if HitInfo.HitNode <> nil then + DoNodeDblClick(HitInfo); + + Node := nil; + if (hiOnItem in HitInfo.HitPositions) and (HitInfo.HitColumn > NoColumn) and + (coFixed in FHeader.Columns[HitInfo.HitColumn].Options) then begin - if (vsSelected in Node.States) and not(Ghosted or CutNode) then - begin - if PaintFocused or (toPopupMode in FOptions.PaintOptions) then - Images.BlendColor := FColors.FocusedSelectionColor - else - Images.BlendColor := FColors.UnfocusedSelectionColor; - end + if hiUpperSplitter in HitInfo.HitPositions then + Node := GetPreviousVisible(HitInfo.HitNode, True) else - Images.BlendColor := Color; + if hiLowerSplitter in HitInfo.HitPositions then + Node := HitInfo.HitNode; + end; - ExtraStyle := ILD_TRANSPARENT; - // If the user returned an index >= 15 then we cannot use the built-in overlay image drawing. - // Instead we do it manually. Also of the image list of the normal and the overlay icon is different, - // we can't use the built-in drawing. See issue #779. - if (ImageInfo[iiOverlay].Index > -1) then begin - CustomOverlayDrawing := (ImageInfo[iiOverlay].Index >= 15) or (ImageInfo[iiOverlay].Images <> ImageInfo[iiNormal].Images); - if not CustomOverlayDrawing then - ExtraStyle := ILD_TRANSPARENT or ILD_OVERLAYMASK and IndexToOverlayMask(ImageInfo[iiOverlay].Index + 1); - end + if Assigned(Node) and (Node <> FRoot) and (toNodeHeightDblClickResize in FOptions.MiscOptions) then + begin + if DoNodeHeightDblClickResize(Node, HitInfo.HitColumn, KeysToShiftState(Message.Keys), Point(Message.XPos, Message.YPos)) then + begin + SetNodeHeight(Node, FDefaultNodeHeight); + UpdateWindow(Handle); + MayEdit := False; + end; + end + else + if hiOnItemCheckBox in HitInfo.HitPositions then + begin + HandleCheckboxClick(HitInfo.HitNode, Message.Keys); + MayEdit := False; + end// if hiOnItemCheckBox else - CustomOverlayDrawing := False; - - // Blend image if enabled and the tree has the focus (or ghosted images must be drawn also if unfocused) ... - if (toUseBlendedImages in FOptions.PaintOptions) and PaintFocused - // ... and the image is ghosted... - and (Ghosted or - // ... or it is not the check image and the node is selected (but selection is not for the entire row)... - ((vsSelected in Node.States) and - not (toFullRowSelect in FOptions.SelectionOptions) and - not (toGridExtensions in FOptions.MiscOptions)) or - // ... or the node must be shown in cut mode. - CutNode) then - ExtraStyle := ExtraStyle or ILD_BLEND50; - - if (vsSelected in Node.States) and not Ghosted then - Images.BlendColor := clDefault; - - DrawImage(Images, Index, Canvas, XPos, YPos, Style[Images.ImageType] or ExtraStyle, DrawEnabled); + begin + if hiOnItemButton in HitInfo.HitPositions then + begin + ToggleNode(HitInfo.HitNode); + MayEdit := False; + end + else + begin + if toToggleOnDblClick in FOptions.MiscOptions then + begin + if ((([hiOnItemButton, hiOnItemLabel, hiOnNormalIcon, hiOnStateIcon] * HitInfo.HitPositions) <> []) or + ((toFullRowSelect in FOptions.SelectionOptions) and Assigned(HitInfo.HitNode))) then + begin + ToggleNode(HitInfo.HitNode); + MayEdit := False; + end; + end; + end; + end; + end; - // Now, draw the overlay. This circumnavigates limitations in the overlay mask index (it has to be 4 bits in size, - // anything larger will be truncated by the ILD_OVERLAYMASK). - // However this will only be done if the overlay image index is > 15, to avoid breaking code that relies - // on overlay image indices (e.g. when using system image lists). - if CustomOverlayDrawing then begin - ExtraStyle := ExtraStyle and not ILD_BLEND50; // Fixes issue #551 - // Note: XPos and YPos are those of the normal images. - DrawImage(ImageInfo[iiOverlay].Images, ImageInfo[iiOverlay].Index, Canvas, XPos, YPos, - Style[ImageInfo[iiOverlay].Images.ImageType] or ExtraStyle, DrawEnabled); - end;//if - end; + if MayEdit and Assigned(FFocusedNode) and (FFocusedNode = HitInfo.HitNode) and + (FFocusedColumn = HitInfo.HitColumn) and CanEdit(FFocusedNode, HitInfo.HitColumn) then + begin + DoStateChange([tsEditPending]); + FEditColumn := FFocusedColumn; + SetTimer(Handle, EditTimer, 0, nil); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.PaintNodeButton(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; const R: TRect; - ButtonX, ButtonY: Integer; BidiMode: TBiDiMode); - +procedure TBaseVirtualTree.HandleCheckboxClick(pHitNode: PVirtualNode; pKeys: LongInt); var - Bitmap: TBitmap; - XPos: Integer; - IsHot: Boolean; - IsSelected : boolean; - Theme: HTHEME; - Glyph: Integer; - State: Integer; - Pos: TRect; - + NewCheckState: TCheckState; begin - IsHot := (FCurrentHotNode = Node) and FHotNodeButtonHit; - IsSelected := (vsSelected in Node.States); - - // Draw the node's plus/minus button according to the directionality. - if BidiMode = bdLeftToRight then - XPos := R.Left + ButtonX - else - XPos := R.Right - ButtonX - FPlusBM.Width; - - if (tsUseExplorerTheme in FStates) and not VclStyleEnabled then - begin - Glyph := IfThen(IsHot, TVP_HOTGLYPH, TVP_GLYPH); - State := IfThen(vsExpanded in Node.States, GLPS_OPENED, GLPS_CLOSED); - Pos := Rect(XPos, R.Top + ButtonY, XPos + FPlusBM.Width, R.Top + ButtonY + FPlusBM.Height); - Theme := OpenThemeData(Handle, 'TREEVIEW'); - DrawThemeBackground(Theme, Canvas.Handle, Glyph, State, Pos, nil); - CloseThemeData(Theme); - end - else - begin - if vsExpanded in Node.States then - begin - if IsHot then - begin - if IsSelected then - BitMap := FSelectedHotMinusBM - else - Bitmap := FHotMinusBM; - end - else - Bitmap := FMinusBM; - end - else + NewCheckState := DetermineNextCheckState(pHitNode.CheckType, pHitNode.CheckState); + if (ssLeft in KeysToShiftState(pKeys)) and DoChecking(pHitNode, NewCheckState) then begin - if IsHot then - begin - if IsSelected then - BitMap := FSelectedHotPlusBM - else - Bitmap := FHotPlusBM; - end + if (Self.SelectedCount > 1) and (Selected[pHitNode]) and not (toSyncCheckboxesWithSelection in TreeOptions.SelectionOptions) then + SetCheckStateForAll(NewCheckState, True) else - Bitmap := FPlusBM; - end; - // Need to draw this masked. - Canvas.Draw(XPos, R.Top + ButtonY, Bitmap); - end; + DoCheckClick(pHitNode, NewCheckState); + end;//if ssLeft end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.PaintTreeLines(const PaintInfo: TVTPaintInfo; IndentSize: Integer; const LineImage: TLineImage); +procedure TBaseVirtualTree.HandleMouseDown(var Message: TWMMouse; var HitInfo: THitInfo); + +// centralized mouse button down handling var - I: Integer; - XPos, - Offset: Integer; - NewStyles: TLineImage; + LastFocused: PVirtualNode; + Column: TColumnIndex; + ShiftState: TShiftState; -begin - NewStyles := nil; + // helper variables to shorten boolean equations/expressions + AutoDrag, // automatic (or allowed) drag start + IsLabelHit, // the node's caption or images are hit + IsCellHit, // for grid extension or full row select (but not check box, button) + IsAnyHit, // either IsHit or IsCellHit + IsHeightTracking, // height tracking + MultiSelect, // multiselection is enabled + ShiftEmpty, // ShiftState = [] + NodeSelected: Boolean; // the new node (if any) is selected + NewColumn: Boolean; // column changed + NewNode: Boolean; // Node changed. + NeedChangeEvent: Boolean; // change event is required for selection change + CanClear: Boolean; + AltPressed: Boolean; // Pressing the Alt key enables special processing for selection. + FullRowDrag: Boolean; // Start dragging anywhere within a node's bound. + NodeRect: TRect; - with PaintInfo do + //--------------- local functions ------------------------------------------- + + //Fix for issue: 310 whenever there is a need to invalidate a column, consider + //auto spanned columns if applicable + procedure invalidateWithAutoSpan(acolumn: TColumnIndex; anode: PVirtualNode); + var + NextColumn: Integer; + Dummy: TColumnIndex; begin - if BidiMode = bdLeftToRight then + if (not FHeader.UseColumns) or (not (toAutoSpanColumns in FOptions.AutoOptions)) + or (acolumn = FHeader.MainColumn) then begin - XPos := CellRect.Left + PaintInfo.Offsets[ofsMargin]; - Offset := FIndent; - end - else + //no need to find auto spanned next columns + InvalidateColumn(acolumn); + exit; + end; + //invalidate auto spanned columns too + with FHeader.Columns do //standard loop for auto span begin - Offset := -Integer(FIndent); - XPos := CellRect.Right - PaintInfo.Offsets[ofsMargin] + Offset; + NextColumn := acolumn; + repeat + InvalidateColumn(NextColumn); + Dummy := GetNextVisibleColumn(NextColumn); + if (Dummy = InvalidColumn) or + not ColumnIsEmpty(anode, Dummy) + or + (Items[Dummy].BidiMode <> bdLeftToRight) then + Break; + NextColumn := Dummy; + until False; end; + end; - case FLineMode of - lmBands: - if poGridLines in PaintInfo.PaintOptions then - begin - // Convert the line images in correct bands. - SetLength(NewStyles, Length(LineImage)); - for I := IndentSize - 1 downto 0 do - begin - if (vsExpanded in Node.States) and not (vsAllChildrenHidden in Node.States) then - NewStyles[I] := ltLeft - else - case LineImage[I] of - ltRight, - ltBottomRight, - ltTopDownRight, - ltTopRight: - NewStyles[I] := ltLeftBottom; - ltNone: - // Have to take over the image to the right of this one. A no line entry can never appear as - // last entry so I don't need an end check here. - if LineImage[I + 1] in [ltNone, ltTopRight] then - NewStyles[I] := NewStyles[I + 1] - else - NewStyles[I] := ltLeft; - ltTopDown: - // Have to check the image to the right of this one. A top down line can never appear as - // last entry so I don't need an end check here. - if LineImage[I + 1] in [ltNone, ltTopRight] then - NewStyles[I] := NewStyles[I + 1] - else - NewStyles[I] := ltLeft; - end; - end; + //--------------- end local functions --------------------------------------- - PaintInfo.Canvas.Font.Color := FColors.GridLineColor; - for I := 0 to IndentSize - 1 do - begin - DoBeforeDrawLineImage(PaintInfo.Node, I + Ord(not (toShowRoot in TreeOptions.PaintOptions)), XPos); - DrawLineImage(PaintInfo, XPos, CellRect.Top, NodeHeight[Node] - 1, VAlign - 1, NewStyles[I], - BidiMode <> bdLeftToRight); - Inc(XPos, Offset); - end; - end; - else // lmNormal - PaintInfo.Canvas.Font.Color := FColors.TreeLineColor; - for I := 0 to IndentSize - 1 do - begin - DoBeforeDrawLineImage(PaintInfo.Node, I + Ord(not (toShowRoot in TreeOptions.PaintOptions)), XPos); - DrawLineImage(PaintInfo, XPos, CellRect.Top, NodeHeight[Node], VAlign - 1, LineImage[I], - BidiMode <> bdLeftToRight); - Inc(XPos, Offset); - end; - end; +begin + if IsEmpty then + Exit; // Nothing to do + if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then + begin + StopWheelPanning; + Exit; end; -end; -//---------------------------------------------------------------------------------------------------------------------- + if tsEditPending in FStates then + begin + StopTimer(EditTimer); + DoStateChange([], [tsEditPending]); + end; -procedure TBaseVirtualTree.PaintSelectionRectangle(Target: TCanvas; WindowOrgX: Integer; const SelectionRect: TRect; - TargetRect: TRect); + FLastHitInfo := HitInfo; // Save for later use in OnNodeClick event, see issue #692 + if (tsEditing in FStates) then begin + if not DoEndEdit then + exit; + // Repeat the hit test as an OnEdited event might got triggered that could modify the tree. + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + end;//if tsEditing -// Helper routine to draw a selection rectangle in the mode determined by DrawSelectionMode. + // Focus change. Don't use the SetFocus method as this does not work for MDI Winapi.Windows. + if not Focused and CanFocus then + begin + Winapi.Windows.SetFocus(Handle); + // Repeat the hit test as an OnExit event might got triggered that could modify the tree. + GetHitTestInfoAt(Message.XPos, Message.YPos, True, HitInfo); + end; -var - BlendRect: TRect; - TextColorBackup, - BackColorBackup: COLORREF; // used to restore forground and background colors when drawing a selection rectangle + // Keep clicked column in case the application needs it. + FHeader.Columns.ClickIndex := HitInfo.HitColumn; -begin - if ((FDrawSelectionMode = smDottedRectangle) and not (tsUseThemes in FStates)) then + // Change column only if we have hit the node label. + if (hiOnItemLabel in HitInfo.HitPositions) or + (toFullRowSelect in FOptions.SelectionOptions) or + (toGridExtensions in FOptions.MiscOptions) then begin - // Classical selection rectangle using dotted borderlines. - TextColorBackup := GetTextColor(Target.Handle); - SetTextColor(Target.Handle, $FFFFFF); - BackColorBackup := GetBkColor(Target.Handle); - SetBkColor(Target.Handle, 0); - Target.DrawFocusRect(SelectionRect); - SetTextColor(Target.Handle, TextColorBackup); - SetBkColor(Target.Handle, BackColorBackup); + NewColumn := FFocusedColumn <> HitInfo.HitColumn; + if toExtendedFocus in FOptions.SelectionOptions then + Column := HitInfo.HitColumn + else + Column := FHeader.MainColumn; end else begin - // Modern alpha blended style. - OffsetRect(TargetRect, WindowOrgX, 0); - if IntersectRect(BlendRect, OrderRect(SelectionRect), TargetRect) then - begin - OffsetRect(BlendRect, -WindowOrgX, 0); - AlphaBlend(0, Target.Handle, BlendRect, Point(0, 0), bmConstantAlphaAndColor, FSelectionBlendFactor, - ColorToRGB(FColors.SelectionRectangleBlendColor)); - - Target.Brush.Color := FColors.SelectionRectangleBorderColor; - Target.FrameRect(SelectionRect); - end; + NewColumn := False; + Column := FFocusedColumn; end; -end; - -//---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.PanningWindowProc(var Message: TMessage); + if NewColumn and not FHeader.AllowFocus(Column) then + begin + NewColumn := False; + Column := FFocusedColumn; + end; -var - PS: TPaintStruct; - Canvas: TCanvas; + NewNode := FFocusedNode <> HitInfo.HitNode; -begin - if Message.Msg = WM_PAINT then + // Translate keys and filter out shift and control key. + ShiftState := KeysToShiftState(Message.Keys) * [ssShift, ssCtrl, ssAlt]; + if ssAlt in ShiftState then begin - BeginPaint(FPanningWindow, PS); - Canvas := TCanvas.Create; - Canvas.Handle := PS.hdc; - try - Canvas.Draw(0, 0, FPanningImage); - finally - Canvas.Handle := 0; - Canvas.Free; - EndPaint(FPanningWindow, PS); - end; - Message.Result := 0; + AltPressed := True; + // Remove the Alt key from the shift state. It is not meaningful there. + Exclude(ShiftState, ssAlt); end else - with Message do - Result := DefWindowProc(FPanningWindow, Msg, wParam, lParam); -end; + AltPressed := False; -//---------------------------------------------------------------------------------------------------------------------- + // Various combinations determine what states the tree enters now. + // We initialize shorthand variables to avoid the following expressions getting too large + // and to avoid repeative expensive checks. + IsLabelHit := not AltPressed and not (toSimpleDrawSelection in FOptions.SelectionOptions) and + ((hiOnItemLabel in HitInfo.HitPositions) or (hiOnNormalIcon in HitInfo.HitPositions)); -procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, MaxWidth: Integer); + IsCellHit := not AltPressed and not IsLabelHit and Assigned(HitInfo.HitNode) and + ([hiOnItemButton, hiOnItemCheckBox, hiNoWhere] * HitInfo.HitPositions = []) and + ((toFullRowSelect in FOptions.SelectionOptions) or + ((toGridExtensions in FOptions.MiscOptions) and (HitInfo.HitColumn > NoColumn))); -// This method is called immediately before a cell's content is drawn und is responsible to paint selection colors etc. + IsAnyHit := IsLabelHit or IsCellHit; + MultiSelect := toMultiSelect in FOptions.SelectionOptions; + ShiftEmpty := ShiftState = []; + NodeSelected := IsAnyHit and (vsSelected in HitInfo.HitNode.States); -var - TextColorBackup, - BackColorBackup: COLORREF; - FocusRect, - InnerRect: TRect; - RowRect: TRect; - Theme: HTHEME; -const - TREIS_HOTSELECTED = 6; + // Determine the Drag behavior. + if MultiSelect and not (toDisableDrawSelection in FOptions.SelectionOptions) then + begin + // We have MultiSelect and want to draw a selection rectangle. + // We will start a full row drag only in case a label was hit, + // otherwise a multi selection will start. + FullRowDrag := (toFullRowDrag in FOptions.MiscOptions) and IsCellHit and + not (hiNowhere in HitInfo.HitPositions) and + (NodeSelected or (hiOnItemLabel in HitInfo.HitPositions) or (hiOnNormalIcon in HitInfo.HitPositions)); + end + else // No MultiSelect, hence we can start a drag anywhere in the row. + FullRowDrag := toFullRowDrag in FOptions.MiscOptions; - //--------------- local functions ------------------------------------------- + IsHeightTracking := (Message.Msg = WM_LBUTTONDOWN) and + (hiOnItem in HitInfo.HitPositions) and + ([hiUpperSplitter, hiLowerSplitter] * HitInfo.HitPositions <> []); - procedure AlphaBlendSelection(Color: TColor); + // Dragging might be started in the inherited handler manually (which is discouraged for stability reasons) + // the test for manual mode is done below (after the focused node is set). + AutoDrag := ((DragMode = dmAutomatic) or Dragging) and (not IsCellHit or FullRowDrag); - var - R: TRect; + // Query the application to learn if dragging may start now (if set to dmManual). + if Assigned(HitInfo.HitNode) and not AutoDrag and (DragMode = dmManual) then + AutoDrag := DoBeforeDrag(HitInfo.HitNode, Column) and (FullRowDrag or IsLabelHit); + // handle node height tracking + if IsHeightTracking then begin - // Take into account any window offset and size limitations in the target bitmap, as this is only as large - // as necessary and might not cover the whole node. For normal painting this does not matter (because of - // clipping) but for the MMX code there is no such check and it will crash badly when bitmap boundaries are - // crossed. - R := InnerRect; - OffsetRect(R, -WindowOrgX, 0); - if R.Left < 0 then - R.Left := 0; - if R.Right > MaxWidth then - R.Right := MaxWidth; - AlphaBlend(0, PaintInfo.Canvas.Handle, R, Point(0, 0), bmConstantAlphaAndColor, - FSelectionBlendFactor, ColorToRGB(Color)); - end; + if hiUpperSplitter in HitInfo.HitPositions then + FHeightTrackNode := GetPreviousVisible(HitInfo.HitNode, True) + else + FHeightTrackNode := HitInfo.HitNode; - //--------------------------------------------------------------------------- + if CanSplitterResizeNode(Point(Message.XPos, Message.YPos), FHeightTrackNode, HitInfo.HitColumn) then + begin + FHeightTrackColumn := HitInfo.HitColumn; + NodeRect := GetDisplayRect(FHeightTrackNode, FHeightTrackColumn, False); + FHeightTrackPoint := Point(NodeRect.Left, NodeRect.Top); + DoStateChange([tsNodeHeightTrackPending]); + Exit; + end; + end; - procedure DrawBackground(State: Integer); + // handle button clicks + if (hiOnItemButton in HitInfo.HitPositions) and (vsHasChildren in HitInfo.HitNode.States) then begin - // if the full row selection is disabled or toGridExtensions is in the MiscOptions, draw the selection - // into the InnerRect, otherwise into the RowRect - if not (toFullRowSelect in FOptions.SelectionOptions) or (toGridExtensions in FOptions.MiscOptions) then - DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, TVP_TREEITEM, State, InnerRect, nil) - else - DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, TVP_TREEITEM, State, RowRect, nil); + ToggleNode(HitInfo.HitNode); + Exit; end; - procedure DrawThemedFocusRect(State: Integer); - var - Theme: HTHEME; + // check event + if hiOnItemCheckBox in HitInfo.HitPositions then begin - Theme := OpenThemeData(Application.ActiveFormHandle, 'Explorer::ItemsView'); - if not (toFullRowSelect in FOptions.SelectionOptions) or (toGridExtensions in FOptions.MiscOptions) then - DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, LVP_LISTDETAIL, State, InnerRect, nil) - else - DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, LVP_LISTDETAIL, State, RowRect, nil); - CloseThemeData(Theme); + HandleCheckboxClick(HitInfo.HitNode, Message.Keys); + Exit; end; - //--------------- end local functions --------------------------------------- + // Keep this node's level in case we need it for constraint selection. + if (FRoot.ChildCount > 0) and ShiftEmpty or (FSelectionCount = 0) then + if Assigned(HitInfo.HitNode) then + FLastSelectionLevel := GetNodeLevelForSelectConstraint(HitInfo.HitNode) + else + FLastSelectionLevel := GetNodeLevelForSelectConstraint(GetLastVisibleNoInit(nil, True)); -begin - if tsUseExplorerTheme in FStates then + // immediate clearance + // Determine for the right mouse button if there is a popup menu. In this case and if drag'n drop is pending + // the current selection has to stay as it is. + with HitInfo, Message do + CanClear := not AutoDrag and + (not (tsRightButtonDown in FStates) or not HasPopupMenu(HitNode, HitColumn, Point(XPos, YPos))); + + // pending clearance + if MultiSelect and ShiftEmpty and not (hiOnItemCheckbox in HitInfo.HitPositions) and IsAnyHit and AutoDrag and + NodeSelected and not FSelectionLocked + then + DoStateChange([tsClearPending]); + + // User starts a selection with a selection rectangle. + if not (toDisableDrawSelection in FOptions.SelectionOptions) and not (IsLabelHit or FullRowDrag) and MultiSelect then begin - Theme := OpenThemeData(Application.ActiveFormHandle, 'Explorer::TreeView'); - RowRect := Rect(0, PaintInfo.CellRect.Top, FRangeX, PaintInfo.CellRect.Bottom); - if (Header.Columns.Count = 0) and (toFullRowSelect in TreeOptions.SelectionOptions) then - RowRect.Right := Max(ClientWidth, RowRect.Right); - if toShowVertGridLines in FOptions.PaintOptions then - Dec(RowRect.Right); + SetCapture(Handle); + DoStateChange([tsDrawSelPending]); + FDrawSelShiftState := ShiftState; + FNewSelRect := Rect(Message.XPos + FEffectiveOffsetX, Message.YPos - FOffsetY, Message.XPos + FEffectiveOffsetX, + Message.YPos - FOffsetY); + FLastSelRect := Rect(0, 0, 0, 0); end; - with PaintInfo, Canvas do + NeedChangeEvent := FSelectionCount >= 1; + if not FSelectionLocked and ((not (IsAnyHit or FullRowDrag) and MultiSelect and ShiftEmpty) or + (IsAnyHit and (not NodeSelected or (NodeSelected and CanClear)) and (ShiftEmpty or not MultiSelect or (tsRightButtonDown in FStates)))) then begin - // Fill cell background if its color differs from tree background. - with FHeader.Columns do - if poColumnColor in PaintOptions then - begin - Brush.Color := Items[Column].GetEffectiveColor; - FillRect(CellRect); - end; - - // Let the application customize the cell background and the content rectangle. - DoBeforeCellPaint(Canvas, Node, Column, cpmPaint, CellRect, ContentRect); - - InnerRect := ContentRect; + Assert(not (tsClearPending in FStates), 'Pending and direct clearance are mutual exclusive!'); - // The selection rectangle depends on alignment. - if not (toGridExtensions in FOptions.MiscOptions) then + // If the currently hit node was already selected then we have to reselect it again after clearing the current + // selection, but without a change event if it is the only selected node. + // The same applies if the Alt key is pressed, which allows to start drawing the selection rectangle also + // on node captions and images. Here the previous selection state does not matter, though. + if NodeSelected or (AltPressed and Assigned(HitInfo.HitNode) and (HitInfo.HitColumn = FHeader.MainColumn)) and not (hiNowhere in HitInfo.HitPositions) then begin - case Alignment of - taLeftJustify: - with TWithSafeRect(InnerRect) do - if Left + NodeWidth < Right then - Right := Left + NodeWidth; - taCenter: - with TWithSafeRect(InnerRect) do - if (Right - Left) > NodeWidth then - begin - Left := (Left + Right - NodeWidth) div 2; - Right := Left + NodeWidth; - end; - taRightJustify: - with TWithSafeRect(InnerRect) do - if (Right - Left) > NodeWidth then - Left := Right - NodeWidth; + InternalClearSelection; + InternalAddToSelection(HitInfo.HitNode, True); + if NeedChangeEvent then + begin + Invalidate; + Change(nil); end; + end + else if (toAlwaysSelectNode in Self.TreeOptions.SelectionOptions) then + begin + if not (hiNowhere in HitInfo.HitPositions) then + ClearSelection(False) + else + if not (ssCtrl in ShiftState) then + DoStateChange([tsClearOnNewSelection], []); + end + else + ClearSelection(False); end; - if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.SelectionOptions) then + // pending node edit + if Focused and + ((hiOnItemLabel in HitInfo.HitPositions) or ((toGridExtensions in FOptions.MiscOptions) and + (hiOnItem in HitInfo.HitPositions))) and NodeSelected and not NewColumn and ShiftEmpty and (SelectedCount = 1) then + begin + DoStateChange([tsEditPending]); + end; + + if not (toDisableDrawSelection in FOptions.SelectionOptions) + and not (IsLabelHit or FullRowDrag) and (MultiSelect or (hiNowhere in HitInfo.HitPositions)) then + begin + // The original code here was moved up to fix issue #187. + // In order not to break the semantics of this procedure, we are leaving these if statements here + if not IsCellHit then begin + if NeedChangeEvent then + Change(nil); + Exit; + end; + end; + + // Keep current mouse position. + FLastClickPos := Point(Message.XPos, Message.YPos); + + // Handle selection and node focus change. + if (IsLabelHit or IsCellHit) and + DoFocusChanging(FFocusedNode, HitInfo.HitNode, FFocusedColumn, Column) then + begin + if NewColumn then begin - // Fill the selection rectangle. - if poDrawSelection in PaintOptions then - begin - if Node = FDropTargetNode then - begin - if (FLastDropMode = dmOnNode) or (vsSelected in Node.States) then - begin - Brush.Color := FColors.DropTargetColor; - Pen.Color := FColors.DropTargetBorderColor; - if (toGridExtensions in FOptions.MiscOptions) or - (toFullRowSelect in FOptions.SelectionOptions) then - InnerRect := CellRect; - if not IsRectEmpty(InnerRect) then - if tsUseExplorerTheme in FStates then - DrawBackground(TREIS_SELECTED) - else - if (toUseBlendedSelection in FOptions.PaintOptions) then - AlphaBlendSelection(Brush.Color) - else - with TWithSafeRect(InnerRect) do - RoundRect(Left, Top, Right, Bottom, FSelectionCurveRadius, FSelectionCurveRadius); - end - else - begin - Brush.Style := bsClear; - end; - end - else - if vsSelected in Node.States then - begin - if Focused or (toPopupMode in FOptions.PaintOptions) then - begin - Brush.Color := FColors.FocusedSelectionColor; - Pen.Color := FColors.FocusedSelectionBorderColor; - end - else - begin - Brush.Color := FColors.UnfocusedSelectionColor; - Pen.Color := FColors.UnfocusedSelectionBorderColor; - end; - if (toGridExtensions in FOptions.MiscOptions) or (toFullRowSelect in FOptions.SelectionOptions) then - InnerRect := CellRect; - if not IsRectEmpty(InnerRect) then - if tsUseExplorerTheme in FStates then - begin - // If the node is also hot, its background will be drawn later. - if not (toHotTrack in FOptions.PaintOptions) or (Node <> FCurrentHotNode) or - ((Column <> FCurrentHotColumn) and not (toFullRowSelect in FOptions.SelectionOptions)) then - DrawBackground(IfThen(Self.Focused, TREIS_SELECTED, TREIS_SELECTEDNOTFOCUS)); - end - else - if (toUseBlendedSelection in FOptions.PaintOptions) then - AlphaBlendSelection(Brush.Color) - else - with TWithSafeRect(InnerRect) do - RoundRect(Left, Top, Right, Bottom, FSelectionCurveRadius, FSelectionCurveRadius); - end; - end; + if not Assigned(FFocusedNode) then + InvalidateColumn(FFocusedColumn) + else + invalidateWithAutoSpan(FFocusedColumn, FFocusedNode); //fix: issue 310 + if not Assigned(HitInfo.HitNode) then + InvalidateColumn(Column) + else + invalidateWithAutoSpan(Column, HitInfo.HitNode); //fix: issue 310 + FFocusedColumn := Column; end; + if DragKind = dkDock then + begin + StopTimer(ScrollTimer); + DoStateChange([], [tsScrollPending, tsScrolling]); + end; + // Get the currently focused node to make multiple multi-selection blocks possible. + LastFocused := FFocusedNode; + if NewNode then + DoFocusNode(HitInfo.HitNode, False); - if (tsUseExplorerTheme in FStates) and (toHotTrack in FOptions.PaintOptions) and (Node = FCurrentHotNode) and - ((Column = FCurrentHotColumn) or (toFullRowSelect in FOptions.SelectionOptions)) then - DrawBackground(IfThen((vsSelected in Node.States) and not (toAlwaysHideSelection in FOptions.PaintOptions), - TREIS_HOTSELECTED, TREIS_HOT)); - - if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.SelectionOptions) then + if MultiSelect and not ShiftEmpty and not (tsRightButtonDown in FStates) then + HandleClickSelection(LastFocused, HitInfo.HitNode, ShiftState, AutoDrag) + else begin - // draw focus rect - if (poDrawFocusRect in PaintOptions) and - (Focused or (toPopupMode in FOptions.PaintOptions)) and (FFocusedNode = Node) and - ( (Column = FFocusedColumn) or - ((not (toExtendedFocus in FOptions.SelectionOptions) or IsWinVistaOrAbove) and - (toFullRowSelect in FOptions.SelectionOptions) and - (tsUseExplorerTheme in FStates) ) ) then - begin - TextColorBackup := GetTextColor(Handle); - SetTextColor(Handle, $FFFFFF); - BackColorBackup := GetBkColor(Handle); - SetBkColor(Handle, 0); + if ShiftEmpty then + FRangeAnchor := HitInfo.HitNode; - if not (toExtendedFocus in FOptions.SelectionOptions) and (toFullRowSelect in FOptions.SelectionOptions) and - (tsUseExplorerTheme in FStates) then - FocusRect := RowRect - else - if toGridExtensions in FOptions.MiscOptions then - FocusRect := CellRect - else - FocusRect := InnerRect; + // If the hit node is not yet selected then do it now. + if not NodeSelected then + AddToSelection(HitInfo.HitNode, True); + end; - if tsUseExplorerTheme in FStates then - InflateRect(FocusRect, -1, -1); + if NewNode or NewColumn then + begin + ScrollIntoView(FFocusedNode, False, + not (toDisableAutoscrollOnFocus in FOptions.AutoOptions) + and not (toFullRowSelect in FOptions.SelectionOptions)); - if (tsUseExplorerTheme in FStates) and IsWinVistaOrAbove then - begin - //Draw focused unselected style like Windows 7 Explorer - if not (vsSelected in Node.States) then - DrawThemedFocusRect(LIS_NORMAL) - else - DrawBackground(TREIS_HOTSELECTED); - end - else - Winapi.Windows.DrawFocusRect(Handle, FocusRect); - SetTextColor(Handle, TextColorBackup); - SetBkColor(Handle, BackColorBackup); - end; + DoFocusChange(FFocusedNode, FFocusedColumn); end; end; - if tsUseExplorerTheme in FStates then - CloseThemeData(Theme); + if (SelectedCount = 0) and NeedChangeEvent then + Change(nil); + + // Drag'n drop initiation + // If we lost focus in the interim the button states would be cleared in WM_KILLFOCUS. + if AutoDrag and IsAnyHit and (FStates * [tsLeftButtonDown, tsRightButtonDown, tsMiddleButtonDown] <> []) then + BeginDrag(False); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.ReadChunk(Stream: TStream; Version: Integer; Node: PVirtualNode; ChunkType, - ChunkSize: Integer): Boolean; +procedure TBaseVirtualTree.HandleMouseUp(var Message: TWMMouse; const HitInfo: THitInfo); -// Called while loading a tree structure, Node is already valid (allocated) at this point. -// The function handles the base and user chunks, any other chunk is marked as being unknown (result becomes False) -// and skipped. descendants may handle them by overriding this method. -// Returns True if the chunk could be handled, otherwise False. -type - TAdvancedVersion2Identifier = packed record - ChildCount, - NodeHeight: Cardinal; - States: Word; - Align: Byte; - CheckState: TCheckState; - CheckType: TCheckType; - Reserved: Cardinal; - end; +// Counterpart to the mouse down handler. var - IdBody: TAdvancedVersion2Identifier; - ChunkBody: TBaseChunkBody; - Run: PVirtualNode; - LastPosition: Integer; + ReselectFocusedNode: Boolean; begin - case ChunkType of - BaseChunk: + ReleaseCapture; + + if not (tsVCLDragPending in FStates) then + begin + // reset pending or persistent states + if IsMouseSelecting then + begin + DoStateChange([], [tsDrawSelecting, tsDrawSelPending, tsToggleFocusedSelection, tsClearOnNewSelection]); + Invalidate; + end; + + if tsClearPending in FStates then + begin + ReselectFocusedNode := Assigned(FFocusedNode) and (vsSelected in FFocusedNode.States); + ClearSelection; + if ReselectFocusedNode then + AddToSelection(FFocusedNode, False); + end; + + if (tsToggleFocusedSelection in FStates) and (HitInfo.HitNode = FFocusedNode) and Assigned(HitInfo.HitNode) then //Prevent AV when dereferencing HitInfo.HitNode below, see bug #100 + begin + if vsSelected in HitInfo.HitNode.States then begin - // Load base chunk's body (chunk header has already been consumed). - case Version of - 1: - begin - with ChunkBody do - begin - // In version prior to 2 there was a smaller chunk body. Hence we have to read it entry by entry now. - Stream.Read(ChildCount, SizeOf(ChildCount)); - Stream.Read(NodeHeight, SizeOf(NodeHeight)); - // TVirtualNodeStates was a byte sized type in version 1. - States := []; - Stream.Read(States, SizeOf(Byte)); - // vsVisible is now in the place where vsSelected was before, but every node was visible in the old version - // so we need to fix this too. - if vsVisible in States then - //sync path note: prior version stream reading, ignored for syncing - Include(States, vsSelected) - else - Include(States, vsVisible); - Stream.Read(Align, SizeOf(Align)); - Stream.Read(CheckState, SizeOf(CheckState)); - Stream.Read(CheckType, SizeOf(CheckType)); - end; - end; - 2: - begin - ZeroMemory(@IdBody, SizeOf(IdBody)); - Stream.Read(IdBody, SizeOf(IdBody)); - // If Align is greater than zero, we have a stream prior to VT version 6.2 - if IdBody.Align > 0 then - with ChunkBody do - begin - ChildCount := IdBody.ChildCount; - NodeHeight := IdBody.NodeHeight; - States := []; - Move(IdBody.States, States, SizeOf(IdBody.States)); - CheckState := IdBody.CheckState; - CheckType := IdBody.CheckType; - Reserved := IdBody.Reserved; - end - else - begin - // Stream is compatible with current size of TBaseChunkBody - Stream.Position := Stream.Position - SizeOf(IdBody); - Stream.Read(ChunkBody, SizeOf(ChunkBody)); - end; - end; - 3: - Stream.Read(ChunkBody, SizeOf(ChunkBody)); - end; + if not (toAlwaysSelectNode in TreeOptions.SelectionOptions) or (Self.SelectedCount > 1) then + RemoveFromSelection(HitInfo.HitNode); + end + else + AddToSelection(HitInfo.HitNode, False); + end; - with Node^ do - begin - // Set states first, in case the node is invisible. - States := ChunkBody.States; - NodeHeight := ChunkBody.NodeHeight; - TotalHeight := NodeHeight; - Align := ChunkBody.Align; - CheckState := ChunkBody.CheckState; - CheckType := ChunkBody.CheckType; - ChildCount := ChunkBody.ChildCount; + DoStateChange([], [tsOLEDragPending, tsOLEDragging, tsClearPending, tsDrawSelPending, tsToggleFocusedSelection, + tsScrollPending, tsScrolling]); + StopTimer(ScrollTimer); - // Create and read child nodes. - while ChunkBody.ChildCount > 0 do - begin - Run := MakeNewNode; + if (FHeader.Columns.ClickIndex > NoColumn) and (FHeader.Columns.ClickIndex = HitInfo.HitColumn) then + DoColumnClick(HitInfo.HitColumn, KeysToShiftState(Message.Keys)); - Run.PrevSibling := Node.LastChild; - if Assigned(Run.PrevSibling) then - Run.Index := Run.PrevSibling.Index + 1; - if Assigned(Node.LastChild) then - Node.LastChild.NextSibling := Run - else - Node.FirstChild := Run; - Node.LastChild := Run; - Run.Parent := Node; + if FLastHitInfo.HitNode <> nil then begin // Use THitInfo of mouse down here, see issue #692 + DoNodeClick(FLastHitInfo); + if Assigned(FLastHitInfo.HitNode) then begin + InvalidateNode(FLastHitInfo.HitNode); + FLastHitInfo.HitNode := nil; // prevent firing the event again + end;//if + end; - ReadNode(Stream, Version, Run); - Dec(ChunkBody.ChildCount); - end; - end; - Result := True; - end; - UserChunk: - if ChunkSize > 0 then + // handle a pending edit event + if tsEditPending in FStates then + begin + // Is the mouse still over the same node? + if (HitInfo.HitNode = FFocusedNode) and (hiOnItem in HitInfo.HitPositions) and + (toEditOnClick in FOptions.MiscOptions) and (FFocusedColumn = HitInfo.HitColumn) and + CanEdit(FFocusedNode, HitInfo.HitColumn) then begin - // need to know whether the data was read - LastPosition := Stream.Position; - DoLoadUserData(Node, Stream); - // compare stream position to learn whether the data was read - Result := Stream.Position > LastPosition; - // Improve stability by advancing the stream to the chunk's real end if - // the application did not read what has been written. - if not Result or (Stream.Position <> (LastPosition + ChunkSize)) then - Stream.Position := LastPosition + ChunkSize; + FEditColumn := FFocusedColumn; + SetTimer(Handle, EditTimer, FEditDelay, nil); end else - Result := True; - else - // unknown chunk, skip it - Stream.Position := Stream.Position + ChunkSize; - Result := False; + DoStateChange([], [tsEditPending]); + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.ReadNode(Stream: TStream; Version: Integer; Node: PVirtualNode); +function TBaseVirtualTree.HasImage(Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex): Boolean; + +// Determines whether the given node has got an image of the given kind in the given column. +// Returns True if so, otherwise False. +// The given node will be implicitly initialized if needed. + +var + Ghosted: Boolean; + Index: TImageIndex; + +begin + if not (vsInitialized in Node.States) then + InitNode(Node); + + Index := -1; + Ghosted := False; + DoGetImageIndex(Node, Kind, Column, Ghosted, Index); + Result := Index > -1; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.HasPopupMenu(Node: PVirtualNode; Column: TColumnIndex; Pos: TPoint): Boolean; -// Reads the anchor chunk of each node and initiates reading the sub chunks for this node +// Determines whether the tree got a popup menu, either in its PopupMenu property, via the OnGetPopupMenu event or +// through inheritance. The latter case must be checked by the descendant which must override this method. + +begin + Result := Assigned(PopupMenu) or Assigned(DoGetPopupMenu(Node, Column, Pos)); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.IncVisibleCount; +begin + Inc(FVisibleCount); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.InitChildren(Node: PVirtualNode); + +// Initiates the initialization of the child number of the given node. var - Header: TChunkHeader; - EndPosition: Integer; + Count: Cardinal; begin - with Stream do + if Assigned(Node) and (Node <> FRoot) and (vsHasChildren in Node.States) then begin - // Read anchor chunk of the node. - Stream.Read(Header, SizeOf(Header)); - if Header.ChunkType = NodeChunk then + Count := Node.ChildCount; + if DoInitChildren(Node, Count) then begin - EndPosition := Stream.Position + Header.ChunkSize; - // Read all subchunks until the indicated chunk end position is reached in the stream. - while Position < EndPosition do - begin - // Read new chunk header. - Stream.Read(Header, SizeOf(Header)); - ReadChunk(Stream, Version, Node, Header.ChunkType, Header.ChunkSize); - end; - // If the last chunk does not end at the given end position then there is something wrong. - if Position <> EndPosition then - ShowError(SCorruptStream2, hcTFCorruptStream2); - end - else - ShowError(SCorruptStream1, hcTFCorruptStream1); + SetChildCount(Node, Count); + if Count = 0 then + Exclude(Node.States, vsHasChildren); + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.RedirectFontChangeEvent(Canvas: TCanvas); +procedure TBaseVirtualTree.InitNode(Node: PVirtualNode); +// Initiates the initialization of the given node to allow the application to load needed data for it. + +var + InitStates: TVirtualNodeInitStates; + MustAdjustInternalVariables: Boolean; + ParentCheckState, SelfCheckState: TCheckState; begin - if @Canvas.Font.OnChange <> @FOldFontChange then + with Node^ do begin - FOldFontChange := Canvas.Font.OnChange; - Canvas.Font.OnChange := FontChanged; + Include(States, vsInitializing); + try + InitStates := []; + if vsInitialized in States then + Include(InitStates, ivsReInit); + Include(States, vsInitialized); + if Parent = FRoot then + DoInitNode(nil, Node, InitStates) + else + DoInitNode(Parent, Node, InitStates); + + // Fix: Any parent check state must be propagated here. + // Because the CheckType is normally set in DoInitNode + // by the App. + if (Node.CheckType = ctTriStateCheckBox) and (toAutoTristateTracking in FOptions.AutoOptions) then + begin + ParentCheckState := Self.GetCheckState(Node.Parent); + SelfCheckState := Self.GetCheckState(Node); + if ((ParentCheckState = csCheckedNormal) + or (ParentCheckState = csUncheckedNormal)) + and (not SelfCheckState.IsDisabled()) + and (SelfCheckState <> ParentCheckState) + and (Parent <> FRoot) + then + SetCheckState(Node, Node.Parent.CheckState); + end + else if (toSyncCheckboxesWithSelection in TreeOptions.SelectionOptions) then + Node.CheckType := TCheckType.ctCheckBox; + + if ivsDisabled in InitStates then + Include(States, vsDisabled); + if ivsHasChildren in InitStates then + Include(States, vsHasChildren); + if ivsSelected in InitStates then + InternalAddToSelection(Node, False); + if ivsMultiline in InitStates then + Include(States, vsMultiline); + if ivsFiltered in InitStates then + begin + MustAdjustInternalVariables := not ((ivsReInit in InitStates) and (vsFiltered in States)); + + Include(States, vsFiltered); + + if not (toShowFilteredNodes in FOptions.PaintOptions) and MustAdjustInternalVariables then + begin + AdjustTotalHeight(Node, -NodeHeight, True); + if FullyVisible[Node] then + Dec(FVisibleCount); + if FUpdateCount = 0 then + UpdateScrollBars(True); + end; + end; + + // Expanded may already be set (when called from ReinitNode) or be set in DoInitNode, allow both. + if (vsExpanded in Node.States) xor (ivsExpanded in InitStates) then + begin + // Expand node if not yet done (this will automatically initialize child nodes). + if ivsExpanded in InitStates then + ToggleNode(Node) + else + // If the node already was expanded then explicitly trigger child initialization. + if vsHasChildren in Node.States then + InitChildren(Node); + end; + finally + Exclude(States, vsInitializing); + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.RemoveFromSelection(Node: PVirtualNode); +procedure TBaseVirtualTree.InternalAddFromStream(Stream: TStream; Version: Integer; Node: PVirtualNode); + +// Loads all details for Node (including its children) from the given stream. +// Because the new nodes might be selected this method also fixes the selection array. var + Stop: PVirtualNode; Index: Integer; + LastTotalHeight: Cardinal; + WasFullyVisible: Boolean; begin - if not FSelectionLocked then - begin - Assert(Assigned(Node), 'Node must not be nil!'); - Assert(GetCurrentThreadId = MainThreadId, Self.Classname + '.RemoveFromSelection() must only be called from UI thread.'); - if vsSelected in Node.States then - begin - Assert(FSelectionCount > 0, 'if one node has set the vsSelected flag, SelectionCount must be >0.'); - //sync path note: deselect when a ctrl click removes a selection - Exclude(Node.States, vsSelected); - if SyncCheckstateWithSelection[Node] then - Node.CheckState := csUncheckedNormal; // Avoid using SetCheckState() as it handles toSyncCheckboxesWithSelection as well. + Assert(Node <> FRoot, 'The root node cannot be loaded from stream.'); - if FindNodeInSelection(Node, Index, -1, -1) and (Index < FSelectionCount - 1) then - Move(FSelection[Index + 1], FSelection[Index], (FSelectionCount - Index - 1) * SizeOf(Pointer)); - if FSelectionCount > 0 then - Dec(FSelectionCount); - SetLength(FSelection, FSelectionCount); + // Keep the current total height value of Node as it has already been applied + // but might change in the load and fixup code. We have to adjust that afterwards. + LastTotalHeight := Node.TotalHeight; + WasFullyVisible := FullyVisible[Node] and not IsEffectivelyFiltered[Node]; - if FSelectionCount = 0 then - ResetRangeAnchor; + // Read in the new nodes. + ReadNode(Stream, Version, Node); - if FSelectionCount <= 1 then - UpdateNextNodeToSelect(Node); + // One time update of node-internal states and the global visibility counter. + // This is located here to ease and speed up the loading process. + FixupTotalCount(Node); + AdjustTotalCount(Node.Parent, Node.TotalCount - 1, True); // -1 because Node itself was already set. + FixupTotalHeight(Node); + AdjustTotalHeight(Node.Parent, Integer(Node.TotalHeight) - Integer(LastTotalHeight), True); - DoRemoveFromSelection(Node); - InvalidateNode(Node); - Change(Node); + // New nodes are always visible, so the visible node count has been increased already. + // If Node is now invisible we have to take back this increment and don't need to add any visible child node. + if not FullyVisible[Node] or IsEffectivelyFiltered[Node] then + begin + if WasFullyVisible then + Dec(FVisibleCount); + end + else + // It can never happen that the node is now fully visible but was not before as this would require + // that the visibility state of one of its parents has changed, which cannot happen during loading. + Inc(FVisibleCount, CountVisibleChildren(Node)); + + // Fix selection array. + ClearTempCache; + if Node = FRoot then + Stop := nil + else + Stop := Node.NextSibling; + + if toMultiSelect in FOptions.SelectionOptions then + begin + // Add all nodes which were selected before to the current selection (unless they are already there). + while Node <> Stop do + begin + if (vsSelected in Node.States) and not FindNodeInSelection(Node, Index, 0, High(FSelection)) then + InternalCacheNode(Node); + Node := GetNextNoInit(Node); + end; + if FTempNodeCount > 0 then + AddToSelection(FTempNodeCache, FTempNodeCount, True); + ClearTempCache; + end + else // No further selected nodes allowed so delete the corresponding flag in all new nodes. + while Node <> Stop do + begin + Exclude(Node.States, vsSelected); + Node := GetNextNoInit(Node); end; - end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.UpdateNextNodeToSelect(Node: PVirtualNode); - -// save a potential node to select after the currently selected node will be deleted. -// This will make the VT to behave more like the Win32 TreeView, which always selecta a new node if the currently -// selected one gets deleted. - +function TBaseVirtualTree.InternalAddToSelection(Node: PVirtualNode; ForceInsert: Boolean): Boolean; +var + lSingletonNodeArray: TNodeArray; begin - if not (toAlwaysSelectNode in TreeOptions.SelectionOptions) then - Exit; - if GetNextSibling(Node) <> nil then - FNextNodeToSelect := GetNextSibling(Node) - else if GetPreviousSibling(Node) <> nil then - FNextNodeToSelect := GetPreviousSibling(Node) - else if Node.Parent <> FRoot then - FNextNodeToSelect := Node.Parent - else - FNextNodeToSelect := nil; -end;//if Assigned(Node); + Assert(Assigned(Node), 'Node must not be nil!'); + SetLength(lSingletonNodeArray, 1); + lSingletonNodeArray[0] := Node; + Result := InternalAddToSelection(lSingletonNodeArray, 1, ForceInsert); +end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.RenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium; - ForClipboard: Boolean): HResult; +function TBaseVirtualTree.InternalAddToSelection(const NewItems: TNodeArray; NewLength: Integer; + ForceInsert: Boolean): Boolean; -// Returns a memory expression of all currently selected nodes in the Medium structure. -// Note: The memory requirement of this method might be very high. This depends however on the requested storage format. -// For HGlobal (a global memory block) we need to render first all nodes to local memory and copy this then to -// the global memory in Medium. This is necessary because we have first to determine how much -// memory is needed before we can allocate it. Hence for a short moment we need twice the space as used by the -// nodes alone (plus the amount the nodes need in the tree anyway)! -// With IStream this does not happen. We directly stream out the nodes and pass the constructed stream along. +// Internal version of method AddToSelection which does not trigger OnChange events - //--------------- local function -------------------------------------------- +var + I, J: Integer; + CurrentEnd: Integer; + Constrained, + SiblingConstrained: Boolean; + lPreviousSelectedCount: Integer; + AddedNodesSize: Integer; + PTmpNode: PVirtualNode; - procedure WriteNodes(Stream: TStream); +begin + lPreviousSelectedCount := FSelectionCount; + // The idea behind this code is to use a kind of reverse merge sort. QuickSort is quite fast + // and would do the job here too but has a serious problem with already sorted lists like FSelection. - var - Selection: TNodeArray; - I: Integer; + // current number of valid entries + AddedNodesSize := 0; + // 1) Remove already selected items, mark all other as being selected. + if ForceInsert then begin - if ForClipboard then - Selection := GetSortedCutCopySet(True) - else - Selection := GetSortedSelection(True); - for I := 0 to High(Selection) do - WriteNode(Stream, Selection[I]); + //Fix: For already selected node when selected, this path + //is used that didn't contain the Constraint logic. Added. + Constrained := toLevelSelectConstraint in FOptions.SelectionOptions; + if Constrained and (FLastSelectionLevel = -1) then + FLastSelectionLevel := GetNodeLevelForSelectConstraint(NewItems[0]); + AddedNodesSize := NewLength; + end + else + begin + Constrained := toLevelSelectConstraint in FOptions.SelectionOptions; + if Constrained and (FLastSelectionLevel = -1) then + FLastSelectionLevel := GetNodeLevelForSelectConstraint(NewItems[0]); + SiblingConstrained := toSiblingSelectConstraint in FOptions.SelectionOptions; + if SiblingConstrained and (FRangeAnchor = nil) then + FRangeAnchor := NewItems[0]; + + for I := 0 to NewLength - 1 do + if ([vsSelected, vsDisabled] * NewItems[I].States <> []) or + (Constrained and (Cardinal(FLastSelectionLevel) <> GetNodeLevel(NewItems[I]))) or + (SiblingConstrained and (FRangeAnchor.Parent <> NewItems[I].Parent)) + then + Inc(PAnsiChar(NewItems[I])) // mark as invalid by setting the LSB + else + Inc(AddedNodesSize); end; - //--------------- end local function ---------------------------------------- + I := PackArray(NewItems, NewLength); + if I > -1 then + NewLength := I; -var - Data: PCardinal; - ResPointer: Pointer; - ResSize: Integer; - OLEStream: IStream; - VCLStream: TStream; + Result := NewLength > 0; + if Result then + begin + // 2) Sort the new item list so we can easily traverse it. + if NewLength > 1 then + QuickSort(NewItems, 0, NewLength - 1); + // 3) Make room in FSelection for the new items. + if lPreviousSelectedCount + NewLength >= Length(FSelection) then + SetLength(FSelection, lPreviousSelectedCount + NewLength); -begin - ZeroMemory (@Medium, SizeOf(Medium)); + // 4) Merge in new items + J := NewLength - 1; + CurrentEnd := lPreviousSelectedCount - 1; - // We can render the native clipboard format in two different storage media. - if (FormatEtcIn.cfFormat = CF_VIRTUALTREE) and (FormatEtcIn.tymed and (TYMED_HGLOBAL or TYMED_ISTREAM) <> 0) then - begin - VCLStream := nil; - try - Medium.unkForRelease := nil; - // Return data in one of the supported storage formats, prefer IStream. - if FormatEtcIn.tymed and TYMED_ISTREAM <> 0 then + while J >= 0 do + begin + // First insert all new entries which are greater than the greatest entry in the old list. + // If the current end marker is < 0 then there's nothing more to move in the selection + // array and only the remaining new items must be inserted. + if CurrentEnd >= 0 then begin - // Create an IStream on a memory handle (here it is 0 which indicates to implicitely allocated a handle). - // Do not use TStreamAdapter as it is not compatible with OLE (when flushing the clipboard OLE wants the HGlobal - // back which is not supported by TStreamAdapater). - CreateStreamOnHGlobal(0, True, OLEStream); - VCLStream := TOLEStream.Create(OLEStream); - WriteNodes(VCLStream); - // Rewind stream. - VCLStream.Position := 0; - Medium.tymed := TYMED_ISTREAM; - IUnknown(Medium.stm) := OLEStream; - Result := S_OK; + while (J >= 0) and (PAnsiChar(NewItems[J]) > PAnsiChar(FSelection[CurrentEnd])) do + begin + FSelection[CurrentEnd + J + 1] := NewItems[J]; + Dec(J); + end; + // early out if nothing more needs to be copied + if J < 0 then + Break; end else begin - VCLStream := TMemoryStream.Create; - WriteNodes(VCLStream); - ResPointer := TMemoryStream(VCLStream).Memory; - ResSize := VCLStream.Position; - - // Allocate memory to hold the string. - if ResSize > 0 then - begin - Medium.hGlobal := GlobalAlloc(GHND or GMEM_SHARE, ResSize + SizeOf(Cardinal)); - Data := GlobalLock(Medium.hGlobal); - // Store the size of the data too, for easy retrival. - Data^ := ResSize; - Inc(Data); - Move(ResPointer^, Data^, ResSize); - GlobalUnlock(Medium.hGlobal); - Medium.tymed := TYMED_HGLOBAL; - - Result := S_OK; - end - else - Result := E_FAIL; + // insert remaining new entries at position 0 + Move(NewItems[0], FSelection[0], (J + 1) * SizeOf(Pointer)); + // nothing more to do so exit main loop + Break; end; - finally - // We can free the VCL stream here since it was either a pure memory stream or only a wrapper around - // the OLEStream which exists independently. - VCLStream.Free; - end; - end - else // Ask application descendants to render self defined formats. - Result := DoRenderOLEData(FormatEtcIn, Medium, ForClipboard); -end; - -//---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.ResetRangeAnchor; - -// Called when there is no selected node anymore and the selection range anchor needs a new value. - -begin - FRangeAnchor := FFocusedNode; - FLastSelectionLevel := -1; -end; + // find the last entry in the remaining selection list which is smaller then the largest + // entry in the remaining new items list + FindNodeInSelection(NewItems[J], I, 0, CurrentEnd); + Dec(I); + // move all entries which are greater than the greatest entry in the new items list up + // so the remaining gap travels down to where new items must be inserted + Move(FSelection[I + 1], FSelection[I + J + 2], (CurrentEnd - I) * SizeOf(Pointer)); + CurrentEnd := I; + end; -//---------------------------------------------------------------------------------------------------------------------- + // update selection count + Inc(FSelectionCount, AddedNodesSize); -procedure TBaseVirtualTree.RestoreFontChangeEvent(Canvas: TCanvas); + // post process added nodes + for I := 0 to AddedNodesSize - 1 do + begin + PTmpNode := NewItems[I]; + //sync path note: on click, multi-select ctrl-click and draw selection + Include(PTmpNode.States, vsSelected); + // call on add event callbackevent + if Assigned(FOnAddToSelection) then + FOnAddToSelection(Self, PTmpNode); + if SyncCheckstateWithSelection[PTmpNode] then + checkstate[PTmpNode] := csCheckedNormal; + end; -begin - Canvas.Font.OnChange := FOldFontChange; - FOldFontChange := nil; + Assert(FSelectionCount = (lPreviousSelectedCount + NewLength), 'Fixing issue #487 seems to ahve caused a problem here.') + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.SelectNodes(StartNode, EndNode: PVirtualNode; AddOnly: Boolean); +procedure TBaseVirtualTree.InternalCacheNode(Node: PVirtualNode); -// Selects a range of nodes and unselects all other eventually selected nodes which are not in this range if -// AddOnly is False. -// EndNode must be visible while StartNode does not necessarily as in the case where the last focused node is the start -// node but it is a child of a node which has been collapsed previously. In this case the first visible parent node -// is used as start node. StartNode can be nil in which case the very first node in the tree is used. +// Adds the given node to the temporary node cache (used when collecting possibly large amounts of nodes). var - NodeFrom, - NodeTo, - LastAnchor: PVirtualNode; - Index: Integer; + Len: Cardinal; begin - Assert(Assigned(EndNode), 'EndNode must not be nil!'); - if not FSelectionLocked then + Len := Length(FTempNodeCache); + if FTempNodeCount = Len then begin - ClearTempCache; - if StartNode = nil then - StartNode := GetFirstVisibleNoInit(nil, True) + if Len < 100 then + Len := 100 else - if not FullyVisible[StartNode] then - begin - StartNode := GetPreviousVisible(StartNode, True); - if StartNode = nil then - StartNode := GetFirstVisibleNoInit(nil, True); - end; + Len := Len + Len div 10; + SetLength(FTempNodeCache, Len); + end; + FTempNodeCache[FTempNodeCount] := Node; + Inc(FTempNodeCount); +end; - if CompareNodePositions(StartNode, EndNode, True) < 0 then - begin - NodeFrom := StartNode; - NodeTo := EndNode; - end - else - begin - NodeFrom := EndNode; - NodeTo := StartNode; - end; +//---------------------------------------------------------------------------------------------------------------------- - // The range anchor will be reset by the following call. - LastAnchor := FRangeAnchor; - if not AddOnly then - InternalClearSelection; +procedure TBaseVirtualTree.InternalClearSelection(); - while NodeFrom <> NodeTo do +var + Count: Integer; + lNode: PVirtualNode; +begin + // It is possible that there are invalid node references in the selection array + // if the tree update is locked and changes in the structure were made. + // Handle this potentially dangerous situation by packing the selection array explicitely. + if IsUpdating then + begin + Count := PackArray(FSelection, FSelectionCount); + if Count > -1 then begin - InternalCacheNode(NodeFrom); - NodeFrom := GetNextVisible(NodeFrom, True); - end; - // select last node too - InternalCacheNode(NodeFrom); - // now add them all in "one" step - AddToSelection(FTempNodeCache, FTempNodeCount); - ClearTempCache; - if Assigned(LastAnchor) and FindNodeInSelection(LastAnchor, Index, -1, -1) then - FRangeAnchor := LastAnchor; + FSelectionCount := Count; + SetLength(FSelection, FSelectionCount); + end; + end; + + while FSelectionCount > 0 do + begin + Dec(FSelectionCount); + lNode := FSelection[FSelectionCount]; + //sync path note: deselect when click on another or on outside area + Exclude(lNode.States, vsSelected); + if SyncCheckstateWithSelection[lNode] then + CheckState[lNode] := csUncheckedNormal; + DoRemoveFromSelection(lNode); end; + ResetRangeAnchor; + FSelection := nil; + DoStateChange([], [tsClearPending]); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.SetFocusedNodeAndColumn(Node: PVirtualNode; Column: TColumnIndex); +procedure TBaseVirtualTree.InternalConnectNode(Node, Destination: PVirtualNode; Target: TBaseVirtualTree; + Mode: TVTNodeAttachMode); + +// Connects Node with Destination depending on Mode. +// No error checking takes place. Node as well as Destination must be valid. Node must never be a root node and +// Destination must not be a root node if Mode is amInsertBefore or amInsertAfter. var - OldColumn: TColumnIndex; - WasDifferent: Boolean; + Run: PVirtualNode; begin - if not FHeader.AllowFocus(Column) then - Column := FFocusedColumn; + // Keep in mind that the destination node might belong to another tree. + with Target do + begin + case Mode of + amInsertBefore: + begin + Node.PrevSibling := Destination.PrevSibling; + Destination.PrevSibling := Node; + Node.NextSibling := Destination; + Node.Parent := Destination.Parent; + Node.Index := Destination.Index; + if Node.PrevSibling = nil then + Node.Parent.FirstChild := Node + else + Node.PrevSibling.NextSibling := Node; - WasDifferent := (Node <> FFocusedNode) or (Column <> FFocusedColumn); + // reindex all following nodes + Run := Destination; + while Assigned(Run) do + begin + Inc(Run.Index); + Run := Run.NextSibling; + end; + end; + amInsertAfter: + begin + Node.NextSibling := Destination.NextSibling; + Destination.NextSibling := Node; + Node.PrevSibling := Destination; + Node.Parent := Destination.Parent; + if Node.NextSibling = nil then + Node.Parent.LastChild := Node + else + Node.NextSibling.PrevSibling := Node; + Node.Index := Destination.Index; - OldColumn := FFocusedColumn; - FFocusedColumn := Column; + // reindex all following nodes + Run := Node; + while Assigned(Run) do + begin + Inc(Run.Index); + Run := Run.NextSibling; + end; + end; + amAddChildFirst: + begin + if Assigned(Destination.FirstChild) then + begin + // If there's a first child then there must also be a last child. + Destination.FirstChild.PrevSibling := Node; + Node.NextSibling := Destination.FirstChild; + Destination.FirstChild := Node; + end + else + begin + // First child node at this location. + Destination.FirstChild := Node; + Destination.LastChild := Node; + Node.NextSibling := nil; + end; + Node.PrevSibling := nil; + Node.Parent := Destination; + Node.Index := 0; + // reindex all following nodes + Run := Node.NextSibling; + while Assigned(Run) do + begin + Inc(Run.Index); + Run := Run.NextSibling; + end; + end; + amAddChildLast: + begin + if Assigned(Destination.LastChild) then + begin + // If there's a last child then there must also be a first child. + Destination.LastChild.NextSibling := Node; + Node.PrevSibling := Destination.LastChild; + Destination.LastChild := Node; + end + else + begin + // first child node at this location + Destination.FirstChild := Node; + Destination.LastChild := Node; + Node.PrevSibling := nil; + end; + Node.NextSibling := nil; + Node.Parent := Destination; + if Assigned(Node.PrevSibling) then + Node.Index := Node.PrevSibling.Index + 1 + else + Node.Index := 0; + end; + else + // amNoWhere: do nothing + end; + // Remove temporary states. + Node.States := Node.States - [vsChecking, vsCutOrCopy, vsDeleting]; - DoFocusNode(Node, True); + if (Mode <> amNoWhere) then begin + Inc(Node.Parent.ChildCount); + Include(Node.Parent.States, vsHasChildren); + AdjustTotalCount(Node.Parent, Node.TotalCount, True); - // Check if the change was accepted. - if FFocusedNode = Node then - begin - CancelEditNode; - if WasDifferent then - DoFocusChange(FFocusedNode, FFocusedColumn); - end - else - // If the user did not accept the new cell to focus then set also the focused column back - // to its original state. - FFocusedColumn := OldColumn; + // Add the new node's height only if its parent is expanded. + if (vsExpanded in Node.Parent.States) and (vsVisible in Node.States) then begin + AdjustTotalHeight(Node.Parent, Node.TotalHeight, True); + Inc(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); + end;//if + + // Update the hidden children flag of the parent. + if (Node.Parent <> FRoot) then + begin + // If we have added a visible node then simply remove the all-children-hidden flag. + if IsEffectivelyVisible[Node] then + Exclude(Node.Parent.States, vsAllChildrenHidden) + else begin + // If we have added an invisible node and this is the only child node then + // make sure the all-children-hidden flag is in a determined state. + // If there were child nodes before then no action is needed. + if Node.Parent.ChildCount = 1 then + Include(Node.Parent.States, vsAllChildrenHidden); + end;//else + end; //if Node.Parent <> FRoot + end;//if Mode <> amNoWhere + end;//With end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.SkipNode(Stream: TStream); - -// Skips the data for the next node in the given stream (including the child nodes). - -var - Header: TChunkHeader; +function TBaseVirtualTree.InternalData(Node: PVirtualNode): Pointer; begin - with Stream do - begin - // read achor chunk of the node - Stream.Read(Header, SizeOf(Header)); - if Header.ChunkType = NodeChunk then - Stream.Position := Stream.Position + Header.ChunkSize - else - ShowError(SCorruptStream1, hcTFCorruptStream1); - end; + Result := nil; end; //---------------------------------------------------------------------------------------------------------------------- -var - PanningWindowClass: TWndClass = ( - style: 0; - lpfnWndProc: @DefWindowProc; - cbClsExtra: 0; - cbWndExtra: 0; - hInstance: 0; - hIcon: 0; - hCursor: 0; - hbrBackground: 0; - lpszMenuName: nil; - lpszClassName: 'VTPanningWindow' - ); +procedure TBaseVirtualTree.InternalDisconnectNode(Node: PVirtualNode; KeepFocus: Boolean; Reindex: Boolean = True; ParentClearing: Boolean = False); -procedure TBaseVirtualTree.StartWheelPanning(Position: TPoint); +// Disconnects the given node from its parent and siblings. The node's pointer are not reset so they can still be used +// after return from this method (probably a very short time only!). +// If KeepFocus is True then the focused node is not reset. This is useful if the given node is reconnected to the tree +// immediately after return of this method and should stay being the focused node if it was it before. +// Note: Node must not be nil or the root node. -// Called when wheel panning should start. A little helper window is created to indicate the reference position, -// which determines in which direction and how far wheel panning/scrolling will happen. +var + Parent, + Run: PVirtualNode; + Index: Integer; + AdjustHeight: Boolean; - //--------------- local function -------------------------------------------- +begin + Assert(Assigned(Node) and (Node <> FRoot), 'Node must neither be nil nor the root node.'); - function CreateClipRegion: HRGN; + if (Node = FFocusedNode) and not KeepFocus then + begin + DoFocusNode(nil, False); + DoFocusChange(FFocusedNode, FFocusedColumn); + end; - // In order to avoid doing all the transparent drawing ourselves we use a - // window region for the wheel window. - // Since we only work on a very small image (32x32 pixels) this is acceptable. + if Node = FRangeAnchor then + ResetRangeAnchor; - var - Start, X, Y: Integer; - Temp: HRGN; + // Update the hidden children flag of the parent. + if (Node.Parent <> FRoot) and not (ParentClearing) then + if FUpdateCount = 0 then + DetermineHiddenChildrenFlag(Node.Parent) + else + Include(FStates, tsUpdateHiddenChildrenNeeded); + if not (vsDeleting in Node.States) then begin - Assert(not FPanningImage.Empty, 'Invalid wheel panning image.'); + // Some states are only temporary so take them out. + Node.States := Node.States - [vsChecking]; + Parent := Node.Parent; + Dec(Parent.ChildCount); + AdjustHeight := (vsExpanded in Parent.States) and (vsVisible in Node.States); + if Parent.ChildCount = 0 then + begin + Parent.States := Parent.States - [vsAllChildrenHidden, vsHasChildren]; + if (Parent <> FRoot) and (vsExpanded in Parent.States) then + Exclude(Parent.States, vsExpanded); + end; + AdjustTotalCount(Parent, -Integer(Node.TotalCount), True); + if AdjustHeight then + AdjustTotalHeight(Parent, -Integer(Node.TotalHeight), True); + if FullyVisible[Node] then + Dec(FVisibleCount, CountVisibleChildren(Node) + Cardinal(IfThen(IsEffectivelyVisible[Node], 1))); - // Create an initial region on which we operate. - Result := CreateRectRgn(0, 0, 0, 0); - with FPanningImage, Canvas do + if Assigned(Node.PrevSibling) then + Node.PrevSibling.NextSibling := Node.NextSibling + else + Parent.FirstChild := Node.NextSibling; + + if Assigned(Node.NextSibling) then begin - for Y := 0 to Height - 1 do + Node.NextSibling.PrevSibling := Node.PrevSibling; + // Reindex all following nodes. + if Reindex then begin - Start := -1; - for X := 0 to Width - 1 do - begin - // Start a new span if we found a non-transparent pixel and no span is currently started. - if (Start = -1) and (Pixels[X, Y] <> clFuchsia) then - Start := X - else - if (Start > -1) and (Pixels[X, Y] = clFuchsia) then - begin - // A non-transparent span is finished. Add it to the result region. - Temp := CreateRectRgn(Start, Y, X, Y + 1); - CombineRgn(Result, Result, Temp, RGN_OR); - DeleteObject(Temp); - Start := -1; - end; - end; - // If there is an open span then add this also to the result region. - if Start > -1 then + Run := Node.NextSibling; + Index := Node.Index; + while Assigned(Run) do begin - Temp := CreateRectRgn(Start, Y, Width, Y + 1); - CombineRgn(Result, Result, Temp, RGN_OR); - DeleteObject(Temp); + Run.Index := Index; + Inc(Index); + Run := Run.NextSibling; end; end; - end; - // The resulting region is used as window region so we must not delete it. - // Windows will own it after the assignment below. + end + else + Parent.LastChild := Node.PrevSibling; end; +end; - //--------------- end local function ---------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.InternalRemoveFromSelection(Node: PVirtualNode); + +// Special version to mark a node to be no longer in the current selection. PackArray must +// be used to remove finally those entries. var - TempClass: TWndClass; - ClassRegistered: Boolean; - ImageName: string; - Pt: TPoint; + Index: Integer; begin - // Set both panning and scrolling flag. One will be removed shortly depending on whether the middle mouse button is - // released before the mouse is moved or vice versa. The first case is referred to as wheel scrolling while the - // latter is called wheel panning. - StopTimer(ScrollTimer); - DoStateChange([tsWheelPanning, tsWheelScrolling]); - - // Register the helper window class. - PanningWindowClass.hInstance := HInstance; - ClassRegistered := GetClassInfo(HInstance, PanningWindowClass.lpszClassName, TempClass); - if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then + // Because pointers are always DWORD aligned we can simply increment all those + // which we want to have removed (see also PackArray) and still have the + // order in the list preserved. + if FindNodeInSelection(Node, Index, -1, -1) then begin - if ClassRegistered then - Winapi.Windows.UnregisterClass(PanningWindowClass.lpszClassName, HInstance); - Winapi.Windows.RegisterClass(PanningWindowClass); + //sync path note: deselect when overlapping drawselection is made + Exclude(Node.States, vsSelected); + if SyncCheckstateWithSelection[Node] then + Node.CheckState := csUncheckedNormal; // Avoid using SetCheckState() as it handles toSyncCheckboxesWithSelection as well. + Inc(PAnsiChar(FSelection[Index])); + DoRemoveFromSelection(Node); + AdviseChangeEvent(False, Node, crIgnore); end; - // Create the helper window and show it at the given position without activating it. - Pt := ClientToScreen(Position); - FPanningWindow := CreateWindowEx(WS_EX_TOOLWINDOW, PanningWindowClass.lpszClassName, nil, WS_POPUP, Pt.X - 16, Pt.Y - 16, - 32, 32, Handle, 0, HInstance, nil); +end; - FPanningImage := TBitmap.Create; - if Integer(FRangeX) > ClientWidth then - begin - if Integer(FRangeY) > ClientHeight then - ImageName := 'VT_MOVEALL' - else - ImageName := 'VT_MOVEEW'; - end - else - ImageName := 'VT_MOVENS'; - FPanningImage.LoadFromResourceName(HInstance, ImageName); - SetWindowRgn(FPanningWindow, CreateClipRegion, False); +procedure TBaseVirtualTree.InternalSetFocusedColumn(const index: TColumnIndex); +begin + FFocusedColumn := index; +end; - {$ifdef CPUX64} - SetWindowLongPtr(FPanningWindow, GWLP_WNDPROC, LONG_PTR(System.Classes.MakeObjectInstance(PanningWindowProc))); - {$else} - SetWindowLong(FPanningWindow, GWL_WNDPROC, NativeInt(System.Classes.MakeObjectInstance(PanningWindowProc))); - {$endif CPUX64} - ShowWindow(FPanningWindow, SW_SHOWNOACTIVATE); +//---------------------------------------------------------------------------------------------------------------------- - // Setup the panscroll timer and capture all mouse input. - TrySetFocus(); - SetCapture(Handle); - SetTimer(Handle, ScrollTimer, 20, nil); +procedure TBaseVirtualTree.InvalidateCache; + +// Marks the cache as invalid. + +begin + DoStateChange([tsValidationNeeded], [tsUseCache]); + //ChangeTreeStatesAsync([csValidationNeeded], [csUseCache]); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.StopWheelPanning; +procedure TBaseVirtualTree.MarkCutCopyNodes; -// Stops panning if currently active and destroys the helper window. +// Sets the vsCutOrCopy style in every currently selected but not disabled node to indicate it is +// now part of a clipboard operation. var - Instance: Pointer; + Nodes: TNodeArray; + I: Integer; begin - if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then + Nodes := nil; + if FSelectionCount > 0 then begin - // Release the mouse capture and stop the panscroll timer. - StopTimer(ScrollTimer); - ReleaseCapture; - DoStateChange([], [tsWheelPanning, tsWheelScrolling]); - - // Destroy the helper window. - {$ifdef CPUX64} - Instance := Pointer(GetWindowLongPtr(FPanningWindow, GWLP_WNDPROC)); - {$else} - Instance := Pointer(GetWindowLong(FPanningWindow, GWL_WNDPROC)); - {$endif CPUX64} - DestroyWindow(FPanningWindow); - if Instance <> @DefWindowProc then - System.Classes.FreeObjectInstance(Instance); - FPanningWindow := 0; - FPanningImage.Free; - FPanningImage := nil; - DeleteObject(FPanningCursor); - FPanningCursor := 0; - Winapi.Windows.SetCursor(Screen.Cursors[Cursor]); + // need the current selection sorted to exclude selected nodes which are children, grandchildren etc. of + // already selected nodes + Nodes := GetSortedSelection(False); + for I := 0 to High(Nodes) do + with Nodes[I]^ do + if not (vsDisabled in States) then + Include(States, vsCutOrCopy); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.StructureChange(Node: PVirtualNode; Reason: TChangeReason); +procedure TBaseVirtualTree.Loaded; + +var + LastRootCount: Cardinal; + IsReadOnly: Boolean; begin - AdviseChangeEvent(True, Node, Reason); + inherited; - if FUpdateCount = 0 then + // Call RegisterDragDrop after all visual inheritance changes to MiscOptions have been applied. + if not (csDesigning in ComponentState) and (toAcceptOLEDrop in FOptions.MiscOptions) then + if HandleAllocated then + RegisterDragDrop(Handle, DragManager as IDropTarget); + + // If a root node count has been set during load of the tree then update its child structure now + // as this hasn't been done yet in this case. + if (tsNeedRootCountUpdate in FStates) and (FRoot.ChildCount > 0) then begin - if (FChangeDelay > 0) and HandleAllocated and not (tsSynchMode in FStates) then - SetTimer(Handle, StructureChangeTimer, FChangeDelay, nil) + DoStateChange([], [tsNeedRootCountUpdate]); + IsReadOnly := toReadOnly in FOptions.MiscOptions; + FOptions.InternalSetMiscOptions(FOptions.MiscOptions - [toReadOnly]); + LastRootCount := FRoot.ChildCount; + FRoot.ChildCount := 0; + BeginUpdate; + SetChildCount(FRoot, LastRootCount); + EndUpdate; + if IsReadOnly then + FOptions.InternalSetMiscOptions(FOptions.MiscOptions + [toReadOnly]); + end; + + // Prevent the object inspector at design time from marking the header as being modified + // when auto resize is enabled. + Updating; + try + TVTHeaderCracker(FHeader).UpdateMainColumn; + TVirtualTreeColumnsCracker(FHeader.Columns).FixPositions; + if toAutoBidiColumnOrdering in FOptions.AutoOptions then + TVirtualTreeColumnsCracker(FHeader.Columns).ReorderColumns(UseRightToLeftAlignment); + // Because of the special recursion and update stopper when creating the window (or resizing it) + // we have to manually trigger the auto size calculation here. + if hsNeedScaling in FHeader.States then + TVTHeaderCracker(FHeader).RescaleHeader else - DoStructureChange(Node, Reason); + TVTHeaderCracker(FHeader).RecalculateHeader; + if hoAutoResize in FHeader.Options then + TVirtualTreeColumnsCracker(FHeader.Columns).AdjustAutoSize(InvalidColumn, True); + finally + Updated; end; end; -function TBaseVirtualTree.StyleServices(AControl: TControl): TCustomStyleServices; +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.MainColumnChanged; + begin - if AControl = nil then - AControl := Self; - Result := VTStyleServices(AControl); + DoCancelEdit; + + if Assigned(FAccessibleItem) then + NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, Handle, OBJID_CLIENT, CHILDID_SELF); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.SuggestDropEffect(Source: TObject; Shift: TShiftState; Pt: TPoint; - AllowedEffects: Integer): Integer; +procedure TBaseVirtualTree.MouseMove(Shift: TShiftState; X, Y: Integer); -// determines the drop action to take if the drag'n drop operation ends on this tree -// Note: Source can be any Delphi object not just a virtual tree +var + R: TRect; begin - Result := AllowedEffects; + if tsNodeHeightTrackPending in FStates then + begin + // Remove hint if shown currently. + Application.CancelHint; - // prefer MOVE if source and target are the same control, otherwise whatever is allowed as initial value - if Assigned(Source) and (Source = Self) then - if (AllowedEffects and DROPEFFECT_MOVE) <> 0 then - Result := DROPEFFECT_MOVE - else // no change - else - // drag between different applicatons - if (AllowedEffects and DROPEFFECT_COPY) <> 0 then - Result := DROPEFFECT_COPY; + // Stop wheel panning if active. + StopWheelPanning; - // consider modifier keys and what is allowed at the moment, if none of the following conditions apply then - // the initial value just set is used - if ssCtrl in Shift then + // Stop timers + StopTimer(ExpandTimer); + StopTimer(EditTimer); + StopTimer(HeaderTimer); + StopTimer(ScrollTimer); + StopTimer(SearchTimer); + FSearchBuffer := ''; + FLastSearchNode := nil; + + DoStateChange([tsNodeHeightTracking], [tsScrollPending, tsScrolling, tsEditPending, tsOLEDragPending, tsVCLDragPending, + tsIncrementalSearching, tsNodeHeightTrackPending]); + end; + + if tsDrawSelPending in FStates then begin - // copy or link - if ssShift in Shift then - begin - // link - if (AllowedEffects and DROPEFFECT_LINK) <> 0 then - Result := DROPEFFECT_LINK; - end - else + // Remove current selection in case the user clicked somewhere in the window (but not a node) + // and moved the mouse. + if CalculateSelectionRect(X, Y) then begin - // copy - if (AllowedEffects and DROPEFFECT_COPY) <> 0 then - Result := DROPEFFECT_COPY; + InvalidateRect(Handle, @FNewSelRect, False); + UpdateWindow(Handle); + if (Abs(FNewSelRect.Right - FNewSelRect.Left) > Mouse.DragThreshold) or + (Abs(FNewSelRect.Bottom - FNewSelRect.Top) > Mouse.DragThreshold) then + begin + if tsClearPending in FStates then + begin + DoStateChange([], [tsClearPending]); + ClearSelection; + end; + DoStateChange([tsDrawSelecting], [tsDrawSelPending]); + + // Reset to main column for multiselection. + FocusedColumn := FHeader.MainColumn; + + // The current rectangle may already include some node captions. Handle this. + if HandleDrawSelection(X, Y) then + InvalidateRect(Handle, nil, False); + end; end; end else begin - // move, link or default - if ssShift in Shift then + if tsNodeHeightTracking in FStates then begin - // move - if (AllowedEffects and DROPEFFECT_MOVE) <> 0 then - Result := DROPEFFECT_MOVE; - end + // Handle height tracking. + if DoNodeHeightTracking(FHeightTrackNode, FHeightTrackColumn, TVTHeaderCracker(FHeader).GetShiftState, FHeightTrackPoint, Point(X, Y)) then + begin + // Avoid negative (or zero) node heights. + if FHeightTrackPoint.Y >= Y then + Y := FHeightTrackPoint.Y + 1; + SetNodeHeight(FHeightTrackNode, Y - FHeightTrackPoint.Y); + UpdateWindow(Handle); + Exit; + end; + end; + + // If both wheel panning and auto scrolling are pending then the user moved the mouse while holding down the + // middle mouse button. This means panning is being used, hence remove the wheel scroll flag. + if [tsWheelPanning, tsWheelScrolling] * FStates = [tsWheelPanning, tsWheelScrolling] then + begin + if ((Abs(FLastClickPos.X - X) >= Mouse.DragThreshold) or (Abs(FLastClickPos.Y - Y) >= Mouse.DragThreshold)) then + DoStateChange([], [tsWheelScrolling]); + end; + + // Really start dragging if the mouse has been moved more than the threshold. + if (tsOLEDragPending in FStates) and + ( + ((Abs(FLastClickPos.X - X) >= FDragThreshold) and (X > 0)) or // Check >0 to fix issue #833 + ((Abs(FLastClickPos.Y - Y) >= FDragThreshold) and (Y > 0)) + ) + then + DoDragging(FLastClickPos) else begin - // link or default - if ssAlt in Shift then + if CanAutoScroll then + DoAutoScroll(X, Y); + if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then + AdjustPanningCursor(X, Y); + if not IsMouseSelecting then begin - // link - if (AllowedEffects and DROPEFFECT_LINK) <> 0 then - Result := DROPEFFECT_LINK; + HandleHotTrack(X, Y); + inherited MouseMove(Shift, X, Y); + end + else + begin + // Handle draw selection if required, but don't do the work twice if the + // auto scrolling code already cares about the selection. + if not (tsScrolling in FStates) and CalculateSelectionRect(X, Y) then + begin + // If something in the selection changed then invalidate the entire + // tree instead trying to figure out the display rects of all changed nodes. + if HandleDrawSelection(X, Y) then + InvalidateRect(Handle, nil, False) + else + begin + UnionRect(R, OrderRect(FNewSelRect), OrderRect(FLastSelRect)); + OffsetRect(R, -FEffectiveOffsetX, FOffsetY); + InvalidateRect(Handle, @R, False); + end; + UpdateWindow(Handle); + end; end; - // else default end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.ToggleSelection(StartNode, EndNode: PVirtualNode); - -// Switchs the selection state of a range of nodes. -// Note: This method is specifically designed to help selecting ranges with the keyboard and considers therefore -// the range anchor. - -var - NodeFrom, - NodeTo: PVirtualNode; - NewSize: Integer; - Position: Integer; +procedure TBaseVirtualTree.Notification(AComponent: TComponent; Operation: TOperation); begin - if not FSelectionLocked then + if (AComponent <> Self) and (Operation = opRemove) then begin - Assert(Assigned(EndNode), 'EndNode must not be nil!'); - if StartNode = nil then - StartNode := FRoot.FirstChild - else - if not FullyVisible[StartNode] then - StartNode := GetPreviousVisible(StartNode, True); - - Position := CompareNodePositions(StartNode, EndNode); - // nothing to do if start and end node are the same - if Position <> 0 then + // Check for components linked to the tree. + if AComponent = FImages then begin - if Position < 0 then + Images := nil; + if not (csDestroying in ComponentState) then + Invalidate; + end + else + if AComponent = FStateImages then begin - NodeFrom := StartNode; - NodeTo := EndNode; + StateImages := nil; + if not (csDestroying in ComponentState) then + Invalidate; end else - begin - NodeFrom := EndNode; - NodeTo := StartNode; - end; + if AComponent = FCustomCheckImages then + begin + CustomCheckImages := nil; + FCheckImageKind := ckSystemDefault; + if not (csDestroying in ComponentState) then + Invalidate; + end + else + if AComponent = PopupMenu then + PopupMenu := nil + else + // Check for components linked to the header. + if Assigned(FHeader) then + begin + if AComponent = FHeader.Images then + FHeader.Images := nil + else + if AComponent = FHeader.PopupMenu then + FHeader.PopupMenu := nil; + end; + end; + inherited; +end; - ClearTempCache; +//---------------------------------------------------------------------------------------------------------------------- - // 1) toggle the start node if it is before the range anchor - if CompareNodePositions(NodeFrom, FRangeAnchor) < 0 then - if not (vsSelected in NodeFrom.States) then - InternalCacheNode(NodeFrom) - else - InternalRemoveFromSelection(NodeFrom); +procedure TBaseVirtualTree.OriginalWMNCPaint(DC: HDC); - // 2) toggle all nodes within the range - NodeFrom := GetNextVisible(NodeFrom, True); - while NodeFrom <> NodeTo do - begin - if not (vsSelected in NodeFrom.States) then - InternalCacheNode(NodeFrom) - else - InternalRemoveFromSelection(NodeFrom); - NodeFrom := GetNextVisible(NodeFrom, True); - end; +// Unfortunately, the painting for the non-client area in TControl is not always correct and does also not consider +// existing clipping regions, so it has been modified here to take this into account. - // 3) toggle end node if it is after the range anchor - if CompareNodePositions(NodeFrom, FRangeAnchor) > 0 then - if not (vsSelected in NodeFrom.States) then - InternalCacheNode(NodeFrom) - else - InternalRemoveFromSelection(NodeFrom); +const + InnerStyles: array[TBevelCut] of Integer = (0, BDR_SUNKENINNER, BDR_RAISEDINNER, 0); + OuterStyles: array[TBevelCut] of Integer = (0, BDR_SUNKENOUTER, BDR_RAISEDOUTER, 0); + EdgeStyles: array[TBevelKind] of Integer = (0, 0, BF_SOFT, BF_FLAT); + Ctl3DStyles: array[Boolean] of Integer = (BF_MONO, 0); - // Do some housekeeping if there was a change. - NewSize := PackArray(FSelection, FSelectionCount); - if NewSize > -1 then - begin - FSelectionCount := NewSize; - SetLength(FSelection, FSelectionCount); - end; - // If the range went over the anchor then we need to reselect it. - if not (vsSelected in FRangeAnchor.States) then - InternalCacheNode(FRangeAnchor); - if FTempNodeCount > 0 then - AddToSelection(FTempNodeCache, FTempNodeCount); - ClearTempCache; - end; - end; -end; +var + RC, RW: TRect; + EdgeSize: Integer; + Size: TSize; -procedure TBaseVirtualTree.TrySetFocus(); begin - if Visible and CanFocus then + if (BevelKind <> bkNone) or (BorderWidth > 0) then begin - try - Self.SetFocus(); - except - on EInvalidOperation do - Exit; + RC := Rect(0, 0, Width, Height); + Size := GetBorderDimensions; + InflateRect(RC, Size.cx, Size.cy); + + RW := RC; + + if BevelKind <> bkNone then + begin + DrawEdge(DC, RC, InnerStyles[BevelInner] or OuterStyles[BevelOuter], Byte(BevelEdges) or EdgeStyles[BevelKind] or + Ctl3DStyles[Ctl3D]); + + EdgeSize := 0; + if BevelInner <> bvNone then + Inc(EdgeSize, BevelWidth); + if BevelOuter <> bvNone then + Inc(EdgeSize, BevelWidth); + with TWithSafeRect(RC) do + begin + if beLeft in BevelEdges then + Inc(Left, EdgeSize); + if beTop in BevelEdges then + Inc(Top, EdgeSize); + if beRight in BevelEdges then + Dec(Right, EdgeSize); + if beBottom in BevelEdges then + Dec(Bottom, EdgeSize); + end; end; - end;//if + + // Repaint only the part in the original clipping region and not yet drawn parts. + IntersectClipRect(DC, RC.Left, RC.Top, RC.Right, RC.Bottom); + + // Determine inner rectangle to exclude (RC corresponds then to the client area). + InflateRect(RC, -Integer(BorderWidth), -Integer(BorderWidth)); + + // Remove the inner rectangle. + ExcludeClipRect(DC, RC.Left, RC.Top, RC.Right, RC.Bottom); + + // Erase parts not drawn. + Brush.Color := FColors.BorderColor; + Winapi.Windows.FillRect(DC, RW, Brush.Handle); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.UnselectNodes(StartNode, EndNode: PVirtualNode); +procedure TBaseVirtualTree.Paint; -// Deselects a range of nodes. -// EndNode must be visible while StartNode must not as in the case where the last focused node is the start node -// but it is a child of a node which has been collapsed previously. In this case the first visible parent node -// is used as start node. StartNode can be nil in which case the very first node in the tree is used. +// Window paint routine. Used when the tree window needs to be updated. var - NodeFrom, - NodeTo: PVirtualNode; - NewSize: Integer; + Window: TRect; + Target: TPoint; + Temp: Integer; + Options: TVTInternalPaintOptions; + RTLOffset: Integer; begin - if not FSelectionLocked then - begin - Assert(Assigned(EndNode), 'EndNode must not be nil!'); - if StartNode = nil then - StartNode := FRoot.FirstChild - else - if not FullyVisible[StartNode] then - begin - StartNode := GetPreviousVisible(StartNode, True); - if StartNode = nil then - StartNode := FRoot.FirstChild; - end; + Options := [poBackground, poColumnColor, poDrawFocusRect, poDrawDropMark, poDrawSelection, poGridLines]; + if UseRightToLeftAlignment and FHeader.UseColumns then + RTLOffset := ComputeRTLOffset(True) + else + RTLOffset := 0; - if CompareNodePositions(StartNode, EndNode) < 0 then + // The update rect has already been filled in WMPaint, as it is the window's update rect, which gets + // reset when BeginPaint is called (in the ancestor). + // The difference to the DC's clipbox is that it is also valid with internal paint operations used + // e.g. by the Explorer while dragging, but show window content while dragging is disabled. + if not IsRectEmpty(FUpdateRect) then + begin + Temp := Header.Columns.GetVisibleFixedWidth; + if Temp = 0 then begin - NodeFrom := StartNode; - NodeTo := EndNode; + Window := FUpdateRect; + Target := Window.TopLeft; + + // The clipping rectangle is given in client coordinates of the window. We have to convert it into + // a sliding window of the tree image. + OffsetRect(Window, FEffectiveOffsetX - RTLOffset, -FOffsetY); + PaintTree(Canvas, Window, Target, Options); end else begin - NodeFrom := EndNode; - NodeTo := StartNode; - end; + // First part, fixed columns + Window := ClientRect; + Window.Right := Temp; + Target := Window.TopLeft; - while NodeFrom <> NodeTo do - begin - InternalRemoveFromSelection(NodeFrom); - NodeFrom := GetNextVisible(NodeFrom, True); - end; - // Deselect last node too. - InternalRemoveFromSelection(NodeFrom); + OffsetRect(Window, -RTLOffset, -FOffsetY); + PaintTree(Canvas, Window, Target, Options); - // Do some housekeeping. - NewSize := PackArray(FSelection, FSelectionCount); - if NewSize > -1 then - begin - FSelectionCount := NewSize; - SetLength(FSelection, FSelectionCount); + // Second part, other columns + Window := GetClientRect; + + if Temp > Window.Right then + Exit; + + Window.Left := Temp; + Target := Window.TopLeft; + + OffsetRect(Window, FEffectiveOffsetX - RTLOffset, -FOffsetY); + PaintTree(Canvas, Window, Target, Options); end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.UpdateColumnCheckState(Col: TVirtualTreeColumn); +procedure TBaseVirtualTree.PaintCheckImage(Canvas: TCanvas; const ImageInfo: TVTImageInfo; Selected: Boolean); +var + ForegroundColor: COLORREF; + R: TRect; + Details, lSizeDetails: TThemedElementDetails; + lSize: TSize; + Theme: HTHEME; begin - Col.CheckState := DetermineNextCheckState(Col.CheckType, Col.CheckState); + with ImageInfo do + begin + if (tsUseThemes in FStates) and (FCheckImageKind = ckSystemDefault) then + begin + Details.Element := teButton; + case Index of + // ctRadioButton + 1 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedNormal); + 2 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedHot); + 3 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedPressed); + 4 : Details := StyleServices.GetElementDetails(tbRadioButtonUncheckedDisabled); + 5 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedNormal); + 6 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedHot); + 7 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedPressed); + 8 : Details := StyleServices.GetElementDetails(tbRadioButtonCheckedDisabled); + // ct(TriState)CheckBox + 9 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal); + 10 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedHot); + 11 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedPressed); + 12 : Details := StyleServices.GetElementDetails(tbCheckBoxUncheckedDisabled); + 13 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedNormal); + 14 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedHot); + 15 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedPressed); + 16 : Details := StyleServices.GetElementDetails(tbCheckBoxCheckedDisabled); + 17 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedNormal); + 18 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedHot); + 19 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedPressed); + 20 : Details := StyleServices.GetElementDetails(tbCheckBoxMixedDisabled); + // ctButton + ckButtonNormal: Details := StyleServices.GetElementDetails(tbPushButtonNormal); + ckButtonHot: Details := StyleServices.GetElementDetails(tbPushButtonHot); + ckButtonPressed: Details := StyleServices.GetElementDetails(tbPushButtonPressed); + ckButtonDisabled: Details := StyleServices.GetElementDetails(tbPushButtonDisabled); + else + Details := StyleServices.GetElementDetails(tbButtonRoot); + end; + if StyleServices.IsSystemStyle {and not (Index in [ckButtonNormal..ckButtonDisabled])} then + begin + Theme := OpenThemeData(Handle, 'BUTTON'); + GetThemePartSize(Theme, Canvas.Handle, Details.Part, Details.State, nil, TS_TRUE, lSize); + if (Index in [ckButtonNormal..ckButtonDisabled]) then begin + lSizeDetails := StyleServices.GetElementDetails(tbCheckBoxCheckedNormal); // Size of dropdown button should be based on size of checkboxes + GetThemePartSize(Theme, Canvas.Handle, lSizeDetails.Part, lSizeDetails.State, nil, TS_TRUE, lSize); + // dropdown buttons should be slightly larger than checkboxes, see issue #887 + lSize.cx := Round(lSize.cx * 1.15); + lSize.cy := Round(lSize.cy * 1.1); + end; + R := Rect(XPos, YPos, XPos + lSize.cx, YPos + lSize.cy); + if (Index in [ckButtonNormal..ckButtonDisabled]) then + R.Offset(-1, 0); // Eliminate 1 pixel border around Windows themed button + DrawThemeBackground(Theme, Canvas.Handle, Details.Part, Details.State, R, nil); + CloseThemeData(Theme); + end + else + begin + if (Index in [ckButtonNormal..ckButtonDisabled]) or not StyleServices.GetElementSize(Canvas.Handle, Details, TElementSize.esActual, lSize{$IF CompilerVersion >= 34}, CurrentPPI{$IFEND}) then begin + // radio buttons fail in RAD Studio 10 Seattle and lower, fallback to checkbox images. See issue #615 + if not StyleServices.GetElementSize(Canvas.Handle, StyleServices.GetElementDetails(tbCheckBoxUncheckedNormal), TElementSize.esActual, lSize{$IF CompilerVersion >= 34}, CurrentPPI{$IFEND}) then + lSize := TSize.Create(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK)); + end;//if + R := Rect(XPos, YPos, XPos + lSize.cx, YPos + lSize.cy); + StyleServices.DrawElement(Canvas.Handle, Details, R {$IF CompilerVersion >= 34}, nil, FCurrentPPI{$IFEND}); + Canvas.Refresh; // Every time you give a Canvas.Handle away to some other code you can't control you have to call Canvas.Refresh afterwards because the Canvas object and the HDC can be out of sync. + end; + if (Index in [ckButtonNormal..ckButtonDisabled]) then begin + Canvas.Pen.Color := clGray; + // These constants have been determined by test using various themes and dpi-scalings + DrawArrow(Canvas, TScrollDirection.sdDown, Point(R.Left + Round(lSize.cx * 0.22), R.Top + Round(lSize.cy * 0.33)), Round(lSize.cx *0.28)); + end;//if + end + else + with FCheckImages do + begin + if Selected and not Ghosted then + begin + if Focused or (toPopupMode in FOptions.PaintOptions) then + ForegroundColor := ColorToRGB(FColors.FocusedSelectionColor) + else + ForegroundColor := ColorToRGB(FColors.UnfocusedSelectionColor); + end + else + ForegroundColor := GetRGBColor(BlendColor); + + ImageList_DrawEx(Handle, Index, Canvas.Handle, XPos, YPos, 0, 0, GetRGBColor(BkColor), ForegroundColor, + ILD_TRANSPARENT); + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.UpdateDesigner; +procedure TBaseVirtualTree.PaintImage(var PaintInfo: TVTPaintInfo; ImageInfoIndex: TVTImageInfoIndex; DoOverlay: Boolean); +const + Style: array[TImageType] of Cardinal = (0, ILD_MASK); var - ParentForm: TCustomForm; - + ExtraStyle: Cardinal; + CutNode: Boolean; + PaintFocused: Boolean; + DrawEnabled: Boolean; + CustomOverlayDrawing: Boolean; // False if the built-in overloay drawing of TImageList should be used, True if custom drawing should take place. begin - if (csDesigning in ComponentState) and not (csUpdating in ComponentState) then + with PaintInfo do begin - ParentForm := GetParentForm(Self); - if Assigned(ParentForm) and Assigned(ParentForm.Designer) then - ParentForm.Designer.Modified; + CutNode := (vsCutOrCopy in Node.States) and (tsCutPending in FStates); + PaintFocused := Focused or (toGhostedIfUnfocused in FOptions.PaintOptions); + + // Since the overlay image must be specified together with the image to draw + // it is meaningfull to retrieve it in advance. + if DoOverlay then + GetImageIndex(PaintInfo, ikOverlay, iiOverlay) + else + PaintInfo.ImageInfo[iiOverlay].Index := -1; + + DrawEnabled := not (vsDisabled in Node.States) and Enabled; + with ImageInfo[ImageInfoIndex] do + begin + if (vsSelected in Node.States) and not(Ghosted or CutNode) then + begin + if PaintFocused or (toPopupMode in FOptions.PaintOptions) then + Images.BlendColor := FColors.FocusedSelectionColor + else + Images.BlendColor := FColors.UnfocusedSelectionColor; + end + else + Images.BlendColor := Color; + + ExtraStyle := ILD_TRANSPARENT; + // If the user returned an index >= 15 then we cannot use the built-in overlay image drawing. + // Instead we do it manually. Also of the image list of the normal and the overlay icon is different, + // we can't use the built-in drawing. See issue #779. + if (ImageInfo[iiOverlay].Index > -1) then begin + CustomOverlayDrawing := (ImageInfo[iiOverlay].Index >= 15) or (ImageInfo[iiOverlay].Images <> ImageInfo[iiNormal].Images); + if not CustomOverlayDrawing then + ExtraStyle := ILD_TRANSPARENT or ILD_OVERLAYMASK and IndexToOverlayMask(ImageInfo[iiOverlay].Index + 1); + end + else + CustomOverlayDrawing := False; + + // Blend image if enabled and the tree has the focus (or ghosted images must be drawn also if unfocused) ... + if (toUseBlendedImages in FOptions.PaintOptions) and PaintFocused + // ... and the image is ghosted... + and (Ghosted or + // ... or it is not the check image and the node is selected (but selection is not for the entire row)... + ((vsSelected in Node.States) and + not (toFullRowSelect in FOptions.SelectionOptions) and + not (toGridExtensions in FOptions.MiscOptions)) or + // ... or the node must be shown in cut mode. + CutNode) then + ExtraStyle := ExtraStyle or ILD_BLEND50; + + if (vsSelected in Node.States) and not Ghosted then + Images.BlendColor := clDefault; + + DrawImage(Images, Index, Canvas, XPos, YPos, Style[Images.ImageType] or ExtraStyle, DrawEnabled); + + // Now, draw the overlay. This circumnavigates limitations in the overlay mask index (it has to be 4 bits in size, + // anything larger will be truncated by the ILD_OVERLAYMASK). + // However this will only be done if the overlay image index is > 15, to avoid breaking code that relies + // on overlay image indices (e.g. when using system image lists). + if CustomOverlayDrawing then begin + ExtraStyle := ExtraStyle and not ILD_BLEND50; // Fixes issue #551 + // Note: XPos and YPos are those of the normal images. + DrawImage(ImageInfo[iiOverlay].Images, ImageInfo[iiOverlay].Index, Canvas, XPos, YPos, + Style[ImageInfo[iiOverlay].Images.ImageType] or ExtraStyle, DrawEnabled); + end;//if + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.UpdateHeaderRect(); - -// Calculates the rectangle the header occupies in non-client area. -// These coordinates are in window rectangle. +procedure TBaseVirtualTree.PaintNodeButton(Canvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; const R: TRect; + ButtonX, ButtonY: Integer; BidiMode: TBiDiMode); var - OffsetX, - OffsetY: Integer; - EdgeSize: Integer; - Size: TSize; + Bitmap: TBitmap; + XPos: Integer; + IsHot: Boolean; + IsSelected : boolean; + Theme: HTHEME; + Glyph: Integer; + State: Integer; + Pos: TRect; begin - FHeaderRect := Rect(0, 0, Width, Height); - - // Consider borders... - if HandleAllocated then begin // Prevent preliminary creation of window handle, see issue #933 - Size := GetBorderDimensions(); - InflateRect(FHeaderRect, Size.cx, Size.cy); - end; - - // ... and bevels. - OffsetX := BorderWidth; - OffsetY := BorderWidth; - if BevelKind <> bkNone then - begin - EdgeSize := 0; - if BevelInner <> bvNone then - Inc(EdgeSize, BevelWidth); - if BevelOuter <> bvNone then - Inc(EdgeSize, BevelWidth); - if beLeft in BevelEdges then - Inc(OffsetX, EdgeSize); - if beTop in BevelEdges then - Inc(OffsetY, EdgeSize); - end; + IsHot := (FCurrentHotNode = Node) and FHotNodeButtonHit; + IsSelected := (vsSelected in Node.States); - InflateRect(FHeaderRect, -OffsetX, -OffsetY); + // Draw the node's plus/minus button according to the directionality. + if BidiMode = bdLeftToRight then + XPos := R.Left + ButtonX + else + XPos := R.Right - ButtonX - FPlusBM.Width; - if hoVisible in FHeader.Options then + if (tsUseExplorerTheme in FStates) and not VclStyleEnabled then begin - if FHeaderRect.Left <= FHeaderRect.Right then - FHeaderRect.Bottom := FHeaderRect.Top + Integer(FHeader.Height) - else - FHeaderRect := Rect(0, 0, 0, 0); + Glyph := IfThen(IsHot, TVP_HOTGLYPH, TVP_GLYPH); + State := IfThen(vsExpanded in Node.States, GLPS_OPENED, GLPS_CLOSED); + Pos := Rect(XPos, R.Top + ButtonY, XPos + FPlusBM.Width, R.Top + ButtonY + FPlusBM.Height); + Theme := OpenThemeData(Handle, 'TREEVIEW'); + DrawThemeBackground(Theme, Canvas.Handle, Glyph, State, Pos, nil); + CloseThemeData(Theme); end else - FHeaderRect.Bottom := FHeaderRect.Top; + begin + if vsExpanded in Node.States then + begin + if IsHot then + begin + if IsSelected then + BitMap := FSelectedHotMinusBM + else + Bitmap := FHotMinusBM; + end + else + Bitmap := FMinusBM; + end + else + begin + if IsHot then + begin + if IsSelected then + BitMap := FSelectedHotPlusBM + else + Bitmap := FHotPlusBM; + end + else + Bitmap := FPlusBM; + end; + // Need to draw this masked. + Canvas.Draw(XPos, R.Top + ButtonY, Bitmap); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.UpdateEditBounds; - -// Used to update the bounds of the current node editor if editing is currently active. +procedure TBaseVirtualTree.PaintTreeLines(const PaintInfo: TVTPaintInfo; IndentSize: Integer; const LineImage: TLineImage); var - R: TRect; - CurrentAlignment: TAlignment; - CurrentBidiMode: TBidiMode; - offsets : TVTOffsets; - offset : Integer; + I: Integer; + XPos, + Offset: Integer; + NewStyles: TLineImage; begin - if (tsEditing in FStates) and Assigned(FFocusedNode) and - (FEditColumn < FHeader.Columns.Count) then // prevent EArgumentOutOfRangeException + NewStyles := nil; + + with PaintInfo do begin - if (GetCurrentThreadId <> MainThreadID) then + if BidiMode = bdLeftToRight then begin - // UpdateEditBounds() will be called at the end of the thread - Exit; + XPos := CellRect.Left + PaintInfo.Offsets[ofsMargin]; + Offset := FIndent; + end + else + begin + Offset := -Integer(FIndent); + XPos := CellRect.Right - PaintInfo.Offsets[ofsMargin] + Offset; end; - if vsMultiline in FFocusedNode.States then - R := GetDisplayRect(FFocusedNode, FEditColumn, True, False) - else if not (toGridExtensions in FOptions.MiscOptions) then - R := GetDisplayRect(FFocusedNode, FEditColumn, True, True); - if (toGridExtensions in FOptions.MiscOptions) then - begin - // Use the whole cell when grid extensions are on. - R := GetDisplayRect(FFocusedNode, FEditColumn, False, False); - if FEditColumn = FHeader.MainColumn then - begin - // Calculate an offset for the main column. - GetOffsets(FFocusedNode, offsets, ofsLabel, FEditColumn); - offset := offsets[ofsLabel]; -// if offsets[ofsToggleButton] < 0 then -// Inc(offset, offsets[ofsToggleButton]); - end - else - offset := 0; + case FLineMode of + lmBands: + if poGridLines in PaintInfo.PaintOptions then + begin + // Convert the line images in correct bands. + SetLength(NewStyles, Length(LineImage)); + for I := IndentSize - 1 downto 0 do + begin + if (vsExpanded in Node.States) and not (vsAllChildrenHidden in Node.States) then + NewStyles[I] := ltLeft + else + case LineImage[I] of + ltRight, + ltBottomRight, + ltTopDownRight, + ltTopRight: + NewStyles[I] := ltLeftBottom; + ltNone: + // Have to take over the image to the right of this one. A no line entry can never appear as + // last entry so I don't need an end check here. + if LineImage[I + 1] in [ltNone, ltTopRight] then + NewStyles[I] := NewStyles[I + 1] + else + NewStyles[I] := ltLeft; + ltTopDown: + // Have to check the image to the right of this one. A top down line can never appear as + // last entry so I don't need an end check here. + if LineImage[I + 1] in [ltNone, ltTopRight] then + NewStyles[I] := NewStyles[I + 1] + else + NewStyles[I] := ltLeft; + end; + end; - // Adjust edit bounds depending on alignment and bidi mode. - if FEditColumn <= NoColumn then - begin - CurrentAlignment := Alignment; - CurrentBidiMode := BiDiMode; - end - else - begin - CurrentAlignment := FHeader.Columns[FEditColumn].Alignment; - CurrentBidiMode := FHeader.Columns[FEditColumn].BiDiMode; - end; - // Consider bidi mode here. In RTL context does left alignment actually mean right alignment and vice versa. - if CurrentBidiMode <> bdLeftToRight then - ChangeBiDiModeAlignment(CurrentAlignment); - if CurrentAlignment = taLeftJustify then - begin - if CurrentBiDiMode = bdLeftToRight then - Inc(R.Left, offset) - else - Dec(R.Right, offset); - end - else + PaintInfo.Canvas.Font.Color := FColors.GridLineColor; + for I := 0 to IndentSize - 1 do + begin + DoBeforeDrawLineImage(PaintInfo.Node, I + Ord(not (toShowRoot in TreeOptions.PaintOptions)), XPos); + DrawLineImage(PaintInfo, XPos, CellRect.Top, NodeHeight[Node] - 1, VAlign - 1, NewStyles[I], + BidiMode <> bdLeftToRight); + Inc(XPos, Offset); + end; + end; + else // lmNormal + PaintInfo.Canvas.Font.Color := FColors.TreeLineColor; + for I := 0 to IndentSize - 1 do begin - if CurrentBiDiMode = bdLeftToRight then - Inc(R.Left, offset) - else - Dec(R.Right, offset); + DoBeforeDrawLineImage(PaintInfo.Node, I + Ord(not (toShowRoot in TreeOptions.PaintOptions)), XPos); + DrawLineImage(PaintInfo, XPos, CellRect.Top, NodeHeight[Node], VAlign - 1, LineImage[I], + BidiMode <> bdLeftToRight); + Inc(XPos, Offset); end; end; - if toShowHorzGridLines in TreeOptions.PaintOptions then - Dec(R.Bottom); - R.Bottom := R.Top + R.Bottom - R.Top; - FEditLink.SetBounds(R); end; end; //---------------------------------------------------------------------------------------------------------------------- -const - ScrollMasks: array[Boolean] of Cardinal = (0, SIF_DISABLENOSCROLL); +procedure TBaseVirtualTree.PaintSelectionRectangle(Target: TCanvas; WindowOrgX: Integer; const SelectionRect: TRect; + TargetRect: TRect); -const // Region identifiers for GetRandomRgn - CLIPRGN = 1; - METARGN = 2; - APIRGN = 3; - SYSRGN = 4; +// Helper routine to draw a selection rectangle in the mode determined by DrawSelectionMode. -function GetRandomRgn(DC: HDC; Rgn: HRGN; iNum: Integer): Integer; stdcall; external 'GDI32.DLL'; +var + BlendRect: TRect; + TextColorBackup, + BackColorBackup: COLORREF; // used to restore forground and background colors when drawing a selection rectangle -procedure TBaseVirtualTree.UpdateWindowAndDragImage(const Tree: TBaseVirtualTree; TreeRect: TRect; UpdateNCArea, - ReshowDragImage: Boolean); +begin + if ((FDrawSelectionMode = smDottedRectangle) and not (tsUseThemes in FStates)) then + begin + // Classical selection rectangle using dotted borderlines. + TextColorBackup := GetTextColor(Target.Handle); + SetTextColor(Target.Handle, $FFFFFF); + BackColorBackup := GetBkColor(Target.Handle); + SetBkColor(Target.Handle, 0); + Target.DrawFocusRect(SelectionRect); + SetTextColor(Target.Handle, TextColorBackup); + SetBkColor(Target.Handle, BackColorBackup); + end + else + begin + // Modern alpha blended style. + OffsetRect(TargetRect, WindowOrgX, 0); + if IntersectRect(BlendRect, OrderRect(SelectionRect), TargetRect) then + begin + OffsetRect(BlendRect, -WindowOrgX, 0); + AlphaBlend(0, Target.Handle, BlendRect, Point(0, 0), bmConstantAlphaAndColor, FSelectionBlendFactor, + ColorToRGB(FColors.SelectionRectangleBlendColor)); -// Method to repaint part of the window area which is not covered by the drag image and to initiate a recapture -// of the drag image. -// Note: This method must only be called during a drag operation and the tree passed in is the one managing the current -// drag image (so it is the actual drag source). + Target.Brush.Color := FColors.SelectionRectangleBorderColor; + Target.FrameRect(SelectionRect); + end; + end; +end; -var - DragRegion, // the region representing the drag image - UpdateRegion, // the unclipped region within the tree to be updated - NCRegion: HRGN; // the region representing the non-client area of the tree - DragRect, - NCRect: TRect; - RedrawFlags: Cardinal; +//---------------------------------------------------------------------------------------------------------------------- - VisibleTreeRegion: HRGN; +procedure TBaseVirtualTree.PanningWindowProc(var Message: TMessage); - DC: HDC; +var + PS: TPaintStruct; + Canvas: TCanvas; - //This function was originally designed only for tree's drag image. But we modified - //it for reusing it with header's drag image too for solving issue 248. - useDragImage: TVTDragImage; begin - if IntersectRect(TreeRect, TreeRect, ClientRect) then + if Message.Msg = WM_PAINT then begin - // Retrieve the visible region of the window. This is important to avoid overpainting parts of other windows - // which overlap this one. - VisibleTreeRegion := CreateRectRgn(0, 0, 1, 1); - DC := GetDCEx(Handle, 0, DCX_CACHE or DCX_WINDOW or DCX_CLIPSIBLINGS or DCX_CLIPCHILDREN); - GetRandomRgn(DC, VisibleTreeRegion, SYSRGN); - ReleaseDC(Handle, DC); - - //Take proper drag image depending on whether the drag is being done in the tree - //or in the header. - useDragImage := Tree.FDragImage; - if (not useDragImage.Visible) - and (Tree.FHeader.DragImage <> nil) and (Tree.FHeader.DragImage.Visible) - then - useDragImage := Tree.FHeader.DragImage; + BeginPaint(FPanningWindow, PS); + Canvas := TCanvas.Create; + Canvas.Handle := PS.hdc; + try + Canvas.Draw(0, 0, FPanningImage); + finally + Canvas.Handle := 0; + Canvas.Free; + EndPaint(FPanningWindow, PS); + end; + Message.Result := 0; + end + else + with Message do + Result := DefWindowProc(FPanningWindow, Msg, wParam, lParam); +end; - // The drag image will figure out itself what part of the rectangle can be recaptured. - // Recapturing is not done by taking a snapshot of the screen, but by letting the tree draw itself - // into the back bitmap of the drag image. So the order here is unimportant. - useDragImage.RecaptureBackground(Self, TreeRect, VisibleTreeRegion, UpdateNCArea, ReshowDragImage); +//---------------------------------------------------------------------------------------------------------------------- - // Calculate the screen area not covered by the drag image and which needs an update. - DragRect := useDragImage.GetDragImageRect; - MapWindowPoints(0, Handle, DragRect, 2); - DragRegion := CreateRectRgnIndirect(DragRect); +procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, MaxWidth: Integer); - // Start with non-client area if requested. - if UpdateNCArea then - begin - // Compute the part of the non-client area which must be updated. +// This method is called immediately before a cell's content is drawn und is responsible to paint selection colors etc. - // Determine the outer rectangle of the entire tree window. - GetWindowRect(Handle, NCRect); - // Express the tree window rectangle in client coordinates (because RedrawWindow wants them so). - MapWindowPoints(0, Handle, NCRect, 2); - NCRegion := CreateRectRgnIndirect(NCRect); - // Determine client rect in screen coordinates and create another region for it. - UpdateRegion := CreateRectRgnIndirect(ClientRect); - // Create a region which only contains the NC part by subtracting out the client area. - CombineRgn(NCRegion, NCRegion, UpdateRegion, RGN_DIFF); - // Subtract also out what is hidden by the drag image. - CombineRgn(NCRegion, NCRegion, DragRegion, RGN_DIFF); - RedrawWindow(Handle, nil, NCRegion, RDW_FRAME or RDW_NOERASE or RDW_NOCHILDREN or RDW_INVALIDATE or RDW_VALIDATE or - RDW_UPDATENOW); - DeleteObject(NCRegion); - DeleteObject(UpdateRegion); - end; +var + TextColorBackup, + BackColorBackup: COLORREF; + FocusRect, + InnerRect: TRect; + RowRect: TRect; + Theme: HTHEME; +const + TREIS_HOTSELECTED = 6; - UpdateRegion := CreateRectRgnIndirect(TreeRect); - RedrawFlags := RDW_INVALIDATE or RDW_VALIDATE or RDW_UPDATENOW or RDW_NOERASE or RDW_NOCHILDREN; - // Remove the part of the update region which is covered by the drag image. - CombineRgn(UpdateRegion, UpdateRegion, DragRegion, RGN_DIFF); - RedrawWindow(Handle, nil, UpdateRegion, RedrawFlags); - DeleteObject(UpdateRegion); - DeleteObject(DragRegion); - DeleteObject(VisibleTreeRegion); - end; -end; + //--------------- local functions ------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- + procedure AlphaBlendSelection(Color: TColor); -procedure TBaseVirtualTree.ValidateCache(); + var + R: TRect; -// Starts cache validation if not already done by adding this instance to the worker thread's waiter list -// (if not already there) and signalling the thread it can start validating. + begin + // Take into account any window offset and size limitations in the target bitmap, as this is only as large + // as necessary and might not cover the whole node. For normal painting this does not matter (because of + // clipping) but for the MMX code there is no such check and it will crash badly when bitmap boundaries are + // crossed. + R := InnerRect; + OffsetRect(R, -WindowOrgX, 0); + if R.Left < 0 then + R.Left := 0; + if R.Right > MaxWidth then + R.Right := MaxWidth; + AlphaBlend(0, PaintInfo.Canvas.Handle, R, Point(0, 0), bmConstantAlphaAndColor, + FSelectionBlendFactor, ColorToRGB(Color)); + end; -begin - // stop validation if it is currently validating this tree's cache. - InterruptValidation(); + //--------------------------------------------------------------------------- - FStartIndex := 0; - if (tsValidationNeeded in FStates) and (FVisibleCount > CacheThreshold) then + procedure DrawBackground(State: Integer); begin - // Tell the thread this tree needs actually something to do. - TWorkerThread.AddTree(Self); + // if the full row selection is disabled or toGridExtensions is in the MiscOptions, draw the selection + // into the InnerRect, otherwise into the RowRect + if not (toFullRowSelect in FOptions.SelectionOptions) or (toGridExtensions in FOptions.MiscOptions) then + DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, TVP_TREEITEM, State, InnerRect, nil) + else + DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, TVP_TREEITEM, State, RowRect, nil); end; -end; -//---------------------------------------------------------------------------------------------------------------------- + procedure DrawThemedFocusRect(State: Integer); + var + Theme: HTHEME; + begin + Theme := OpenThemeData(Application.ActiveFormHandle, 'Explorer::ItemsView'); + if not (toFullRowSelect in FOptions.SelectionOptions) or (toGridExtensions in FOptions.MiscOptions) then + DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, LVP_LISTDETAIL, State, InnerRect, nil) + else + DrawThemeBackground(Theme, PaintInfo.Canvas.Handle, LVP_LISTDETAIL, State, RowRect, nil); + CloseThemeData(Theme); + end; -procedure TBaseVirtualTree.ValidateNodeDataSize(var Size: Integer); + //--------------- end local functions --------------------------------------- begin - Size := SizeOf(Pointer); - if Assigned(FOnGetNodeDataSize) then - FOnGetNodeDataSize(Self, Size); -end; + if tsUseExplorerTheme in FStates then + begin + Theme := OpenThemeData(Application.ActiveFormHandle, 'Explorer::TreeView'); + RowRect := Rect(0, PaintInfo.CellRect.Top, FRangeX, PaintInfo.CellRect.Bottom); + if (Header.Columns.Count = 0) and (toFullRowSelect in TreeOptions.SelectionOptions) then + RowRect.Right := Max(ClientWidth, RowRect.Right); + if toShowVertGridLines in FOptions.PaintOptions then + Dec(RowRect.Right); + end; -//---------------------------------------------------------------------------------------------------------------------- + with PaintInfo, Canvas do + begin + // Fill cell background if its color differs from tree background. + with FHeader.Columns do + if poColumnColor in PaintOptions then + begin + Brush.Color := Items[Column].GetEffectiveColor; + FillRect(CellRect); + end; -procedure TBaseVirtualTree.VclStyleChanged(); + // Let the application customize the cell background and the content rectangle. + DoBeforeCellPaint(Canvas, Node, Column, cpmPaint, CellRect, ContentRect); - // Updates the member FVclStyleEnabled, should be called initially and when the VCL style changes + InnerRect := ContentRect; -begin - FVclStyleEnabled := StyleServices.Enabled and not StyleServices.IsSystemStyle and not (csDesigning in ComponentState); - Header.StyleChanged(); -end; + // The selection rectangle depends on alignment. + if not (toGridExtensions in FOptions.MiscOptions) then + begin + case Alignment of + taLeftJustify: + with TWithSafeRect(InnerRect) do + if Left + NodeWidth < Right then + Right := Left + NodeWidth; + taCenter: + with TWithSafeRect(InnerRect) do + if (Right - Left) > NodeWidth then + begin + Left := (Left + Right - NodeWidth) div 2; + Right := Left + NodeWidth; + end; + taRightJustify: + with TWithSafeRect(InnerRect) do + if (Right - Left) > NodeWidth then + Left := Right - NodeWidth; + end; + end; + + if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.SelectionOptions) then + begin + // Fill the selection rectangle. + if poDrawSelection in PaintOptions then + begin + if Node = FDropTargetNode then + begin + if (FLastDropMode = dmOnNode) or (vsSelected in Node.States) then + begin + Brush.Color := FColors.DropTargetColor; + Pen.Color := FColors.DropTargetBorderColor; + + if (toGridExtensions in FOptions.MiscOptions) or + (toFullRowSelect in FOptions.SelectionOptions) then + InnerRect := CellRect; + if not IsRectEmpty(InnerRect) then + if tsUseExplorerTheme in FStates then + DrawBackground(TREIS_SELECTED) + else + if (toUseBlendedSelection in FOptions.PaintOptions) then + AlphaBlendSelection(Brush.Color) + else + with TWithSafeRect(InnerRect) do + RoundRect(Left, Top, Right, Bottom, FSelectionCurveRadius, FSelectionCurveRadius); + end + else + begin + Brush.Style := bsClear; + end; + end + else + if vsSelected in Node.States then + begin + if Focused or (toPopupMode in FOptions.PaintOptions) then + begin + Brush.Color := FColors.FocusedSelectionColor; + Pen.Color := FColors.FocusedSelectionBorderColor; + end + else + begin + Brush.Color := FColors.UnfocusedSelectionColor; + Pen.Color := FColors.UnfocusedSelectionBorderColor; + end; + if (toGridExtensions in FOptions.MiscOptions) or (toFullRowSelect in FOptions.SelectionOptions) then + InnerRect := CellRect; + if not IsRectEmpty(InnerRect) then + if tsUseExplorerTheme in FStates then + begin + // If the node is also hot, its background will be drawn later. + if not (toHotTrack in FOptions.PaintOptions) or (Node <> FCurrentHotNode) or + ((Column <> FCurrentHotColumn) and not (toFullRowSelect in FOptions.SelectionOptions)) then + DrawBackground(IfThen(Self.Focused, TREIS_SELECTED, TREIS_SELECTEDNOTFOCUS)); + end + else + if (toUseBlendedSelection in FOptions.PaintOptions) then + AlphaBlendSelection(Brush.Color) + else + with TWithSafeRect(InnerRect) do + RoundRect(Left, Top, Right, Bottom, FSelectionCurveRadius, FSelectionCurveRadius); + end; + end; + end; -//---------------------------------------------------------------------------------------------------------------------- + if (tsUseExplorerTheme in FStates) and (toHotTrack in FOptions.PaintOptions) and (Node = FCurrentHotNode) and + ((Column = FCurrentHotColumn) or (toFullRowSelect in FOptions.SelectionOptions)) then + DrawBackground(IfThen((vsSelected in Node.States) and not (toAlwaysHideSelection in FOptions.PaintOptions), + TREIS_HOTSELECTED, TREIS_HOT)); -//PROFILE-NO -procedure TBaseVirtualTree.WndProc(var Message: TMessage); + if (Column = FFocusedColumn) or (toFullRowSelect in FOptions.SelectionOptions) then + begin + // draw focus rect + if (poDrawFocusRect in PaintOptions) and + (Focused or (toPopupMode in FOptions.PaintOptions)) and (FFocusedNode = Node) and + ( (Column = FFocusedColumn) or + ((not (toExtendedFocus in FOptions.SelectionOptions) or IsWinVistaOrAbove) and + (toFullRowSelect in FOptions.SelectionOptions) and + (tsUseExplorerTheme in FStates) ) ) then + begin + TextColorBackup := GetTextColor(Handle); + SetTextColor(Handle, $FFFFFF); + BackColorBackup := GetBkColor(Handle); + SetBkColor(Handle, 0); -var - Handled: Boolean; + if not (toExtendedFocus in FOptions.SelectionOptions) and (toFullRowSelect in FOptions.SelectionOptions) and + (tsUseExplorerTheme in FStates) then + FocusRect := RowRect + else + if toGridExtensions in FOptions.MiscOptions then + FocusRect := CellRect + else + FocusRect := InnerRect; -begin - Handled := False; + if tsUseExplorerTheme in FStates then + InflateRect(FocusRect, -1, -1); - // Try the header whether it needs to take this message. - if Assigned(FHeader) and (FHeader.States <> []) then - Handled := FHeader.HandleMessage(Message); - if not Handled then - begin - // For auto drag mode, let tree handle itself, instead of TControl. - if not (csDesigning in ComponentState) and - ((Message.Msg = WM_LBUTTONDOWN) or (Message.Msg = WM_LBUTTONDBLCLK)) then - begin - if (DragMode = dmAutomatic) and (DragKind = dkDrag) then - begin - if IsControlMouseMsg(TWMMouse(Message)) then - Handled := True; - if not Handled then + if (tsUseExplorerTheme in FStates) and IsWinVistaOrAbove then begin - ControlState := ControlState + [csLButtonDown]; - Dispatch(Message); // overrides TControl's BeginDrag - Handled := True; - end; + //Draw focused unselected style like Windows 7 Explorer + if not (vsSelected in Node.States) then + DrawThemedFocusRect(LIS_NORMAL) + else + DrawBackground(TREIS_HOTSELECTED); + end + else + Winapi.Windows.DrawFocusRect(Handle, FocusRect); + SetTextColor(Handle, TextColorBackup); + SetBkColor(Handle, BackColorBackup); end; end; - - if not Handled and Assigned(FHeader) then - Handled := FHeader.HandleMessage(Message); - - if not Handled then - begin - if (Message.Msg in [WM_NCLBUTTONDOWN, WM_NCRBUTTONDOWN, WM_NCMBUTTONDOWN]) and not Focused then - TrySetFocus; - inherited; - end; end; + + if tsUseExplorerTheme in FStates then + CloseThemeData(Theme); end; -//PROFILE-YES //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.WriteChunks(Stream: TStream; Node: PVirtualNode); +function TBaseVirtualTree.ReadChunk(Stream: TStream; Version: Integer; Node: PVirtualNode; ChunkType, + ChunkSize: Integer): Boolean; -// Writes the core chunks for Node into the stream. -// Note: descendants can optionally override this method to add other node specific chunks. -// Keep in mind that this method is also called for the root node. Using this fact in descendants you can -// create a kind of "global" chunks not directly bound to a specific node. +// Called while loading a tree structure, Node is already valid (allocated) at this point. +// The function handles the base and user chunks, any other chunk is marked as being unknown (result becomes False) +// and skipped. descendants may handle them by overriding this method. +// Returns True if the chunk could be handled, otherwise False. +type + TAdvancedVersion2Identifier = packed record + ChildCount, + NodeHeight: Cardinal; + States: Word; + Align: Byte; + CheckState: TCheckState; + CheckType: TCheckType; + Reserved: Cardinal; + end; var - Header: TChunkHeader; - LastPosition, - ChunkSize: Integer; - Chunk: TBaseChunk; + IdBody: TAdvancedVersion2Identifier; + ChunkBody: TBaseChunkBody; Run: PVirtualNode; + LastPosition: Integer; begin - with Stream do - begin - // 1. The base chunk... - LastPosition := Position; - Chunk.Header.ChunkType := BaseChunk; - with Node^, Chunk do - begin - Body.ChildCount := ChildCount; - Body.NodeHeight := NodeHeight; - // Some states are only temporary so take them out as they make no sense at the new location. - Body.States := States - [vsChecking, vsCutOrCopy, vsDeleting, vsOnFreeNodeCallRequired, vsHeightMeasured]; - Body.Align := Align; - Body.CheckState := GetCheckState(Node); - Body.CheckType := CheckType; - Body.Reserved := 0; - end; - // write the base chunk - Write(Chunk, SizeOf(Chunk)); - - // 2. ... directly followed by the child node chunks (actually they are child chunks of - // the base chunk) - if vsInitialized in Node.States then - begin - Run := Node.FirstChild; - while Assigned(Run) do + case ChunkType of + BaseChunk: begin - WriteNode(Stream, Run); - Run := Run.NextSibling; - end; - end; + // Load base chunk's body (chunk header has already been consumed). + case Version of + 1: + begin + with ChunkBody do + begin + // In version prior to 2 there was a smaller chunk body. Hence we have to read it entry by entry now. + Stream.Read(ChildCount, SizeOf(ChildCount)); + Stream.Read(NodeHeight, SizeOf(NodeHeight)); + // TVirtualNodeStates was a byte sized type in version 1. + States := []; + Stream.Read(States, SizeOf(Byte)); + // vsVisible is now in the place where vsSelected was before, but every node was visible in the old version + // so we need to fix this too. + if vsVisible in States then + //sync path note: prior version stream reading, ignored for syncing + Include(States, vsSelected) + else + Include(States, vsVisible); + Stream.Read(Align, SizeOf(Align)); + Stream.Read(CheckState, SizeOf(CheckState)); + Stream.Read(CheckType, SizeOf(CheckType)); + end; + end; + 2: + begin + ZeroMemory(@IdBody, SizeOf(IdBody)); + Stream.Read(IdBody, SizeOf(IdBody)); + // If Align is greater than zero, we have a stream prior to VT version 6.2 + if IdBody.Align > 0 then + with ChunkBody do + begin + ChildCount := IdBody.ChildCount; + NodeHeight := IdBody.NodeHeight; + States := []; + Move(IdBody.States, States, SizeOf(IdBody.States)); + CheckState := IdBody.CheckState; + CheckType := IdBody.CheckType; + Reserved := IdBody.Reserved; + end + else + begin + // Stream is compatible with current size of TBaseChunkBody + Stream.Position := Stream.Position - SizeOf(IdBody); + Stream.Read(ChunkBody, SizeOf(ChunkBody)); + end; + end; + 3: + Stream.Read(ChunkBody, SizeOf(ChunkBody)); + end; - FinishChunkHeader(Stream, LastPosition, Position); + with Node^ do + begin + // Set states first, in case the node is invisible. + States := ChunkBody.States; + NodeHeight := ChunkBody.NodeHeight; + TotalHeight := NodeHeight; + Align := ChunkBody.Align; + CheckState := ChunkBody.CheckState; + CheckType := ChunkBody.CheckType; + ChildCount := ChunkBody.ChildCount; - // 3. write user data - LastPosition := Position; - Header.ChunkType := UserChunk; - Write(Header, SizeOf(Header)); - DoSaveUserData(Node, Stream); - // check if the application actually wrote data - ChunkSize := Position - LastPosition - SizeOf(TChunkHeader); - // seek back to start of chunk if nothing has been written - if ChunkSize = 0 then - begin - Position := LastPosition; - Size := Size - SizeOf(Header); - end - else - FinishChunkHeader(Stream, LastPosition, Position); + // Create and read child nodes. + while ChunkBody.ChildCount > 0 do + begin + Run := MakeNewNode; + + Run.PrevSibling := Node.LastChild; + if Assigned(Run.PrevSibling) then + Run.Index := Run.PrevSibling.Index + 1; + if Assigned(Node.LastChild) then + Node.LastChild.NextSibling := Run + else + Node.FirstChild := Run; + Node.LastChild := Run; + Run.Parent := Node; + + ReadNode(Stream, Version, Run); + Dec(ChunkBody.ChildCount); + end; + end; + Result := True; + end; + UserChunk: + if ChunkSize > 0 then + begin + // need to know whether the data was read + LastPosition := Stream.Position; + DoLoadUserData(Node, Stream); + // compare stream position to learn whether the data was read + Result := Stream.Position > LastPosition; + // Improve stability by advancing the stream to the chunk's real end if + // the application did not read what has been written. + if not Result or (Stream.Position <> (LastPosition + ChunkSize)) then + Stream.Position := LastPosition + ChunkSize; + end + else + Result := True; + else + // unknown chunk, skip it + Stream.Position := Stream.Position + ChunkSize; + Result := False; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.WriteNode(Stream: TStream; Node: PVirtualNode); +procedure TBaseVirtualTree.ReadNode(Stream: TStream; Version: Integer; Node: PVirtualNode); -// Writes the "cover" chunk for Node to Stream and initiates writing child nodes and chunks. +// Reads the anchor chunk of each node and initiates reading the sub chunks for this node var - LastPosition: Integer; Header: TChunkHeader; + EndPosition: Integer; begin - // Initialize the node first if necessary and wanted. - if toInitOnSave in FOptions.MiscOptions then + with Stream do begin - if not (vsInitialized in Node.States) then - InitNode(Node); - if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then - InitChildren(Node); + // Read anchor chunk of the node. + Stream.Read(Header, SizeOf(Header)); + if Header.ChunkType = NodeChunk then + begin + EndPosition := Stream.Position + Header.ChunkSize; + // Read all subchunks until the indicated chunk end position is reached in the stream. + while Position < EndPosition do + begin + // Read new chunk header. + Stream.Read(Header, SizeOf(Header)); + ReadChunk(Stream, Version, Node, Header.ChunkType, Header.ChunkSize); + end; + // If the last chunk does not end at the given end position then there is something wrong. + if Position <> EndPosition then + ShowError(SCorruptStream2, hcTFCorruptStream2); + end + else + ShowError(SCorruptStream1, hcTFCorruptStream1); end; +end; - with Stream do - begin - LastPosition := Position; - // Emit the anchor chunk. - Header.ChunkType := NodeChunk; - Write(Header, SizeOf(Header)); - // Write other chunks to stream taking their size into this chunk's size. - WriteChunks(Stream, Node); +//---------------------------------------------------------------------------------------------------------------------- - // Update chunk size. - FinishChunkHeader(Stream, LastPosition, Position); +procedure TBaseVirtualTree.RedirectFontChangeEvent(Canvas: TCanvas); + +begin + if @Canvas.Font.OnChange <> @FOldFontChange then + begin + FOldFontChange := Canvas.Font.OnChange; + Canvas.Font.OnChange := FontChanged; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.AbsoluteIndex(Node: PVirtualNode): Cardinal; +procedure TBaseVirtualTree.RemoveFromSelection(Node: PVirtualNode); + +var + Index: Integer; begin - Result := 0; - while Assigned(Node) and (Node <> FRoot) do + if not FSelectionLocked then begin - if not (vsInitialized in Node.States) then - InitNode(Node); - if Assigned(Node.PrevSibling) then - begin - // if there's a previous sibling then add its total count to the result - Node := Node.PrevSibling; - Inc(Result, Node.TotalCount); - end - else + Assert(Assigned(Node), 'Node must not be nil!'); + Assert(GetCurrentThreadId = MainThreadId, Self.Classname + '.RemoveFromSelection() must only be called from UI thread.'); + if vsSelected in Node.States then begin - Node := Node.Parent; - if Node <> FRoot then - Inc(Result); + Assert(FSelectionCount > 0, 'if one node has set the vsSelected flag, SelectionCount must be >0.'); + //sync path note: deselect when a ctrl click removes a selection + Exclude(Node.States, vsSelected); + if SyncCheckstateWithSelection[Node] then + Node.CheckState := csUncheckedNormal; // Avoid using SetCheckState() as it handles toSyncCheckboxesWithSelection as well. + + if FindNodeInSelection(Node, Index, -1, -1) and (Index < FSelectionCount - 1) then + Move(FSelection[Index + 1], FSelection[Index], (FSelectionCount - Index - 1) * SizeOf(Pointer)); + if FSelectionCount > 0 then + Dec(FSelectionCount); + SetLength(FSelection, FSelectionCount); + + if FSelectionCount = 0 then + ResetRangeAnchor; + + if FSelectionCount <= 1 then + UpdateNextNodeToSelect(Node); + + DoRemoveFromSelection(Node); + InvalidateNode(Node); + Change(Node); end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.AddChild(Parent: PVirtualNode; UserData: Pointer = nil): PVirtualNode; +procedure TBaseVirtualTree.UpdateNextNodeToSelect(Node: PVirtualNode); -// Adds a new node to the given parent node. This is simply done by increasing the child count of the -// parent node. If Parent is nil then the new node is added as (last) top level node. -// UserData can be used to set the first SizeOf(Pointer) bytes of the user data area to an initial value which can be used -// in OnInitNode and will also cause to trigger the OnFreeNode event (if <> nil) even if the node is not yet -// "officially" initialized. -// AddChild is a compatibility method and will implicitly validate the parent node. This is however -// against the virtual paradigm and hence I dissuade from its usage. +// save a potential node to select after the currently selected node will be deleted. +// This will make the VT to behave more like the Win32 TreeView, which always selecta a new node if the currently +// selected one gets deleted. begin - if not (toReadOnly in FOptions.MiscOptions) then - Result := InsertNode(Parent, TVTNodeAttachMode.amAddChildLast, UserData) + if not (toAlwaysSelectNode in TreeOptions.SelectionOptions) then + Exit; + if GetNextSibling(Node) <> nil then + FNextNodeToSelect := GetNextSibling(Node) + else if GetPreviousSibling(Node) <> nil then + FNextNodeToSelect := GetPreviousSibling(Node) + else if Node.Parent <> FRoot then + FNextNodeToSelect := Node.Parent else - Result := nil; -end; + FNextNodeToSelect := nil; +end;//if Assigned(Node); -function TBaseVirtualTree.AddChild(Parent: PVirtualNode; const UserData: IInterface): PVirtualNode; -begin - UserData._AddRef(); - Result := AddChild(Parent, Pointer(UserData)); - Include(Result.States, vsReleaseCallOnUserDataRequired); -end; +//---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.AddChild(Parent: PVirtualNode; const UserData: TObject): PVirtualNode; -begin - Result := AddChild(Parent, Pointer(UserData)); -end; +function TBaseVirtualTree.RenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium; + ForClipboard: Boolean): HResult; -//---------------------------------------------------------------------------------------------------------------------- +// Returns a memory expression of all currently selected nodes in the Medium structure. +// Note: The memory requirement of this method might be very high. This depends however on the requested storage format. +// For HGlobal (a global memory block) we need to render first all nodes to local memory and copy this then to +// the global memory in Medium. This is necessary because we have first to determine how much +// memory is needed before we can allocate it. Hence for a short moment we need twice the space as used by the +// nodes alone (plus the amount the nodes need in the tree anyway)! +// With IStream this does not happen. We directly stream out the nodes and pass the constructed stream along. -procedure TBaseVirtualTree.AddFromStream(Stream: TStream; TargetNode: PVirtualNode); + //--------------- local function -------------------------------------------- -// loads nodes from the given stream and adds them to TargetNode -// the current content is not cleared before the load process starts (see also LoadFromStream) + procedure WriteNodes(Stream: TStream); + + var + Selection: TNodeArray; + I: Integer; + + begin + if ForClipboard then + Selection := GetSortedCutCopySet(True) + else + Selection := GetSortedSelection(True); + for I := 0 to High(Selection) do + WriteNode(Stream, Selection[I]); + end; + + //--------------- end local function ---------------------------------------- var - ThisID: TMagicID; - Version, - Count: Cardinal; - Node: PVirtualNode; + Data: PCardinal; + ResPointer: Pointer; + ResSize: Integer; + OLEStream: IStream; + VCLStream: TStream; begin - if not (toReadOnly in FOptions.MiscOptions) then + ZeroMemory (@Medium, SizeOf(Medium)); + + // We can render the native clipboard format in two different storage media. + if (FormatEtcIn.cfFormat = CF_VIRTUALTREE) and (FormatEtcIn.tymed and (TYMED_HGLOBAL or TYMED_ISTREAM) <> 0) then begin - // check first whether this is a stream we can read - Stream.ReadBuffer(ThisID, SizeOf(TMagicID)); - if (ThisID[0] = MagicID[0]) and - (ThisID[1] = MagicID[1]) and - (ThisID[2] = MagicID[2]) and - (ThisID[5] = MagicID[5]) then - begin - Version := Word(ThisID[3]); - if Version <= VTTreeStreamVersion then + VCLStream := nil; + try + Medium.unkForRelease := nil; + // Return data in one of the supported storage formats, prefer IStream. + if FormatEtcIn.tymed and TYMED_ISTREAM <> 0 then begin - BeginUpdate; - try - if Version < 2 then - Count := MaxInt - else - Stream.ReadBuffer(Count, SizeOf(Count)); - - while (Stream.Position < Stream.Size) and (Count > 0) do - begin - Dec(Count); - Node := MakeNewNode; - InternalConnectNode(Node, TargetNode, Self, amAddChildLast); - InternalAddFromStream(Stream, Version, Node); - end; - if TargetNode = FRoot then - DoNodeCopied(nil) - else - DoNodeCopied(TargetNode); - finally - EndUpdate; - end; + // Create an IStream on a memory handle (here it is 0 which indicates to implicitely allocated a handle). + // Do not use TStreamAdapter as it is not compatible with OLE (when flushing the clipboard OLE wants the HGlobal + // back which is not supported by TStreamAdapater). + CreateStreamOnHGlobal(0, True, OLEStream); + VCLStream := TOLEStream.Create(OLEStream); + WriteNodes(VCLStream); + // Rewind stream. + VCLStream.Position := 0; + Medium.tymed := TYMED_ISTREAM; + IUnknown(Medium.stm) := OLEStream; + Result := S_OK; end else - ShowError(SWrongStreamVersion, hcTFWrongStreamVersion); - end - else - ShowError(SWrongStreamVersion, hcTFWrongStreamVersion); - end; + begin + VCLStream := TMemoryStream.Create; + WriteNodes(VCLStream); + ResPointer := TMemoryStream(VCLStream).Memory; + ResSize := VCLStream.Position; + + // Allocate memory to hold the string. + if ResSize > 0 then + begin + Medium.hGlobal := GlobalAlloc(GHND or GMEM_SHARE, ResSize + SizeOf(Cardinal)); + Data := GlobalLock(Medium.hGlobal); + // Store the size of the data too, for easy retrival. + Data^ := ResSize; + Inc(Data); + Move(ResPointer^, Data^, ResSize); + GlobalUnlock(Medium.hGlobal); + Medium.tymed := TYMED_HGLOBAL; + + Result := S_OK; + end + else + Result := E_FAIL; + end; + finally + // We can free the VCL stream here since it was either a pure memory stream or only a wrapper around + // the OLEStream which exists independently. + VCLStream.Free; + end; + end + else // Ask application descendants to render self defined formats. + Result := DoRenderOLEData(FormatEtcIn, Medium, ForClipboard); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.AfterConstruction; +procedure TBaseVirtualTree.ResetRangeAnchor; -begin - inherited; +// Called when there is no selected node anymore and the selection range anchor needs a new value. - if FRoot = nil then - InitRootNode; +begin + FRangeAnchor := FFocusedNode; + FLastSelectionLevel := -1; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.Assign(Source: TPersistent); +procedure TBaseVirtualTree.RestoreFontChangeEvent(Canvas: TCanvas); begin - if (Source is TBaseVirtualTree) and not (toReadOnly in FOptions.MiscOptions) then - with Source as TBaseVirtualTree do - begin - Self.Align := Align; - Self.Anchors := Anchors; - Self.AutoScrollDelay := AutoScrollDelay; - Self.AutoScrollInterval := AutoScrollInterval; - Self.AutoSize := AutoSize; - Self.Background := Background; - Self.BevelEdges := BevelEdges; - Self.BevelInner := BevelInner; - Self.BevelKind := BevelKind; - Self.BevelOuter := BevelOuter; - Self.BevelWidth := BevelWidth; - Self.BiDiMode := BiDiMode; - Self.BorderStyle := BorderStyle; - Self.BorderWidth := BorderWidth; - Self.ChangeDelay := ChangeDelay; - Self.CheckImageKind := CheckImageKind; - Self.Color := Color; - Self.Colors.Assign(Colors); - Self.Constraints.Assign(Constraints); - Self.Ctl3D := Ctl3D; - Self.DefaultNodeHeight := DefaultNodeHeight; - Self.DefaultPasteMode := DefaultPasteMode; - Self.DragCursor := DragCursor; - Self.DragImageKind := DragImageKind; - Self.DragKind := DragKind; - Self.DragMode := DragMode; - Self.Enabled := Enabled; - Self.Font := Font; - Self.Header := Header; - Self.HintMode := HintMode; - Self.HotCursor := HotCursor; - Self.Images := Images; - Self.ImeMode := ImeMode; - Self.ImeName := ImeName; - Self.Indent := Indent; - Self.Margin := Margin; - Self.NodeAlignment := NodeAlignment; - Self.NodeDataSize := NodeDataSize; - Self.TreeOptions := TreeOptions; - Self.ParentBiDiMode := ParentBiDiMode; - Self.ParentColor := ParentColor; - Self.ParentCtl3D := ParentCtl3D; - Self.ParentFont := ParentFont; - Self.ParentShowHint := ParentShowHint; - Self.PopupMenu := PopupMenu; - Self.RootNodeCount := RootNodeCount; - Self.ScrollBarOptions := ScrollBarOptions; - Self.ShowHint := ShowHint; - Self.StateImages := StateImages; - Self.StyleElements := StyleElements; - Self.TabOrder := TabOrder; - Self.TabStop := TabStop; - Self.Visible := Visible; - Self.SelectionCurveRadius := SelectionCurveRadius; - Self.SelectionBlendFactor := SelectionBlendFactor; - Self.EmptyListMessage := EmptyListMessage; - end - else - inherited; + Canvas.Font.OnChange := FOldFontChange; + FOldFontChange := nil; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.AutoScale(isDpiChange: Boolean); +procedure TBaseVirtualTree.SelectNodes(StartNode, EndNode: PVirtualNode; AddOnly: Boolean); -// If toAutoChangeScale is set, this method ensures that the defaulz node height is set correctly. -// isDPIChnage is True, if the DPI of the form has changed. In this case the font may not yet be adapted to this, so do not adjust DefualtNodeHeight. +// Selects a range of nodes and unselects all other eventually selected nodes which are not in this range if +// AddOnly is False. +// EndNode must be visible while StartNode does not necessarily as in the case where the last focused node is the start +// node but it is a child of a node which has been collapsed previously. In this case the first visible parent node +// is used as start node. StartNode can be nil in which case the very first node in the tree is used. var - lTextHeight: Cardinal; + NodeFrom, + NodeTo, + LastAnchor: PVirtualNode; + Index: Integer; + begin - if HandleAllocated and (toAutoChangeScale in TreeOptions.AutoOptions) and not isDpiChange then + Assert(Assigned(EndNode), 'EndNode must not be nil!'); + if not FSelectionLocked then begin - Canvas.Font.Assign(Self.Font); - lTextHeight := Canvas.TextHeight('Tg') + 2; - // By default, we only ensure that DefaultNodeHeight is large enough. - // If the form's dpi has changed, we scale up and down the DefaultNodeHeight, See issue #677. - if (lTextHeight > Self.DefaultNodeHeight) then - Self.DefaultNodeHeight := lTextHeight; + ClearTempCache; + if StartNode = nil then + StartNode := GetFirstVisibleNoInit(nil, True) + else + if not FullyVisible[StartNode] then + begin + StartNode := GetPreviousVisible(StartNode, True); + if StartNode = nil then + StartNode := GetFirstVisibleNoInit(nil, True); + end; + + if CompareNodePositions(StartNode, EndNode, True) < 0 then + begin + NodeFrom := StartNode; + NodeTo := EndNode; + end + else + begin + NodeFrom := EndNode; + NodeTo := StartNode; + end; + + // The range anchor will be reset by the following call. + LastAnchor := FRangeAnchor; + if not AddOnly then + InternalClearSelection; + + while NodeFrom <> NodeTo do + begin + InternalCacheNode(NodeFrom); + NodeFrom := GetNextVisible(NodeFrom, True); + end; + // select last node too + InternalCacheNode(NodeFrom); + // now add them all in "one" step + AddToSelection(FTempNodeCache, FTempNodeCount); + ClearTempCache; + if Assigned(LastAnchor) and FindNodeInSelection(LastAnchor, Index, -1, -1) then + FRangeAnchor := LastAnchor; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.BeginDrag(Immediate: Boolean; Threshold: Integer); +procedure TBaseVirtualTree.SetFocusedNodeAndColumn(Node: PVirtualNode; Column: TColumnIndex); -// Reintroduced method to allow to start OLE drag'n drop as well as VCL drag'n drop. +var + OldColumn: TColumnIndex; + WasDifferent: Boolean; begin - if FDragType = dtVCL then + if not FHeader.AllowFocus(Column) then + Column := FFocusedColumn; + + WasDifferent := (Node <> FFocusedNode) or (Column <> FFocusedColumn); + + OldColumn := FFocusedColumn; + FFocusedColumn := Column; + + DoFocusNode(Node, True); + + // Check if the change was accepted. + if FFocusedNode = Node then begin - DoStateChange([tsVCLDragPending]); - inherited; + CancelEditNode; + if WasDifferent then + DoFocusChange(FFocusedNode, FFocusedColumn); end else - if (FStates * [tsOLEDragPending, tsOLEDragging]) = [] then - begin - // Drag start position has already been recorded in WMMouseDown. - if Threshold < 0 then - FDragThreshold := Mouse.DragThreshold - else - FDragThreshold := Threshold; - if Immediate then - DoDragging(FLastClickPos) - else - DoStateChange([tsOLEDragPending]); - end; + // If the user did not accept the new cell to focus then set also the focused column back + // to its original state. + FFocusedColumn := OldColumn; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.BeginSynch; +procedure TBaseVirtualTree.SkipNode(Stream: TStream); -// Starts the synchronous update mode (if not already active). +// Skips the data for the next node in the given stream (including the child nodes). + +var + Header: TChunkHeader; begin - if not (csDestroying in ComponentState) then + with Stream do begin - if FSynchUpdateCount = 0 then - begin - DoUpdating(usBeginSynch); - - // Stop all timers... - StopTimer(ChangeTimer); - StopTimer(StructureChangeTimer); - StopTimer(ExpandTimer); - StopTimer(EditTimer); - StopTimer(HeaderTimer); - StopTimer(ScrollTimer); - StopTimer(SearchTimer); - FSearchBuffer := ''; - FLastSearchNode := nil; - DoStateChange([], [tsEditPending, tsScrollPending, tsScrolling, tsIncrementalSearching]); - - // ...and trigger pending update states. - if tsStructureChangePending in FStates then - DoStructureChange(FLastStructureChangeNode, FLastStructureChangeReason); - if tsChangePending in FStates then - DoChange(FLastChangedNode); - end + // read achor chunk of the node + Stream.Read(Header, SizeOf(Header)); + if Header.ChunkType = NodeChunk then + Stream.Position := Stream.Position + Header.ChunkSize else - DoUpdating(usSynch); + ShowError(SCorruptStream1, hcTFCorruptStream1); end; - Inc(FSynchUpdateCount); - DoStateChange([tsSynchMode]); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.BeginUpdate; - -begin - Assert(GetCurrentThreadId = MainThreadId, 'UI controls like ' + Classname + ' should only be manipulated through the main thread.'); - if not (csDestroying in ComponentState) then - begin - if FUpdateCount = 0 then - begin - DoUpdating(usBegin); - SetUpdateState(True); - end - else - DoUpdating(usUpdate); - end; - Inc(FUpdateCount); - DoStateChange([tsUpdating]); -end; +var + PanningWindowClass: TWndClass = ( + style: 0; + lpfnWndProc: @DefWindowProc; + cbClsExtra: 0; + cbWndExtra: 0; + hInstance: 0; + hIcon: 0; + hCursor: 0; + hbrBackground: 0; + lpszMenuName: nil; + lpszClassName: 'VTPanningWindow' + ); -//---------------------------------------------------------------------------------------------------------------------- +procedure TBaseVirtualTree.StartWheelPanning(Position: TPoint); -procedure TBaseVirtualTree.CancelCutOrCopy; +// Called when wheel panning should start. A little helper window is created to indicate the reference position, +// which determines in which direction and how far wheel panning/scrolling will happen. -// Resets nodes which are marked as being cut. + //--------------- local function -------------------------------------------- -var - Run: PVirtualNode; + function CreateClipRegion: HRGN; + + // In order to avoid doing all the transparent drawing ourselves we use a + // window region for the wheel window. + // Since we only work on a very small image (32x32 pixels) this is acceptable. + + var + Start, X, Y: Integer; + Temp: HRGN; -begin - if ([tsCutPending, tsCopyPending] * FStates) <> [] then begin - Run := FRoot.FirstChild; - while Assigned(Run) do + Assert(not FPanningImage.Empty, 'Invalid wheel panning image.'); + + // Create an initial region on which we operate. + Result := CreateRectRgn(0, 0, 0, 0); + with FPanningImage, Canvas do begin - if vsCutOrCopy in Run.States then - Exclude(Run.States, vsCutOrCopy); - Run := GetNextNoInit(Run); + for Y := 0 to Height - 1 do + begin + Start := -1; + for X := 0 to Width - 1 do + begin + // Start a new span if we found a non-transparent pixel and no span is currently started. + if (Start = -1) and (Pixels[X, Y] <> clFuchsia) then + Start := X + else + if (Start > -1) and (Pixels[X, Y] = clFuchsia) then + begin + // A non-transparent span is finished. Add it to the result region. + Temp := CreateRectRgn(Start, Y, X, Y + 1); + CombineRgn(Result, Result, Temp, RGN_OR); + DeleteObject(Temp); + Start := -1; + end; + end; + // If there is an open span then add this also to the result region. + if Start > -1 then + begin + Temp := CreateRectRgn(Start, Y, Width, Y + 1); + CombineRgn(Result, Result, Temp, RGN_OR); + DeleteObject(Temp); + end; + end; end; + // The resulting region is used as window region so we must not delete it. + // Windows will own it after the assignment below. end; - DoStateChange([], [tsCutPending, tsCopyPending]); -end; -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CancelEditNode: Boolean; + //--------------- end local function ---------------------------------------- -// Called by the application or the current edit link to cancel the edit action. +var + TempClass: TWndClass; + ClassRegistered: Boolean; + ImageName: string; + Pt: TPoint; begin - if HandleAllocated and ([tsEditing, tsEditPending] * FStates <> []) then - Result := DoCancelEdit - else - Result := True; -end; + // Set both panning and scrolling flag. One will be removed shortly depending on whether the middle mouse button is + // released before the mouse is moved or vice versa. The first case is referred to as wheel scrolling while the + // latter is called wheel panning. + StopTimer(ScrollTimer); + DoStateChange([tsWheelPanning, tsWheelScrolling]); -//---------------------------------------------------------------------------------------------------------------------- + // Register the helper window class. + PanningWindowClass.hInstance := HInstance; + ClassRegistered := GetClassInfo(HInstance, PanningWindowClass.lpszClassName, TempClass); + if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then + begin + if ClassRegistered then + Winapi.Windows.UnregisterClass(PanningWindowClass.lpszClassName, HInstance); + Winapi.Windows.RegisterClass(PanningWindowClass); + end; + // Create the helper window and show it at the given position without activating it. + Pt := ClientToScreen(Position); + FPanningWindow := CreateWindowEx(WS_EX_TOOLWINDOW, PanningWindowClass.lpszClassName, nil, WS_POPUP, Pt.X - 16, Pt.Y - 16, + 32, 32, Handle, 0, HInstance, nil); -procedure TBaseVirtualTree.CancelOperation; + FPanningImage := TBitmap.Create; + if Integer(FRangeX) > ClientWidth then + begin + if Integer(FRangeY) > ClientHeight then + ImageName := 'VT_MOVEALL' + else + ImageName := 'VT_MOVEEW'; + end + else + ImageName := 'VT_MOVENS'; + FPanningImage.LoadFromResourceName(HInstance, ImageName); + SetWindowRgn(FPanningWindow, CreateClipRegion, False); -// Called by the application to cancel a long-running operation. + {$ifdef CPUX64} + SetWindowLongPtr(FPanningWindow, GWLP_WNDPROC, LONG_PTR(System.Classes.MakeObjectInstance(PanningWindowProc))); + {$else} + SetWindowLong(FPanningWindow, GWL_WNDPROC, NativeInt(System.Classes.MakeObjectInstance(PanningWindowProc))); + {$endif CPUX64} + ShowWindow(FPanningWindow, SW_SHOWNOACTIVATE); -begin - if FOperationCount > 0 then - FOperationCanceled := True; + // Setup the panscroll timer and capture all mouse input. + TrySetFocus(); + SetCapture(Handle); + SetTimer(Handle, ScrollTimer, 20, nil); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.CanEdit(Node: PVirtualNode; Column: TColumnIndex): Boolean; +procedure TBaseVirtualTree.StopWheelPanning; -// Returns True if the given node can be edited. +// Stops panning if currently active and destroys the helper window. + +var + Instance: Pointer; begin - Result := (toEditable in FOptions.MiscOptions) and Enabled and not (toReadOnly in FOptions.MiscOptions) - and ((Column < 0) or (coEditable in FHeader.Columns[Column].Options)); - DoCanEdit(Node, Column, Result); + if [tsWheelPanning, tsWheelScrolling] * FStates <> [] then + begin + // Release the mouse capture and stop the panscroll timer. + StopTimer(ScrollTimer); + ReleaseCapture; + DoStateChange([], [tsWheelPanning, tsWheelScrolling]); + + // Destroy the helper window. + {$ifdef CPUX64} + Instance := Pointer(GetWindowLongPtr(FPanningWindow, GWLP_WNDPROC)); + {$else} + Instance := Pointer(GetWindowLong(FPanningWindow, GWL_WNDPROC)); + {$endif CPUX64} + DestroyWindow(FPanningWindow); + if Instance <> @DefWindowProc then + System.Classes.FreeObjectInstance(Instance); + FPanningWindow := 0; + FPanningImage.Free; + FPanningImage := nil; + DeleteObject(FPanningCursor); + FPanningCursor := 0; + Winapi.Windows.SetCursor(Screen.Cursors[Cursor]); + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.CanFocus: Boolean; - -var - Form: TCustomForm; +procedure TBaseVirtualTree.StructureChange(Node: PVirtualNode; Reason: TChangeReason); begin - Result := inherited CanFocus; + AdviseChangeEvent(True, Node, Reason); - if Result and not (csDesigning in ComponentState) then + if FUpdateCount = 0 then begin - Form := GetParentForm(Self); - Result := (Form = nil) or (Form.Enabled and Form.Visible); + if (FChangeDelay > 0) and HandleAllocated and not (tsSynchMode in FStates) then + SetTimer(Handle, StructureChangeTimer, FChangeDelay, nil) + else + DoStructureChange(Node, Reason); end; end; +function TBaseVirtualTree.StyleServices(AControl: TControl): TCustomStyleServices; +begin + if AControl = nil then + AControl := Self; + Result := VTStyleServices(AControl); +end; + //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.Clear; +function TBaseVirtualTree.SuggestDropEffect(Source: TObject; Shift: TShiftState; Pt: TPoint; + AllowedEffects: Integer): Integer; + +// determines the drop action to take if the drag'n drop operation ends on this tree +// Note: Source can be any Delphi object not just a virtual tree begin - if (not IsEmpty and not (toReadOnly in FOptions.MiscOptions)) or (csDestroying in ComponentState) then - begin - BeginUpdate; - try - InterruptValidation; - if IsEditing then - CancelEditNode; + Result := AllowedEffects; - if ClipboardStates * FStates <> [] then - begin - OleSetClipboard(nil); - DoStateChange([], ClipboardStates); - end; - ClearSelection; - FFocusedNode := nil; - FLastSelected := nil; - FCurrentHotNode := nil; - FDropTargetNode := nil; - FLastChangedNode := nil; - FRangeAnchor := nil; - FLastVCLDragTarget := nil; - FLastSearchNode := nil; - DeleteChildren(FRoot, True); - FOffsetX := 0; - FOffsetY := 0; + // prefer MOVE if source and target are the same control, otherwise whatever is allowed as initial value + if Assigned(Source) and (Source = Self) then + if (AllowedEffects and DROPEFFECT_MOVE) <> 0 then + Result := DROPEFFECT_MOVE + else // no change + else + // drag between different applicatons + if (AllowedEffects and DROPEFFECT_COPY) <> 0 then + Result := DROPEFFECT_COPY; - finally - EndUpdate; + // consider modifier keys and what is allowed at the moment, if none of the following conditions apply then + // the initial value just set is used + if ssCtrl in Shift then + begin + // copy or link + if ssShift in Shift then + begin + // link + if (AllowedEffects and DROPEFFECT_LINK) <> 0 then + Result := DROPEFFECT_LINK; + end + else + begin + // copy + if (AllowedEffects and DROPEFFECT_COPY) <> 0 then + Result := DROPEFFECT_COPY; + end; + end + else + begin + // move, link or default + if ssShift in Shift then + begin + // move + if (AllowedEffects and DROPEFFECT_MOVE) <> 0 then + Result := DROPEFFECT_MOVE; + end + else + begin + // link or default + if ssAlt in Shift then + begin + // link + if (AllowedEffects and DROPEFFECT_LINK) <> 0 then + Result := DROPEFFECT_LINK; + end; + // else default end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.ClearChecked; +procedure TBaseVirtualTree.ToggleSelection(StartNode, EndNode: PVirtualNode); + +// Switchs the selection state of a range of nodes. +// Note: This method is specifically designed to help selecting ranges with the keyboard and considers therefore +// the range anchor. var - Node: PVirtualNode; + NodeFrom, + NodeTo: PVirtualNode; + NewSize: Integer; + Position: Integer; begin - Node := RootNode.FirstChild; - while Assigned(Node) do + if not FSelectionLocked then begin - if Node.CheckState <> csUncheckedNormal then - CheckState[Node] := csUncheckedNormal; - Node := GetNextNoInit(Node); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.ClearSelection(); -begin - ClearSelection(True); -end; - -//---------------------------------------------------------------------------------------------------------------------- + Assert(Assigned(EndNode), 'EndNode must not be nil!'); + if StartNode = nil then + StartNode := FRoot.FirstChild + else + if not FullyVisible[StartNode] then + StartNode := GetPreviousVisible(StartNode, True); -procedure TBaseVirtualTree.ClearDragManager; -begin - Pointer(FDragManager) := nil; -end; + Position := CompareNodePositions(StartNode, EndNode); + // nothing to do if start and end node are the same + if Position <> 0 then + begin + if Position < 0 then + begin + NodeFrom := StartNode; + NodeTo := EndNode; + end + else + begin + NodeFrom := EndNode; + NodeTo := StartNode; + end; -//---------------------------------------------------------------------------------------------------------------------- + ClearTempCache; -procedure TBaseVirtualTree.ClearSelection(pFireChangeEvent: Boolean); + // 1) toggle the start node if it is before the range anchor + if CompareNodePositions(NodeFrom, FRangeAnchor) < 0 then + if not (vsSelected in NodeFrom.States) then + InternalCacheNode(NodeFrom) + else + InternalRemoveFromSelection(NodeFrom); -var - Node: PVirtualNode; - Dummy: Integer; - R: TRect; - Counter: Integer; + // 2) toggle all nodes within the range + NodeFrom := GetNextVisible(NodeFrom, True); + while NodeFrom <> NodeTo do + begin + if not (vsSelected in NodeFrom.States) then + InternalCacheNode(NodeFrom) + else + InternalRemoveFromSelection(NodeFrom); + NodeFrom := GetNextVisible(NodeFrom, True); + end; -begin - Assert(GetCurrentThreadId = MainThreadId, Self.Classname + '.ClearSelection() must only be called from UI thread.'); - if not FSelectionLocked and (FSelectionCount > 0) and not (csDestroying in ComponentState) then - begin - if (FUpdateCount = 0) and HandleAllocated and (FVisibleCount > 0) then - begin - // Iterate through nodes currently visible in the client area and invalidate them. - Node := GetNodeAt(0, 0, True, Dummy); - if Assigned(Node) then - R := GetDisplayRect(Node, NoColumn, False); - Counter := FSelectionCount; + // 3) toggle end node if it is after the range anchor + if CompareNodePositions(NodeFrom, FRangeAnchor) > 0 then + if not (vsSelected in NodeFrom.States) then + InternalCacheNode(NodeFrom) + else + InternalRemoveFromSelection(NodeFrom); - while Assigned(Node) do + // Do some housekeeping if there was a change. + NewSize := PackArray(FSelection, FSelectionCount); + if NewSize > -1 then begin - R.Bottom := R.Top + Integer(NodeHeight[Node]); - if vsSelected in Node.States then - begin - InvalidateRect(Handle, @R, False); - Dec(Counter); - // Only try as many nodes as are selected. - if Counter = 0 then - Break; - end; - R.Top := R.Bottom; - if R.Top > ClientHeight then - Break; - Node := GetNextVisibleNoInit(Node, True); + FSelectionCount := NewSize; + SetLength(FSelection, FSelectionCount); end; + // If the range went over the anchor then we need to reselect it. + if not (vsSelected in FRangeAnchor.States) then + InternalCacheNode(FRangeAnchor); + if FTempNodeCount > 0 then + AddToSelection(FTempNodeCache, FTempNodeCount); + ClearTempCache; end; - - InternalClearSelection; - if pFireChangeEvent then - Change(nil); end; end; -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.CopyTo(Source: PVirtualNode; Tree: TBaseVirtualTree; Mode: TVTNodeAttachMode; - ChildrenOnly: Boolean): PVirtualNode; - -// A simplified CopyTo method to allow to copy nodes to the root of another tree. - +procedure TBaseVirtualTree.TrySetFocus(); begin - Result := CopyTo(Source, Tree.FRoot, Mode, ChildrenOnly); + if Visible and CanFocus then + begin + try + Self.SetFocus(); + except + on EInvalidOperation do + Exit; + end; + end;//if end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.CopyTo(Source, Target: PVirtualNode; Mode: TVTNodeAttachMode; - ChildrenOnly: Boolean): PVirtualNode; +procedure TBaseVirtualTree.UnselectNodes(StartNode, EndNode: PVirtualNode); -// Copies Source and all its child nodes to Target. -// Mode is used to specify further where to add the new node actually (as sibling of Target or as child of Target). -// Result is the newly created node to which source has been copied if ChildrenOnly is False or just contains Target -// in the other case. -// ChildrenOnly determines whether to copy also the source node or only its child nodes. +// Deselects a range of nodes. +// EndNode must be visible while StartNode must not as in the case where the last focused node is the start node +// but it is a child of a node which has been collapsed previously. In this case the first visible parent node +// is used as start node. StartNode can be nil in which case the very first node in the tree is used. var - TargetTree: TBaseVirtualTree; - Stream: TMemoryStream; + NodeFrom, + NodeTo: PVirtualNode; + NewSize: Integer; begin - Assert(TreeFromNode(Source) = Self, 'The source tree must contain the source node.'); - - Result := nil; - if (Mode <> amNoWhere) and Assigned(Source) and (Source <> FRoot) then + if not FSelectionLocked then begin - // Assume that an empty destination means the root in this (the source) tree. - if Target = nil then - begin - TargetTree := Self; - Target := FRoot; - Mode := amAddChildFirst; - end - else - TargetTree := TreeFromNode(Target); + Assert(Assigned(EndNode), 'EndNode must not be nil!'); - if not (toReadOnly in TargetTree.TreeOptions.MiscOptions) then - begin - if Target = TargetTree.FRoot then + if StartNode = nil then + StartNode := FRoot.FirstChild + else + if not FullyVisible[StartNode] then begin - case Mode of - amInsertBefore: - Mode := amAddChildFirst; - amInsertAfter: - Mode := amAddChildLast; - end; + StartNode := GetPreviousVisible(StartNode, True); + if StartNode = nil then + StartNode := FRoot.FirstChild; end; - Stream := TMemoryStream.Create; - try - // Write all nodes into a temprary stream depending on the ChildrenOnly flag. - if not ChildrenOnly then - WriteNode(Stream, Source) - else - begin - Source := Source.FirstChild; - while Assigned(Source) do - begin - WriteNode(Stream, Source); - Source := Source.NextSibling; - end; - end; - // Now load the serialized nodes into the target node (tree). - TargetTree.BeginUpdate; - try - Stream.Position := 0; - while Stream.Position < Stream.Size do - begin - Result := TargetTree.MakeNewNode; - InternalConnectNode(Result, Target, TargetTree, Mode); - TargetTree.InternalAddFromStream(Stream, VTTreeStreamVersion, Result); - if not DoNodeCopying(Result, Target) then - begin - TargetTree.DeleteNode(Result); - Result := nil; - end - else - DoNodeCopied(Result); - end; - if ChildrenOnly then - Result := Target; - finally - TargetTree.EndUpdate; - end; - finally - Stream.Free; - end; + if CompareNodePositions(StartNode, EndNode) < 0 then + begin + NodeFrom := StartNode; + NodeTo := EndNode; + end + else + begin + NodeFrom := EndNode; + NodeTo := StartNode; + end; - with TargetTree do - begin - InvalidateCache; - if FUpdateCount = 0 then - begin - ValidateCache; - UpdateScrollBars(True); - Invalidate; - end; - StructureChange(Source, crNodeCopied); - end; + while NodeFrom <> NodeTo do + begin + InternalRemoveFromSelection(NodeFrom); + NodeFrom := GetNextVisible(NodeFrom, True); + end; + // Deselect last node too. + InternalRemoveFromSelection(NodeFrom); + + // Do some housekeeping. + NewSize := PackArray(FSelection, FSelectionCount); + if NewSize > -1 then + begin + FSelectionCount := NewSize; + SetLength(FSelection, FSelectionCount); end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.CopyToClipboard; - -var - DataObject: IDataObject; +procedure TBaseVirtualTree.UpdateColumnCheckState(Col: TVirtualTreeColumn); begin - if FSelectionCount > 0 then - begin - DataObject := TVTDataObject.Create(Self, True) as IDataObject; - if OleSetClipboard(DataObject) = S_OK then - begin - MarkCutCopyNodes; - DoStateChange([tsCopyPending]); - Invalidate; - end; - end; + Col.CheckState := DetermineNextCheckState(Col.CheckType, Col.CheckState); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.CutToClipboard; +procedure TBaseVirtualTree.UpdateDesigner; + +var + ParentForm: TCustomForm; + begin - if (FSelectionCount > 0) and not (toReadOnly in FOptions.MiscOptions) then + if (csDesigning in ComponentState) and not (csUpdating in ComponentState) then begin - if OleSetClipboard(TVTDataObject.Create(Self, True)) = S_OK then - begin - MarkCutCopyNodes; - DoStateChange([tsCutPending], [tsCopyPending]); - Invalidate; - end; + ParentForm := GetParentForm(Self); + if Assigned(ParentForm) and Assigned(ParentForm.Designer) then + ParentForm.Designer.Modified; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DeleteChildren(Node: PVirtualNode; ResetHasChildren: Boolean = False); +procedure TBaseVirtualTree.UpdateHeaderRect(); -// Removes all children and their children from memory without changing the vsHasChildren style by default. +// Calculates the rectangle the header occupies in non-client area. +// These coordinates are in window rectangle. var - Run, - Mark: PVirtualNode; - LastTop, - LastLeft, - NewSize: Integer; - ParentVisible: Boolean; + OffsetX, + OffsetY: Integer; + EdgeSize: Integer; + Size: TSize; begin - if Assigned(Node) and (Node.ChildCount > 0) and not (toReadOnly in FOptions.MiscOptions) then - begin - Assert(not (tsIterating in FStates), 'Deleting nodes during tree iteration leads to invalid pointers.'); - - // The code below uses some flags for speed improvements which may cause invalid pointers if updates of - // the tree happen. Hence switch updates off until we have finished the operation. - Inc(FUpdateCount); - try - InterruptValidation; - LastLeft := -FEffectiveOffsetX; - LastTop := FOffsetY; - - // Make a local copy of the visibility state of this node to speed up - // adjusting the visible nodes count. - ParentVisible := Node = FRoot; - if not ParentVisible then - ParentVisible := FullyVisible[Node] and (vsExpanded in Node.States); + FHeaderRect := Rect(0, 0, Width, Height); - // Show that we are clearing the child list, to avoid registering structure change events. - Run := Node.LastChild; - while Assigned(Run) do - begin - if ParentVisible and IsEffectivelyVisible[Run] then - Dec(FVisibleCount); + // Consider borders... + if HandleAllocated then begin // Prevent preliminary creation of window handle, see issue #933 + Size := GetBorderDimensions(); + InflateRect(FHeaderRect, Size.cx, Size.cy); + end; - Include(Run.States, vsDeleting); - Mark := Run; - Run := Run.PrevSibling; - // Important, to avoid exchange of invalid pointers while disconnecting the node. - if Assigned(Run) then - Run.NextSibling := nil; - DeleteNode(Mark, False, True); - end; - if ResetHasChildren then - Exclude(Node.States, vsHasChildren); - if Node <> FRoot then - Exclude(Node.States, vsExpanded); - Node.ChildCount := 0; - if (Node = FRoot) or (vsDeleting in Node.States) then - begin - Node.TotalHeight := FDefaultNodeHeight + NodeHeight[Node]; - Node.TotalCount := 1; - end - else - begin - AdjustTotalHeight(Node, NodeHeight[Node]); - AdjustTotalCount(Node, 1); - end; - Node.FirstChild := nil; - Node.LastChild := nil; - finally - Dec(FUpdateCount); - end; + // ... and bevels. + OffsetX := BorderWidth; + OffsetY := BorderWidth; + if BevelKind <> bkNone then + begin + EdgeSize := 0; + if BevelInner <> bvNone then + Inc(EdgeSize, BevelWidth); + if BevelOuter <> bvNone then + Inc(EdgeSize, BevelWidth); + if beLeft in BevelEdges then + Inc(OffsetX, EdgeSize); + if beTop in BevelEdges then + Inc(OffsetY, EdgeSize); + end; - InvalidateCache; - if FUpdateCount = 0 then - begin - NewSize := PackArray(FSelection, FSelectionCount); - if NewSize > -1 then - begin - FSelectionCount := NewSize; - SetLength(FSelection, FSelectionCount); - end; + InflateRect(FHeaderRect, -OffsetX, -OffsetY); - ValidateCache; - UpdateScrollBars(True); - // Invalidate entire tree if it scrolled e.g. to make the last node also the - // bottom node in the treeview. - if (LastLeft <> FOffsetX) or (LastTop <> FOffsetY) then - Invalidate - else - InvalidateToBottom(Node); - if tsChangePending in FStates then begin - DoChange(FLastChangedNode); - EnsureNodeSelected(); - end; - end; - StructureChange(Node, crChildDeleted); + if hoVisible in FHeader.Options then + begin + if FHeaderRect.Left <= FHeaderRect.Right then + FHeaderRect.Bottom := FHeaderRect.Top + Integer(FHeader.Height) + else + FHeaderRect := Rect(0, 0, 0, 0); end - else if ResetHasChildren then - Exclude(Node.States, vsHasChildren); + else + FHeaderRect.Bottom := FHeaderRect.Top; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DeleteNode(Node: PVirtualNode; Reindex: Boolean; ParentClearing: Boolean); +procedure TBaseVirtualTree.UpdateEditBounds; + +// Used to update the bounds of the current node editor if editing is currently active. var - LastTop, - LastLeft: Integer; - LastParent: PVirtualNode; - WasInSynchMode: Boolean; + R: TRect; + CurrentAlignment: TAlignment; + CurrentBidiMode: TBidiMode; + offsets : TVTOffsets; + offset : Integer; begin - if Assigned(Node) and (Node <> FRoot) and not (toReadOnly in FOptions.MiscOptions) then + if (tsEditing in FStates) and Assigned(FFocusedNode) and + (FEditColumn < FHeader.Columns.Count) then // prevent EArgumentOutOfRangeException begin - Assert(not (tsIterating in FStates), 'Deleting nodes during tree iteration leads to invalid pointers.'); - - // Determine parent node for structure change notification. - LastParent := Node.Parent; - - if not ParentClearing then + if (GetCurrentThreadId <> MainThreadID) then begin - if LastParent = FRoot then - StructureChange(nil, crChildDeleted) - else - StructureChange(LastParent, crChildDeleted); - if Node = FNextNodeToSelect then - FNextNodeToSelect := nil; + // UpdateEditBounds() will be called at the end of the thread + Exit; end; + if vsMultiline in FFocusedNode.States then + R := GetDisplayRect(FFocusedNode, FEditColumn, True, False) + else if not (toGridExtensions in FOptions.MiscOptions) then + R := GetDisplayRect(FFocusedNode, FEditColumn, True, True); - LastLeft := -FEffectiveOffsetX; - LastTop := FOffsetY; - - if tsHint in FStates then + if (toGridExtensions in FOptions.MiscOptions) then begin - Application.CancelHint; - DoStateChange([], [tsHint]); - end; - - if not ParentClearing then - InterruptValidation; - - DeleteChildren(Node); + // Use the whole cell when grid extensions are on. + R := GetDisplayRect(FFocusedNode, FEditColumn, False, False); + if FEditColumn = FHeader.MainColumn then + begin + // Calculate an offset for the main column. + GetOffsets(FFocusedNode, offsets, ofsLabel, FEditColumn); + offset := offsets[ofsLabel]; +// if offsets[ofsToggleButton] < 0 then +// Inc(offset, offsets[ofsToggleButton]); + end + else + offset := 0; - if vsSelected in Node.States then - begin - if FUpdateCount = 0 then + // Adjust edit bounds depending on alignment and bidi mode. + if FEditColumn <= NoColumn then begin - // Go temporarily into sync mode to avoid a delayed change event for the node - // when unselecting. - WasInSynchMode := tsSynchMode in FStates; - Include(FStates, tsSynchMode); - RemoveFromSelection(Node); - //EnsureNodeSelected(); // also done in DoFreeNode() - if not WasInSynchMode then - Exclude(FStates, tsSynchMode); - InvalidateToBottom(LastParent); + CurrentAlignment := Alignment; + CurrentBidiMode := BiDiMode; + end + else + begin + CurrentAlignment := FHeader.Columns[FEditColumn].Alignment; + CurrentBidiMode := FHeader.Columns[FEditColumn].BiDiMode; + end; + // Consider bidi mode here. In RTL context does left alignment actually mean right alignment and vice versa. + if CurrentBidiMode <> bdLeftToRight then + ChangeBiDiModeAlignment(CurrentAlignment); + if CurrentAlignment = taLeftJustify then + begin + if CurrentBiDiMode = bdLeftToRight then + Inc(R.Left, offset) + else + Dec(R.Right, offset); end else - InternalRemoveFromSelection(Node); - end - else - InvalidateToBottom(LastParent); - - InternalDisconnectNode(Node, False, Reindex); - DoFreeNode(Node); - - if not ParentClearing then - begin - DetermineHiddenChildrenFlag(LastParent); - InvalidateCache; - if FUpdateCount = 0 then begin - ValidateCache; - UpdateScrollBars(True); - // Invalidate entire tree if it scrolled e.g. to make the last node also the - // bottom node in the treeview. - if (LastLeft <> FOffsetX) or (LastTop <> FOffsetY) then - Invalidate; + if CurrentBiDiMode = bdLeftToRight then + Inc(R.Left, offset) + else + Dec(R.Right, offset); end; end; + if toShowHorzGridLines in TreeOptions.PaintOptions then + Dec(R.Bottom); + R.Bottom := R.Top + R.Bottom - R.Top; + FEditLink.SetBounds(R); end; end; -procedure TBaseVirtualTree.DeleteNode(Node: PVirtualNode; pReIndex: Boolean = True); -begin - DeleteNode(Node, pReIndex, False); -end; - //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.DeleteNodes(const pNodes: TNodeArray); - - // Deletes all given nodes. - // Best performance is achieved if nodes are sorted by parent +const + ScrollMasks: array[Boolean] of Cardinal = (0, SIF_DISABLENOSCROLL); -var - I: Integer; - LevelChange: Boolean; -begin - BeginUpdate; - try - for I := High(pNodes) downto 1 do - begin - LevelChange := pNodes[I].Parent <> pNodes[I - 1].Parent; - DeleteNode(pNodes[I], LevelChange, False); - end; - DeleteNode(pNodes[0]); - finally - EndUpdate; - end; -end; +const // Region identifiers for GetRandomRgn + CLIPRGN = 1; + METARGN = 2; + APIRGN = 3; + SYSRGN = 4; -//---------------------------------------------------------------------------------------------------------------------- +function GetRandomRgn(DC: HDC; Rgn: HRGN; iNum: Integer): Integer; stdcall; external 'GDI32.DLL'; -procedure TBaseVirtualTree.DeleteSelectedNodes; +procedure TBaseVirtualTree.UpdateWindowAndDragImage(const Tree: TBaseVirtualTree; TreeRect: TRect; UpdateNCArea, + ReshowDragImage: Boolean); -// Deletes all currently selected nodes (including their child nodes). +// Method to repaint part of the window area which is not covered by the drag image and to initiate a recapture +// of the drag image. +// Note: This method must only be called during a drag operation and the tree passed in is the one managing the current +// drag image (so it is the actual drag source). var - lNodes: TNodeArray; -begin - lNodes := nil; - if (FSelectionCount > 0) and not (toReadOnly in FOptions.MiscOptions) then - begin - lNodes := GetSortedSelection(True); - DeleteNodes(lNodes); - end; -end; + DragRegion, // the region representing the drag image + UpdateRegion, // the unclipped region within the tree to be updated + NCRegion: HRGN; // the region representing the non-client area of the tree + DragRect, + NCRect: TRect; + RedrawFlags: Cardinal; -//---------------------------------------------------------------------------------------------------------------------- + VisibleTreeRegion: HRGN; -function TBaseVirtualTree.Dragging: Boolean; + DC: HDC; + //This function was originally designed only for tree's drag image. But we modified + //it for reusing it with header's drag image too for solving issue 248. + useDragImage: TVTDragImage; begin - // Check for both OLE drag'n drop as well as VCL drag'n drop. - Result := ([tsOLEDragPending, tsOLEDragging] * FStates <> []) or inherited Dragging; -end; - -//---------------------------------------------------------------------------------------------------------------------- + if IntersectRect(TreeRect, TreeRect, ClientRect) then + begin + // Retrieve the visible region of the window. This is important to avoid overpainting parts of other windows + // which overlap this one. + VisibleTreeRegion := CreateRectRgn(0, 0, 1, 1); + DC := GetDCEx(Handle, 0, DCX_CACHE or DCX_WINDOW or DCX_CLIPSIBLINGS or DCX_CLIPCHILDREN); + GetRandomRgn(DC, VisibleTreeRegion, SYSRGN); + ReleaseDC(Handle, DC); -function TBaseVirtualTree.EditNode(Node: PVirtualNode; Column: TColumnIndex): Boolean; + //Take proper drag image depending on whether the drag is being done in the tree + //or in the header. + useDragImage := Tree.FDragImage; + if (not useDragImage.Visible) + and (Tree.FHeader.DragImage <> nil) and (Tree.FHeader.DragImage.Visible) + then + useDragImage := Tree.FHeader.DragImage; -// Application triggered edit event for the given node. -// Returns True if the tree started editing otherwise False. + // The drag image will figure out itself what part of the rectangle can be recaptured. + // Recapturing is not done by taking a snapshot of the screen, but by letting the tree draw itself + // into the back bitmap of the drag image. So the order here is unimportant. + useDragImage.RecaptureBackground(Self, TreeRect, VisibleTreeRegion, UpdateNCArea, ReshowDragImage); -begin - Assert(Assigned(Node), 'Node must not be nil.'); - Assert((Column > InvalidColumn) and (Column < FHeader.Columns.Count), - 'Column must be a valid column index (-1 if no header is shown).'); + // Calculate the screen area not covered by the drag image and which needs an update. + DragRect := useDragImage.GetDragImageRect; + MapWindowPoints(0, Handle, DragRect, 2); + DragRegion := CreateRectRgnIndirect(DragRect); - Result := tsEditing in FStates; - // If the tree is already editing then we don't disrupt this. - if not Result and not (toReadOnly in FOptions.MiscOptions) then - begin - FocusedNode := Node; - if Assigned(FFocusedNode) and (Node = FFocusedNode) and CanEdit(FFocusedNode, Column) then + // Start with non-client area if requested. + if UpdateNCArea then begin - FEditColumn := Column; - if not (vsInitialized in Node.States) then - InitNode(Node); - DoEdit; - Result := tsEditing in FStates; - end - else - Result := False; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.EndEditNode: Boolean; + // Compute the part of the non-client area which must be updated. -// Called to finish a current edit action or stop the edit timer if an edit operation is pending. -// Returns True if editing was successfully ended or the control was not in edit mode -// Returns False if the control could not leave the edit mode e.g. due to an invalid value that was entered. + // Determine the outer rectangle of the entire tree window. + GetWindowRect(Handle, NCRect); + // Express the tree window rectangle in client coordinates (because RedrawWindow wants them so). + MapWindowPoints(0, Handle, NCRect, 2); + NCRegion := CreateRectRgnIndirect(NCRect); + // Determine client rect in screen coordinates and create another region for it. + UpdateRegion := CreateRectRgnIndirect(ClientRect); + // Create a region which only contains the NC part by subtracting out the client area. + CombineRgn(NCRegion, NCRegion, UpdateRegion, RGN_DIFF); + // Subtract also out what is hidden by the drag image. + CombineRgn(NCRegion, NCRegion, DragRegion, RGN_DIFF); + RedrawWindow(Handle, nil, NCRegion, RDW_FRAME or RDW_NOERASE or RDW_NOCHILDREN or RDW_INVALIDATE or RDW_VALIDATE or + RDW_UPDATENOW); + DeleteObject(NCRegion); + DeleteObject(UpdateRegion); + end; -begin - if [tsEditing, tsEditPending] * FStates <> [] then - Result := DoEndEdit - else - Result := True; + UpdateRegion := CreateRectRgnIndirect(TreeRect); + RedrawFlags := RDW_INVALIDATE or RDW_VALIDATE or RDW_UPDATENOW or RDW_NOERASE or RDW_NOCHILDREN; + // Remove the part of the update region which is covered by the drag image. + CombineRgn(UpdateRegion, UpdateRegion, DragRegion, RGN_DIFF); + RedrawWindow(Handle, nil, UpdateRegion, RedrawFlags); + DeleteObject(UpdateRegion); + DeleteObject(DragRegion); + DeleteObject(VisibleTreeRegion); + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.EndSynch; +procedure TBaseVirtualTree.ValidateCache(); + +// Starts cache validation if not already done by adding this instance to the worker thread's waiter list +// (if not already there) and signalling the thread it can start validating. begin - if FSynchUpdateCount > 0 then - Dec(FSynchUpdateCount); + // stop validation if it is currently validating this tree's cache. + InterruptValidation(); - if not (csDestroying in ComponentState) then + FStartIndex := 0; + if (tsValidationNeeded in FStates) and (FVisibleCount > CacheThreshold) then begin - if FSynchUpdateCount = 0 then - begin - DoStateChange([], [tsSynchMode]); - DoUpdating(usEndSynch); - end - else - DoUpdating(usSynch); + // Tell the thread this tree needs actually something to do. + TWorkerThread.AddTree(Self); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.EndUpdate; - -var - NewSize: Integer; +procedure TBaseVirtualTree.ValidateNodeDataSize(var Size: Integer); begin - if FUpdateCount > 0 then - Dec(FUpdateCount); - - if not (csDestroying in ComponentState) then - begin - if (FUpdateCount = 0) and (tsUpdating in FStates) then - begin - if tsUpdateHiddenChildrenNeeded in FStates then - begin - DetermineHiddenChildrenFlagAllNodes; - Exclude(FStates, tsUpdateHiddenChildrenNeeded); - end; - - DoStateChange([], [tsUpdating]); - - NewSize := PackArray(FSelection, FSelectionCount); - if NewSize > -1 then - begin - FSelectionCount := NewSize; - SetLength(FSelection, FSelectionCount); - end; - - InvalidateCache; - ValidateCache; - if HandleAllocated then - UpdateScrollBars(False); + Size := SizeOf(Pointer); + if Assigned(FOnGetNodeDataSize) then + FOnGetNodeDataSize(Self, Size); +end; - if tsStructureChangePending in FStates then - DoStructureChange(FLastStructureChangeNode, FLastStructureChangeReason); - try - if tsChangePending in FStates then - DoChange(FLastChangedNode); - finally - if toAutoSort in FOptions.AutoOptions then - SortTree(FHeader.SortColumn, FHeader.SortDirection, True); +//---------------------------------------------------------------------------------------------------------------------- - SetUpdateState(False); - if HandleAllocated then - Invalidate; - UpdateDesigner; - end; - end; +procedure TBaseVirtualTree.VclStyleChanged(); - if FUpdateCount = 0 then begin - DoUpdating(usEnd); - EnsureNodeSelected(); - end - else - DoUpdating(usUpdate); - end; + // Updates the member FVclStyleEnabled, should be called initially and when the VCL style changes + +begin + FVclStyleEnabled := StyleServices.Enabled and not StyleServices.IsSystemStyle and not (csDesigning in ComponentState); + Header.StyleChanged(); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.ExecuteAction(Action: TBasicAction): Boolean; +//PROFILE-NO +procedure TBaseVirtualTree.WndProc(var Message: TMessage); -// Some support for standard actions. +var + Handled: Boolean; begin - Result := inherited ExecuteAction(Action); + Handled := False; - if not Result then + // Try the header whether it needs to take this message. + if Assigned(FHeader) and (FHeader.States <> []) then + Handled := TVTHeaderCracker(FHeader).HandleMessage(Message); + if not Handled then begin - Result := Action is TEditSelectAll; - if Result then - SelectAll(False) - else + // For auto drag mode, let tree handle itself, instead of TControl. + if not (csDesigning in ComponentState) and + ((Message.Msg = WM_LBUTTONDOWN) or (Message.Msg = WM_LBUTTONDBLCLK)) then begin - Result := Action is TEditCopy; - if Result then - CopyToClipboard - else - if not (toReadOnly in FOptions.MiscOptions) then + if (DragMode = dmAutomatic) and (DragKind = dkDrag) then + begin + if IsControlMouseMsg(TWMMouse(Message)) then + Handled := True; + if not Handled then begin - Result := Action is TEditCut; - if Result then - CutToClipboard - else - begin - Result := Action is TEditPaste; - if Result then - PasteFromClipboard - else - begin - Result := Action is TEditDelete; - if Result then - DeleteSelectedNodes; - end; - end; + ControlState := ControlState + [csLButtonDown]; + Dispatch(Message); // overrides TControl's BeginDrag + Handled := True; + end; end; end; + + if not Handled and Assigned(FHeader) then + Handled := TVTHeaderCracker(FHeader).HandleMessage(Message); + + if not Handled then + begin + if (Message.Msg in [WM_NCLBUTTONDOWN, WM_NCRBUTTONDOWN, WM_NCMBUTTONDOWN]) and not Focused then + TrySetFocus; + inherited; + end; end; end; +//PROFILE-YES //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.FinishCutOrCopy; +procedure TBaseVirtualTree.WriteChunks(Stream: TStream; Node: PVirtualNode); -// Deletes nodes which are marked as being cutted. +// Writes the core chunks for Node into the stream. +// Note: descendants can optionally override this method to add other node specific chunks. +// Keep in mind that this method is also called for the root node. Using this fact in descendants you can +// create a kind of "global" chunks not directly bound to a specific node. var + Header: TChunkHeader; + LastPosition, + ChunkSize: Integer; + Chunk: TBaseChunk; Run: PVirtualNode; begin - if tsCutPending in FStates then + with Stream do begin - Run := FRoot.FirstChild; - while Assigned(Run) do + // 1. The base chunk... + LastPosition := Position; + Chunk.Header.ChunkType := BaseChunk; + with Node^, Chunk do begin - if vsCutOrCopy in Run.States then - DeleteNode(Run); - Run := GetNextNoInit(Run); + Body.ChildCount := ChildCount; + Body.NodeHeight := NodeHeight; + // Some states are only temporary so take them out as they make no sense at the new location. + Body.States := States - [vsChecking, vsCutOrCopy, vsDeleting, vsOnFreeNodeCallRequired, vsHeightMeasured]; + Body.Align := Align; + Body.CheckState := GetCheckState(Node); + Body.CheckType := CheckType; + Body.Reserved := 0; end; - DoStateChange([], [tsCutPending]); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- + // write the base chunk + Write(Chunk, SizeOf(Chunk)); -procedure TBaseVirtualTree.FlushClipboard; + // 2. ... directly followed by the child node chunks (actually they are child chunks of + // the base chunk) + if vsInitialized in Node.States then + begin + Run := Node.FirstChild; + while Assigned(Run) do + begin + WriteNode(Stream, Run); + Run := Run.NextSibling; + end; + end; -// Used to render the data which is currently on the clipboard (finishes delayed rendering). + FinishChunkHeader(Stream, LastPosition, Position); -begin - if ClipboardStates * FStates <> [] then - begin - DoStateChange([tsClipboardFlushing]); - OleFlushClipboard; - CancelCutOrCopy; - DoStateChange([], [tsClipboardFlushing]); + // 3. write user data + LastPosition := Position; + Header.ChunkType := UserChunk; + Write(Header, SizeOf(Header)); + DoSaveUserData(Node, Stream); + // check if the application actually wrote data + ChunkSize := Position - LastPosition - SizeOf(TChunkHeader); + // seek back to start of chunk if nothing has been written + if ChunkSize = 0 then + begin + Position := LastPosition; + Size := Size - SizeOf(Header); + end + else + FinishChunkHeader(Stream, LastPosition, Position); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.FullCollapse(Node: PVirtualNode = nil); +procedure TBaseVirtualTree.WriteNode(Stream: TStream; Node: PVirtualNode); -// This routine collapses all expanded nodes in the subtree given by Node or the whole tree if Node is FRoot or nil. -// Only nodes which are expanded will be collapsed. This excludes uninitialized nodes but nodes marked as visible -// will still be collapsed if they are expanded. +// Writes the "cover" chunk for Node to Stream and initiates writing child nodes and chunks. var - Stop: PVirtualNode; + LastPosition: Integer; + Header: TChunkHeader; begin - if FRoot.TotalCount > 1 then + // Initialize the node first if necessary and wanted. + if toInitOnSave in FOptions.MiscOptions then begin - if Node = FRoot then - Node := nil; - - DoStateChange([tsCollapsing]); - BeginUpdate; - try - Stop := Node; - Node := GetLastVisibleNoInit(Node, True); + if not (vsInitialized in Node.States) then + InitNode(Node); + if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then + InitChildren(Node); + end; - if Assigned(Node) then - begin - repeat - if [vsHasChildren, vsExpanded] * Node.States = [vsHasChildren, vsExpanded] then - ToggleNode(Node); - Node := GetPreviousNoInit(Node, True); - until (Node = Stop) or not Assigned(Node); + with Stream do + begin + LastPosition := Position; + // Emit the anchor chunk. + Header.ChunkType := NodeChunk; + Write(Header, SizeOf(Header)); + // Write other chunks to stream taking their size into this chunk's size. + WriteChunks(Stream, Node); - // Collapse the start node too. - if Assigned(Stop) and ([vsHasChildren, vsExpanded] * Stop.States = [vsHasChildren, vsExpanded]) then - ToggleNode(Stop); - end; - finally - EndUpdate; - DoStateChange([], [tsCollapsing]); - end; + // Update chunk size. + FinishChunkHeader(Stream, LastPosition, Position); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.FullExpand(Node: PVirtualNode = nil); - -// This routine expands all collapsed nodes in the subtree given by Node or the whole tree if Node is FRoot or nil. -// All nodes on the way down are initialized so this procedure might take a long time. -// Since all nodes are validated, the tree cannot make use of optimatizations. Hence it is counter productive and you -// should consider avoiding its use. - -var - Stop: PVirtualNode; +function TBaseVirtualTree.AbsoluteIndex(Node: PVirtualNode): Cardinal; begin - if FRoot.TotalCount > 1 then + Result := 0; + while Assigned(Node) and (Node <> FRoot) do begin - DoStateChange([tsExpanding]); - StartOperation(TVTOperationKind.okExpand); - BeginUpdate; - try - if Node = nil then - begin - Node := FRoot.FirstChild; - Stop := nil; - end - else - begin - Stop := Node.NextSibling; - if Stop = nil then - begin - Stop := Node; - repeat - Stop := Stop.Parent; - until (Stop = FRoot) or Assigned(Stop.NextSibling); - if Stop = FRoot then - Stop := nil - else - Stop := Stop.NextSibling; - end; - end; - - // Initialize the start node. Others will be initialized in GetNext. - if not (vsInitialized in Node.States) then - InitNode(Node); - - repeat - if not (vsExpanded in Node.States) then - ToggleNode(Node); - Node := GetNext(Node); - until (Node = Stop) or OperationCanceled; - finally - EndOperation(TVTOperationKind.okExpand); - EndUpdate; - DoStateChange([], [tsExpanding]); + if not (vsInitialized in Node.States) then + InitNode(Node); + if Assigned(Node.PrevSibling) then + begin + // if there's a previous sibling then add its total count to the result + Node := Node.PrevSibling; + Inc(Result, Node.TotalCount); + end + else + begin + Node := Node.Parent; + if Node <> FRoot then + Inc(Result); end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetControlsAlignment: TAlignment; +function TBaseVirtualTree.AddChild(Parent: PVirtualNode; UserData: Pointer = nil): PVirtualNode; + +// Adds a new node to the given parent node. This is simply done by increasing the child count of the +// parent node. If Parent is nil then the new node is added as (last) top level node. +// UserData can be used to set the first SizeOf(Pointer) bytes of the user data area to an initial value which can be used +// in OnInitNode and will also cause to trigger the OnFreeNode event (if <> nil) even if the node is not yet +// "officially" initialized. +// AddChild is a compatibility method and will implicitly validate the parent node. This is however +// against the virtual paradigm and hence I dissuade from its usage. begin - Result := FAlignment; + if not (toReadOnly in FOptions.MiscOptions) then + Result := InsertNode(Parent, TVTNodeAttachMode.amAddChildLast, UserData) + else + Result := nil; +end; + +function TBaseVirtualTree.AddChild(Parent: PVirtualNode; const UserData: IInterface): PVirtualNode; +begin + UserData._AddRef(); + Result := AddChild(Parent, Pointer(UserData)); + Include(Result.States, vsReleaseCallOnUserDataRequired); +end; + +function TBaseVirtualTree.AddChild(Parent: PVirtualNode; const UserData: TObject): PVirtualNode; +begin + Result := AddChild(Parent, Pointer(UserData)); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetDisplayRect(Node: PVirtualNode; Column: TColumnIndex; TextOnly: Boolean; - Unclipped: Boolean = False; ApplyCellContentMargin: Boolean = False): TRect; +procedure TBaseVirtualTree.AddFromStream(Stream: TStream; TargetNode: PVirtualNode); -// Determines the client coordinates the given node covers, depending on scrolling, expand state etc. -// If the given node cannot be found (because one of its parents is collapsed or it is invisible) then an empty -// rectangle is returned. -// If TextOnly is True then only the text bounds are returned, that is, the resulting rectangle's left and right border -// are updated according to bidi mode, alignment and text width of the node. -// If Unclipped is True (which only makes sense if also TextOnly is True) then the calculated text rectangle is -// not clipped if the text does not entirely fit into the text space. This is special handling needed for hints. -// If ApplyCellContentMargin is True (which only makes sense if also TextOnly is True) then the calculated text -// rectangle respects the cell content margin. -// If Column is -1 then the entire client width is used before determining the node's width otherwise the bounds of the -// particular column are used. -// Note: Column must be a valid column and is used independent of whether the header is visible or not. +// loads nodes from the given stream and adds them to TargetNode +// the current content is not cleared before the load process starts (see also LoadFromStream) var - Temp: PVirtualNode; - LeftOffset: Cardinal; - TopOffset: Cardinal; - CacheIsAvailable: Boolean; - TextWidth: Integer; - CurrentBidiMode: TBidiMode; - CurrentAlignment: TAlignment; - MaxUnclippedHeight: Integer; - TM: TTextMetric; - ExtraVerticalMargin: Integer; - lOffsets: TVTOffsets; + ThisID: TMagicID; + Version, + Count: Cardinal; + Node: PVirtualNode; + begin - Assert(Assigned(Node), 'Node must not be nil.'); - Assert(Node <> FRoot, 'Node must not be the hidden root node.'); + if not (toReadOnly in FOptions.MiscOptions) then + begin + // check first whether this is a stream we can read + Stream.ReadBuffer(ThisID, SizeOf(TMagicID)); + if (ThisID[0] = MagicID[0]) and + (ThisID[1] = MagicID[1]) and + (ThisID[2] = MagicID[2]) and + (ThisID[5] = MagicID[5]) then + begin + Version := Word(ThisID[3]); + if Version <= VTTreeStreamVersion then + begin + BeginUpdate; + try + if Version < 2 then + Count := MaxInt + else + Stream.ReadBuffer(Count, SizeOf(Count)); - if not (vsInitialized in Node.States) then - InitNode(Node); + while (Stream.Position < Stream.Size) and (Count > 0) do + begin + Dec(Count); + Node := MakeNewNode; + InternalConnectNode(Node, TargetNode, Self, amAddChildLast); + InternalAddFromStream(Stream, Version, Node); + end; + if TargetNode = FRoot then + DoNodeCopied(nil) + else + DoNodeCopied(TargetNode); + finally + EndUpdate; + end; + end + else + ShowError(SWrongStreamVersion, hcTFWrongStreamVersion); + end + else + ShowError(SWrongStreamVersion, hcTFWrongStreamVersion); + end; +end; - Result := Rect(0, 0, 0, 0); +//---------------------------------------------------------------------------------------------------------------------- - // Check whether the node is visible (determine indentation level btw.). - if not IsEffectivelyVisible[Node] then - Exit; +procedure TBaseVirtualTree.AfterConstruction; - // Here we know the node is visible. - TopOffset := 0; - CacheIsAvailable := False; - if tsUseCache in FStates then - begin - // If we can use the position cache then do a binary search to find a cached node which is as close as possible - // to the current node. Iterate then through all following and visible nodes and sum up their heights. - Temp := FindInPositionCache(Node, TopOffset); - CacheIsAvailable := Assigned(Temp); - while Assigned(Temp) and (Temp <> Node) do - begin - Inc(TopOffset, NodeHeight[Temp]); - Temp := GetNextVisibleNoInit(Temp, True); - end; - end; - if not CacheIsAvailable then - begin - // If the cache is not available then go straight through all nodes up to the root and sum up their heights. - Temp := Node; - repeat - Temp := GetPreviousVisibleNoInit(Temp, True); - if Temp = nil then - Break; - Inc(TopOffset, NodeHeight[Temp]); - until False; - end; +begin + inherited; - Result := Rect(0, TopOffset, Max(FRangeX, ClientWidth), TopOffset + NodeHeight[Node]); + if FRoot = nil then + InitRootNode; +end; - // Limit left and right bounds to the given column (if any) and move bounds according to current scroll state. - if Column > NoColumn then - begin - FHeader.Columns.GetColumnBounds(Column, Result.Left, Result.Right); - // The right column border is not part of this cell. - Dec(Result.Right); - OffsetRect(Result, 0, FOffsetY); - end - else - OffsetRect(Result, -FEffectiveOffsetX, FOffsetY); +//---------------------------------------------------------------------------------------------------------------------- - // Limit left and right bounds further if only the text area is required. - if TextOnly then - begin - // If the text of a node is involved then we have to consider directionality and alignment too. - if Column <= NoColumn then - begin - CurrentBidiMode := BidiMode; - CurrentAlignment := Alignment; - end - else - begin - CurrentBidiMode := FHeader.Columns[Column].BidiMode; - CurrentAlignment := FHeader.Columns[Column].Alignment; - end; +procedure TBaseVirtualTree.Assign(Source: TPersistent); - GetOffsets(Node, lOffsets, TVTElement.ofsLabel, Column); - LeftOffset := lOffSets[TVTElement.ofsLabel]; - // Offset contains now the distance from the left or right border of the rectangle (depending on bidi mode). - // Now consider the alignment too and calculate the final result. - if CurrentBidiMode = bdLeftToRight then +begin + if (Source is TBaseVirtualTree) and not (toReadOnly in FOptions.MiscOptions) then + with Source as TBaseVirtualTree do begin - Inc(Result.Left, LeftOffset); - // Left-to-right reading does not need any special adjustment of the alignment. + Self.Align := Align; + Self.Anchors := Anchors; + Self.AutoScrollDelay := AutoScrollDelay; + Self.AutoScrollInterval := AutoScrollInterval; + Self.AutoSize := AutoSize; + Self.Background := Background; + Self.BevelEdges := BevelEdges; + Self.BevelInner := BevelInner; + Self.BevelKind := BevelKind; + Self.BevelOuter := BevelOuter; + Self.BevelWidth := BevelWidth; + Self.BiDiMode := BiDiMode; + Self.BorderStyle := BorderStyle; + Self.BorderWidth := BorderWidth; + Self.ChangeDelay := ChangeDelay; + Self.CheckImageKind := CheckImageKind; + Self.Color := Color; + Self.Colors.Assign(Colors); + Self.Constraints.Assign(Constraints); + Self.Ctl3D := Ctl3D; + Self.DefaultNodeHeight := DefaultNodeHeight; + Self.DefaultPasteMode := DefaultPasteMode; + Self.DragCursor := DragCursor; + Self.DragImageKind := DragImageKind; + Self.DragKind := DragKind; + Self.DragMode := DragMode; + Self.Enabled := Enabled; + Self.Font := Font; + Self.Header := Header; + Self.HintMode := HintMode; + Self.HotCursor := HotCursor; + Self.Images := Images; + Self.ImeMode := ImeMode; + Self.ImeName := ImeName; + Self.Indent := Indent; + Self.Margin := Margin; + Self.NodeAlignment := NodeAlignment; + Self.NodeDataSize := NodeDataSize; + Self.TreeOptions := TreeOptions; + Self.ParentBiDiMode := ParentBiDiMode; + Self.ParentColor := ParentColor; + Self.ParentCtl3D := ParentCtl3D; + Self.ParentFont := ParentFont; + Self.ParentShowHint := ParentShowHint; + Self.PopupMenu := PopupMenu; + Self.RootNodeCount := RootNodeCount; + Self.ScrollBarOptions := ScrollBarOptions; + Self.ShowHint := ShowHint; + Self.StateImages := StateImages; + Self.StyleElements := StyleElements; + Self.TabOrder := TabOrder; + Self.TabStop := TabStop; + Self.Visible := Visible; + Self.SelectionCurveRadius := SelectionCurveRadius; + Self.SelectionBlendFactor := SelectionBlendFactor; + Self.EmptyListMessage := EmptyListMessage; end else - begin - Dec(Result.Right, LeftOffset); + inherited; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.AutoScale(isDpiChange: Boolean); + +// If toAutoChangeScale is set, this method ensures that the defaulz node height is set correctly. +// isDPIChnage is True, if the DPI of the form has changed. In this case the font may not yet be adapted to this, so do not adjust DefualtNodeHeight. + +var + lTextHeight: Cardinal; +begin + if HandleAllocated and (toAutoChangeScale in TreeOptions.AutoOptions) and not isDpiChange then + begin + Canvas.Font.Assign(Self.Font); + lTextHeight := Canvas.TextHeight('Tg') + 2; + // By default, we only ensure that DefaultNodeHeight is large enough. + // If the form's dpi has changed, we scale up and down the DefaultNodeHeight, See issue #677. + if (lTextHeight > Self.DefaultNodeHeight) then + Self.DefaultNodeHeight := lTextHeight; + end; +end; - // Consider bidi mode here. In RTL context does left alignment actually mean right alignment and vice versa. - ChangeBiDiModeAlignment(CurrentAlignment); +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.BeginDrag(Immediate: Boolean; Threshold: Integer); + +// Reintroduced method to allow to start OLE drag'n drop as well as VCL drag'n drop. + +begin + if FDragType = dtVCL then + begin + DoStateChange([tsVCLDragPending]); + inherited; + end + else + if (FStates * [tsOLEDragPending, tsOLEDragging]) = [] then + begin + // Drag start position has already been recorded in WMMouseDown. + if Threshold < 0 then + FDragThreshold := Mouse.DragThreshold + else + FDragThreshold := Threshold; + if Immediate then + DoDragging(FLastClickPos) + else + DoStateChange([tsOLEDragPending]); end; +end; - TextWidth := DoGetNodeWidth(Node, Column); +//---------------------------------------------------------------------------------------------------------------------- - // Keep cell height before applying cell content margin in order to increase cell height if text does not fit - // and Unclipped it true (see below). - MaxUnclippedHeight := Result.Bottom - Result.Top; +procedure TBaseVirtualTree.BeginSynch; - if ApplyCellContentMargin then - DoBeforeCellPaint(Self.Canvas, Node, Column, cpmGetContentMargin, Result, Result); +// Starts the synchronous update mode (if not already active). - if Unclipped then +begin + if not (csDestroying in ComponentState) then + begin + if FSynchUpdateCount = 0 then begin - // The caller requested the text coordinates unclipped. This means they must be calculated so as would - // there be enough space, regardless of column bounds etc. - // The layout still depends on the available space too, because this determines the position - // of the unclipped text rectangle. - if Result.Right - Result.Left < TextWidth - 1 then - if CurrentBidiMode = bdLeftToRight then - CurrentAlignment := taLeftJustify - else - CurrentAlignment := taRightJustify; + DoUpdating(usBeginSynch); - // Increase cell height (up to MaxUnclippedHeight determined above) if text does not fit. - GetTextMetrics(Self.Canvas.Handle, TM); - ExtraVerticalMargin := System.Math.Min(TM.tmHeight, MaxUnclippedHeight) - (Result.Bottom - Result.Top); - if ExtraVerticalMargin > 0 then - InflateRect(Result, 0, (ExtraVerticalMargin + 1) div 2); + // Stop all timers... + StopTimer(ChangeTimer); + StopTimer(StructureChangeTimer); + StopTimer(ExpandTimer); + StopTimer(EditTimer); + StopTimer(HeaderTimer); + StopTimer(ScrollTimer); + StopTimer(SearchTimer); + FSearchBuffer := ''; + FLastSearchNode := nil; + DoStateChange([], [tsEditPending, tsScrollPending, tsScrolling, tsIncrementalSearching]); - case CurrentAlignment of - taCenter: - begin - Result.Left := (Result.Left + Result.Right - TextWidth) div 2; - Result.Right := Result.Left + TextWidth; - end; - taRightJustify: - Result.Left := Result.Right - TextWidth; - else // taLeftJustify - Result.Right := Result.Left + TextWidth - 1; - end; + // ...and trigger pending update states. + if tsStructureChangePending in FStates then + DoStructureChange(FLastStructureChangeNode, FLastStructureChangeReason); + if tsChangePending in FStates then + DoChange(FLastChangedNode); end else - // Modify rectangle only if the text fits entirely into the given room. - if Result.Right - Result.Left > TextWidth then - case CurrentAlignment of - taCenter: - begin - Result.Left := (Result.Left + Result.Right - TextWidth) div 2; - Result.Right := Result.Left + TextWidth; - end; - taRightJustify: - Result.Left := Result.Right - TextWidth; - else // taLeftJustify - Result.Right := Result.Left + TextWidth; - end; + DoUpdating(usSynch); end; + Inc(FSynchUpdateCount); + DoStateChange([tsSynchMode]); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetEffectivelyFiltered(Node: PVirtualNode): Boolean; - -// Checks if a node is effectively filtered out. This depends on the nodes state and the paint options. +procedure TBaseVirtualTree.BeginUpdate; begin - if Assigned(Node) then - Result := (vsFiltered in Node.States) and not (toShowFilteredNodes in FOptions.PaintOptions) - else - Result := False; + Assert(GetCurrentThreadId = MainThreadId, 'UI controls like ' + Classname + ' should only be manipulated through the main thread.'); + if not (csDestroying in ComponentState) then + begin + if FUpdateCount = 0 then + begin + DoUpdating(usBegin); + SetUpdateState(True); + end + else + DoUpdating(usUpdate); + end; + Inc(FUpdateCount); + DoStateChange([tsUpdating]); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetEffectivelyVisible(Node: PVirtualNode): Boolean; +procedure TBaseVirtualTree.CancelCutOrCopy; + +// Resets nodes which are marked as being cut. + +var + Run: PVirtualNode; begin - Result := (vsVisible in Node.States) and not IsEffectivelyFiltered[Node]; + if ([tsCutPending, tsCopyPending] * FStates) <> [] then + begin + Run := FRoot.FirstChild; + while Assigned(Run) do + begin + if vsCutOrCopy in Run.States then + Exclude(Run.States, vsCutOrCopy); + Run := GetNextNoInit(Run); + end; + end; + DoStateChange([], [tsCutPending, tsCopyPending]); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirst(ConsiderChildrenAbove: Boolean = False): PVirtualNode; +function TBaseVirtualTree.CancelEditNode: Boolean; -// Returns the first node in the tree while optionally considering toChildrenAbove. +// Called by the application or the current edit link to cancel the edit action. begin - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then - begin - if vsHasChildren in FRoot.States then - begin - Result := FRoot; - - // Child nodes are the first choice if possible. - if Assigned(Result.FirstChild) then - begin - while Assigned(Result.FirstChild) do - begin - Result := Result.FirstChild; - if not (vsInitialized in Result.States) then - InitNode(Result); - - if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then - InitChildren(Result); - end; - end - else - Result := nil; - end - else - Result := nil; - end + if HandleAllocated and ([tsEditing, tsEditPending] * FStates <> []) then + Result := DoCancelEdit else - Result := FRoot.FirstChild; - - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); + Result := True; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstChecked(State: TCheckState = csCheckedNormal; - ConsiderChildrenAbove: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.CancelOperation; -// Returns the first node in the tree with the given check state. +// Called by the application to cancel a long-running operation. begin - Result := GetNextChecked(nil, State, ConsiderChildrenAbove); + if FOperationCount > 0 then + FOperationCanceled := True; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstChild(Node: PVirtualNode): PVirtualNode; +function TBaseVirtualTree.CanEdit(Node: PVirtualNode; Column: TColumnIndex): Boolean; -// Returns the first child of the given node. The result node is initialized before exit. +// Returns True if the given node can be edited. begin - if (Node = nil) or (Node = FRoot) then - Result := FRoot.FirstChild - else - begin - if not (vsInitialized in Node.States) then - InitNode(Node); - if vsHasChildren in Node.States then - begin - if Node.ChildCount = 0 then - InitChildren(Node); - Result := Node.FirstChild; - end - else - Result := nil; - end; - - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); + Result := (toEditable in FOptions.MiscOptions) and Enabled and not (toReadOnly in FOptions.MiscOptions) + and ((Column < 0) or (coEditable in FHeader.Columns[Column].Options)); + DoCanEdit(Node, Column, Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstChildNoInit(Node: PVirtualNode): PVirtualNode; -// Determines the first child of the given node but does not initialize it. +function TBaseVirtualTree.CanFocus: Boolean; + +var + Form: TCustomForm; begin - if (Node = nil) or (Node = FRoot) then - Result := FRoot.FirstChild - else + Result := inherited CanFocus; + + if Result and not (csDesigning in ComponentState) then begin - if vsHasChildren in Node.States then - Result := Node.FirstChild - else - Result := nil; + Form := GetParentForm(Self); + Result := (Form = nil) or (Form.Enabled and Form.Visible); end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstCutCopy(ConsiderChildrenAbove: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.Clear; -// Returns the first node in the tree which is currently marked for a clipboard operation. -// See also GetNextCutCopy for comments on initialization. +begin + if (not IsEmpty and not (toReadOnly in FOptions.MiscOptions)) or (csDestroying in ComponentState) then + begin + BeginUpdate; + try + InterruptValidation; + if IsEditing then + CancelEditNode; + + if ClipboardStates * FStates <> [] then + begin + OleSetClipboard(nil); + DoStateChange([], ClipboardStates); + end; + ClearSelection; + FFocusedNode := nil; + FLastSelected := nil; + FCurrentHotNode := nil; + FDropTargetNode := nil; + FLastChangedNode := nil; + FRangeAnchor := nil; + FLastVCLDragTarget := nil; + FLastSearchNode := nil; + DeleteChildren(FRoot, True); + FOffsetX := 0; + FOffsetY := 0; -begin - Result := GetNextCutCopy(nil, ConsiderChildrenAbove); + finally + EndUpdate; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstInitialized(ConsiderChildrenAbove: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.ClearChecked; -// Returns the first node which is already initialized. +var + Node: PVirtualNode; begin - Result := GetFirstNoInit(ConsiderChildrenAbove); - if Assigned(Result) and not (vsInitialized in Result.States) then - Result := GetNextInitialized(Result, ConsiderChildrenAbove); + Node := RootNode.FirstChild; + while Assigned(Node) do + begin + if Node.CheckState <> csUncheckedNormal then + CheckState[Node] := csUncheckedNormal; + Node := GetNextNoInit(Node); + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstLeaf: PVirtualNode; - -// Returns the first node in the tree which has currently no children. -// The result is initialized if necessary. - +procedure TBaseVirtualTree.ClearSelection(); begin - Result := GetNextLeaf(nil); + ClearSelection(True); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstLevel(NodeLevel: Cardinal): PVirtualNode; - -// Returns the first node in the tree on a specific level. -// The result is initialized if necessary. - +procedure TBaseVirtualTree.ClearDragManager; begin - Result := GetFirstNoInit(True); - while Assigned(Result) and (GetNodeLevel(Result) <> NodeLevel) do - Result := GetNextNoInit(Result, True); - - if Assigned(Result) and (GetNodeLevel(Result) <> NodeLevel) then // i.e. there is no node with the desired level in the tree - Result := nil; - - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); + Pointer(FDragManager) := nil; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstNoInit(ConsiderChildrenAbove: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.ClearSelection(pFireChangeEvent: Boolean); -// Returns the first node in the tree while optionally considering toChildrenAbove. -// No initialization is performed. +var + Node: PVirtualNode; + Dummy: Integer; + R: TRect; + Counter: Integer; begin - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then + Assert(GetCurrentThreadId = MainThreadId, Self.Classname + '.ClearSelection() must only be called from UI thread.'); + if not FSelectionLocked and (FSelectionCount > 0) and not (csDestroying in ComponentState) then begin - if vsHasChildren in FRoot.States then + if (FUpdateCount = 0) and HandleAllocated and (FVisibleCount > 0) then begin - Result := FRoot; + // Iterate through nodes currently visible in the client area and invalidate them. + Node := GetNodeAt(0, 0, True, Dummy); + if Assigned(Node) then + R := GetDisplayRect(Node, NoColumn, False); + Counter := FSelectionCount; - // Child nodes are the first choice if possible. - if Assigned(Result.FirstChild) then + while Assigned(Node) do begin - while Assigned(Result.FirstChild) do - Result := Result.FirstChild; - end - else - Result := nil; - end - else - Result := nil; - end - else - Result := FRoot.FirstChild; + R.Bottom := R.Top + Integer(NodeHeight[Node]); + if vsSelected in Node.States then + begin + InvalidateRect(Handle, @R, False); + Dec(Counter); + // Only try as many nodes as are selected. + if Counter = 0 then + Break; + end; + R.Top := R.Bottom; + if R.Top > ClientHeight then + Break; + Node := GetNextVisibleNoInit(Node, True); + end; + end; + + InternalClearSelection; + if pFireChangeEvent then + Change(nil); + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstSelected(ConsiderChildrenAbove: Boolean = False): PVirtualNode; +function TBaseVirtualTree.CopyTo(Source: PVirtualNode; Tree: TBaseVirtualTree; Mode: TVTNodeAttachMode; + ChildrenOnly: Boolean): PVirtualNode; -// Returns the first node in the current selection while optionally considering toChildrenAbove. +// A simplified CopyTo method to allow to copy nodes to the root of another tree. begin - Result := GetNextSelected(nil, ConsiderChildrenAbove); + Result := CopyTo(Source, Tree.FRoot, Mode, ChildrenOnly); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstVisible(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True; - IncludeFiltered: Boolean = False): PVirtualNode; +function TBaseVirtualTree.CopyTo(Source, Target: PVirtualNode; Mode: TVTNodeAttachMode; + ChildrenOnly: Boolean): PVirtualNode; -// Returns the first visible node in the tree while optionally considering toChildrenAbove. -// If necessary nodes are initialized on demand. +// Copies Source and all its child nodes to Target. +// Mode is used to specify further where to add the new node actually (as sibling of Target or as child of Target). +// Result is the newly created node to which source has been copied if ChildrenOnly is False or just contains Target +// in the other case. +// ChildrenOnly determines whether to copy also the source node or only its child nodes. + +var + TargetTree: TBaseVirtualTree; + Stream: TMemoryStream; begin - Result := Node; - if not Assigned(Result) then - Result := FRoot; + Assert(TreeFromNode(Source) = Self, 'The source tree must contain the source node.'); - if vsHasChildren in Result.States then + Result := nil; + if (Mode <> amNoWhere) and Assigned(Source) and (Source <> FRoot) then begin - if Result.ChildCount = 0 then - InitChildren(Result); - - // Child nodes are the first choice if possible. - if Assigned(Result.FirstChild) then + // Assume that an empty destination means the root in this (the source) tree. + if Target = nil then begin - Result := GetFirstChild(Result); + TargetTree := Self; + Target := FRoot; + Mode := amAddChildFirst; + end + else + TargetTree := TreeFromNode(Target); - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then + if not (toReadOnly in TargetTree.TreeOptions.MiscOptions) then + begin + if Target = TargetTree.FRoot then begin - repeat - // Search the first visible sibling. - while Assigned(Result.NextSibling) and not (vsVisible in Result.States) do - begin - Result := Result.NextSibling; - // Init node on demand as this might change the visibility. - if not (vsInitialized in Result.States) then - InitNode(Result); - end; + case Mode of + amInsertBefore: + Mode := amAddChildFirst; + amInsertAfter: + Mode := amAddChildLast; + end; + end; - // If there are no visible siblings take the parent. - if not (vsVisible in Result.States) then - begin - Result := Result.Parent; - if Result = FRoot then - Result := nil; - Break; - end - else + Stream := TMemoryStream.Create; + try + // Write all nodes into a temprary stream depending on the ChildrenOnly flag. + if not ChildrenOnly then + WriteNode(Stream, Source) + else + begin + Source := Source.FirstChild; + while Assigned(Source) do begin - if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then - InitChildren(Result); - if (not Assigned(Result.FirstChild)) or (not (vsExpanded in Result.States)) then - Break; + WriteNode(Stream, Source); + Source := Source.NextSibling; end; - - Result := Result.FirstChild; - if not (vsInitialized in Result.States) then - InitNode(Result); - until False; - end - else - begin - // If there are no children or the first child is not visible then search the sibling nodes or traverse parents. - if not (vsVisible in Result.States) then - begin - repeat - // Is there a next sibling? - if Assigned(Result.NextSibling) then + end; + // Now load the serialized nodes into the target node (tree). + TargetTree.BeginUpdate; + try + Stream.Position := 0; + while Stream.Position < Stream.Size do + begin + Result := TargetTree.MakeNewNode; + InternalConnectNode(Result, Target, TargetTree, Mode); + TargetTree.InternalAddFromStream(Stream, VTTreeStreamVersion, Result); + if not DoNodeCopying(Result, Target) then begin - Result := Result.NextSibling; - // The visible state can be removed during initialization so init the node first. - if not (vsInitialized in Result.States) then - InitNode(Result); - if vsVisible in Result.States then - Break; + TargetTree.DeleteNode(Result); + Result := nil; end else - begin - // No sibling anymore, so use the parent's next sibling. - if Result.Parent <> FRoot then - Result := Result.Parent - else - begin - // There are no further nodes to examine, hence there is no further visible node. - Result := nil; - Break; - end; - end; - until False; + DoNodeCopied(Result); + end; + if ChildrenOnly then + Result := Target; + finally + TargetTree.EndUpdate; end; + finally + Stream.Free; end; - end - else - Result := nil; - end - else - Result := nil; - if Assigned(Result) and not IncludeFiltered and IsEffectivelyFiltered[Result] then - Result := GetNextVisible(Result); + with TargetTree do + begin + InvalidateCache; + if FUpdateCount = 0 then + begin + ValidateCache; + UpdateScrollBars(True); + Invalidate; + end; + StructureChange(Source, crNodeCopied); + end; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstVisibleChild(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.CopyToClipboard; -// Returns the first visible child node of Node. If necessary nodes are initialized on demand. +var + DataObject: IDataObject; begin - if Node = nil then - Node := FRoot; - Result := GetFirstChild(Node); + if FSelectionCount > 0 then + begin + DataObject := TVTDataObject.Create(Self, True) as IDataObject; + if OleSetClipboard(DataObject) = S_OK then + begin + MarkCutCopyNodes; + DoStateChange([tsCopyPending]); + Invalidate; + end; + end; +end; - if Assigned(Result) and (not (vsVisible in Result.States) or - (not IncludeFiltered and IsEffectivelyFiltered[Result])) then - Result := GetNextVisibleSibling(Result, IncludeFiltered); +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.CutToClipboard; +begin + if (FSelectionCount > 0) and not (toReadOnly in FOptions.MiscOptions) then + begin + if OleSetClipboard(TVTDataObject.Create(Self, True)) = S_OK then + begin + MarkCutCopyNodes; + DoStateChange([tsCutPending], [tsCopyPending]); + Invalidate; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstVisibleChildNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.DeleteChildren(Node: PVirtualNode; ResetHasChildren: Boolean = False); -// Returns the first visible child node of Node. +// Removes all children and their children from memory without changing the vsHasChildren style by default. + +var + Run, + Mark: PVirtualNode; + LastTop, + LastLeft, + NewSize: Integer; + ParentVisible: Boolean; begin - if Node = nil then - Node := FRoot; - Result := Node.FirstChild; - if Assigned(Result) and (not (vsVisible in Result.States) or - (not IncludeFiltered and IsEffectivelyFiltered[Result])) then - Result := GetNextVisibleSiblingNoInit(Result, IncludeFiltered); + if Assigned(Node) and (Node.ChildCount > 0) and not (toReadOnly in FOptions.MiscOptions) then + begin + Assert(not (tsIterating in FStates), 'Deleting nodes during tree iteration leads to invalid pointers.'); + + // The code below uses some flags for speed improvements which may cause invalid pointers if updates of + // the tree happen. Hence switch updates off until we have finished the operation. + Inc(FUpdateCount); + try + InterruptValidation; + LastLeft := -FEffectiveOffsetX; + LastTop := FOffsetY; + + // Make a local copy of the visibility state of this node to speed up + // adjusting the visible nodes count. + ParentVisible := Node = FRoot; + if not ParentVisible then + ParentVisible := FullyVisible[Node] and (vsExpanded in Node.States); + + // Show that we are clearing the child list, to avoid registering structure change events. + Run := Node.LastChild; + while Assigned(Run) do + begin + if ParentVisible and IsEffectivelyVisible[Run] then + Dec(FVisibleCount); + + Include(Run.States, vsDeleting); + Mark := Run; + Run := Run.PrevSibling; + // Important, to avoid exchange of invalid pointers while disconnecting the node. + if Assigned(Run) then + Run.NextSibling := nil; + DeleteNode(Mark, False, True); + end; + if ResetHasChildren then + Exclude(Node.States, vsHasChildren); + if Node <> FRoot then + Exclude(Node.States, vsExpanded); + Node.ChildCount := 0; + if (Node = FRoot) or (vsDeleting in Node.States) then + begin + Node.TotalHeight := FDefaultNodeHeight + NodeHeight[Node]; + Node.TotalCount := 1; + end + else + begin + AdjustTotalHeight(Node, NodeHeight[Node]); + AdjustTotalCount(Node, 1); + end; + Node.FirstChild := nil; + Node.LastChild := nil; + finally + Dec(FUpdateCount); + end; + + InvalidateCache; + if FUpdateCount = 0 then + begin + NewSize := PackArray(FSelection, FSelectionCount); + if NewSize > -1 then + begin + FSelectionCount := NewSize; + SetLength(FSelection, FSelectionCount); + end; + + ValidateCache; + UpdateScrollBars(True); + // Invalidate entire tree if it scrolled e.g. to make the last node also the + // bottom node in the treeview. + if (LastLeft <> FOffsetX) or (LastTop <> FOffsetY) then + Invalidate + else + InvalidateToBottom(Node); + if tsChangePending in FStates then begin + DoChange(FLastChangedNode); + EnsureNodeSelected(); + end; + end; + StructureChange(Node, crChildDeleted); + end + else if ResetHasChildren then + Exclude(Node.States, vsHasChildren); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstVisibleNoInit(Node: PVirtualNode = nil; - ConsiderChildrenAbove: Boolean = True; IncludeFiltered: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.DeleteNode(Node: PVirtualNode; Reindex: Boolean; ParentClearing: Boolean); -// Returns the first visible node in the tree or given subtree while optionally considering toChildrenAbove. -// No initialization is performed. +var + LastTop, + LastLeft: Integer; + LastParent: PVirtualNode; + WasInSynchMode: Boolean; begin - Result := Node; - if not Assigned(Result) then - Result := FRoot; - - if vsHasChildren in Result.States then + if Assigned(Node) and (Node <> FRoot) and not (toReadOnly in FOptions.MiscOptions) then begin - // Child nodes are the first choice if possible. - if Assigned(Result.FirstChild) then + Assert(not (tsIterating in FStates), 'Deleting nodes during tree iteration leads to invalid pointers.'); + + // Determine parent node for structure change notification. + LastParent := Node.Parent; + + if not ParentClearing then begin - Result := Result.FirstChild; + if LastParent = FRoot then + StructureChange(nil, crChildDeleted) + else + StructureChange(LastParent, crChildDeleted); + if Node = FNextNodeToSelect then + FNextNodeToSelect := nil; + end; - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then - begin - repeat - // Search the first visible sibling. - while Assigned(Result.NextSibling) and not (vsVisible in Result.States) do - Result := Result.NextSibling; + LastLeft := -FEffectiveOffsetX; + LastTop := FOffsetY; - // If there a no visible siblings take the parent. - if not (vsVisible in Result.States) then - begin - Result := Result.Parent; - if Result = FRoot then - Result := nil; - Break; - end - else - if (not Assigned(Result.FirstChild)) or (not (vsExpanded in Result.States))then - Break; + if tsHint in FStates then + begin + Application.CancelHint; + DoStateChange([], [tsHint]); + end; - Result := Result.FirstChild; - until False; + if not ParentClearing then + InterruptValidation; + + DeleteChildren(Node); + + if vsSelected in Node.States then + begin + if FUpdateCount = 0 then + begin + // Go temporarily into sync mode to avoid a delayed change event for the node + // when unselecting. + WasInSynchMode := tsSynchMode in FStates; + Include(FStates, tsSynchMode); + RemoveFromSelection(Node); + //EnsureNodeSelected(); // also done in DoFreeNode() + if not WasInSynchMode then + Exclude(FStates, tsSynchMode); + InvalidateToBottom(LastParent); end else + InternalRemoveFromSelection(Node); + end + else + InvalidateToBottom(LastParent); + + InternalDisconnectNode(Node, False, Reindex); + DoFreeNode(Node); + + if not ParentClearing then + begin + DetermineHiddenChildrenFlag(LastParent); + InvalidateCache; + if FUpdateCount = 0 then begin - // If there are no children or the first child is not visible then search the sibling nodes or traverse parents. - if not (vsVisible in Result.States) then - begin - repeat - // Is there a next sibling? - if Assigned(Result.NextSibling) then - begin - Result := Result.NextSibling; - if vsVisible in Result.States then - Break; - end - else - begin - // No sibling anymore, so use the parent's next sibling. - if Result.Parent <> FRoot then - Result := Result.Parent - else - begin - // There are no further nodes to examine, hence there is no further visible node. - Result := nil; - Break; - end; - end; - until False; - end; + ValidateCache; + UpdateScrollBars(True); + // Invalidate entire tree if it scrolled e.g. to make the last node also the + // bottom node in the treeview. + if (LastLeft <> FOffsetX) or (LastTop <> FOffsetY) then + Invalidate; end; - end - else - Result := nil; - end - else - Result := nil; + end; + end; +end; - if Assigned(Result) and not IncludeFiltered and IsEffectivelyFiltered[Result] then - Result := GetNextVisibleNoInit(Result); +procedure TBaseVirtualTree.DeleteNode(Node: PVirtualNode; pReIndex: Boolean = True); +begin + DeleteNode(Node, pReIndex, False); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.GetHitTestInfoAt(X, Y: Integer; Relative: Boolean; var HitInfo: THitInfo); +procedure TBaseVirtualTree.DeleteNodes(const pNodes: TNodeArray); -// Determines the node that occupies the specified point or nil if there's none. The parameter Relative determines -// whether to consider X and Y as being client coordinates (if True) or as being absolute tree coordinates. -// HitInfo is filled with flags describing the hit further. + // Deletes all given nodes. + // Best performance is achieved if nodes are sorted by parent var - ColLeft, - ColRight: Integer; - NodeTop: Integer; - InitialColumn, - NextColumn: TColumnIndex; - CurrentBidiMode: TBidiMode; - CurrentAlignment: TAlignment; - NodeRect: TRect; - + I: Integer; + LevelChange: Boolean; begin - HitInfo.HitNode := nil; - HitInfo.HitPositions := []; - HitInfo.HitColumn := NoColumn; + BeginUpdate; + try + for I := High(pNodes) downto 1 do + begin + LevelChange := pNodes[I].Parent <> pNodes[I - 1].Parent; + DeleteNode(pNodes[I], LevelChange, False); + end; + DeleteNode(pNodes[0]); + finally + EndUpdate; + end; +end; - // Determine if point lies in the tree's client area. - if X < 0 then - Include(HitInfo.HitPositions, hiToLeft) - else - if X > Max(FRangeX, ClientWidth) then - Include(HitInfo.HitPositions, hiToRight); +//---------------------------------------------------------------------------------------------------------------------- - if Y < 0 then - Include(HitInfo.HitPositions, hiAbove) - else - if Y > Max(FRangeY, ClientHeight) then - Include(HitInfo.HitPositions, hiBelow); +procedure TBaseVirtualTree.DeleteSelectedNodes; - // Convert position into absolute coordinate if necessary. - if Relative then +// Deletes all currently selected nodes (including their child nodes). + +var + lNodes: TNodeArray; +begin + lNodes := nil; + if (FSelectionCount > 0) and not (toReadOnly in FOptions.MiscOptions) then begin - if X >= Header.Columns.GetVisibleFixedWidth then - Inc(X, FEffectiveOffsetX); - Inc(Y, -FOffsetY); + lNodes := GetSortedSelection(True); + DeleteNodes(lNodes); end; - HitInfo.HitPoint.X := X; - HitInfo.HitPoint.Y := Y; +end; - // If the point is in the tree area then check the nodes. - if HitInfo.HitPositions = [] then +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.Dragging: Boolean; + +begin + // Check for both OLE drag'n drop as well as VCL drag'n drop. + Result := ([tsOLEDragPending, tsOLEDragging] * FStates <> []) or inherited Dragging; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.EditNode(Node: PVirtualNode; Column: TColumnIndex): Boolean; + +// Application triggered edit event for the given node. +// Returns True if the tree started editing otherwise False. + +begin + Assert(Assigned(Node), 'Node must not be nil.'); + Assert((Column > InvalidColumn) and (Column < FHeader.Columns.Count), + 'Column must be a valid column index (-1 if no header is shown).'); + + Result := tsEditing in FStates; + // If the tree is already editing then we don't disrupt this. + if not Result and not (toReadOnly in FOptions.MiscOptions) then begin - HitInfo.HitNode := GetNodeAt(X, Y, False, NodeTop); - if HitInfo.HitNode = nil then - Include(HitInfo.HitPositions, hiNowhere) - else + FocusedNode := Node; + if Assigned(FFocusedNode) and (Node = FFocusedNode) and CanEdit(FFocusedNode, Column) then begin - // At this point we need some info about the node, so it must be initialized. - if not (vsInitialized in HitInfo.HitNode.States) then - InitNode(HitInfo.HitNode); + FEditColumn := Column; + if not (vsInitialized in Node.States) then + InitNode(Node); + DoEdit; + Result := tsEditing in FStates; + end + else + Result := False; + end; +end; - if FHeader.UseColumns then - begin - HitInfo.HitColumn := FHeader.Columns.GetColumnAndBounds(Point(X, Y), ColLeft, ColRight, False); - // If auto column spanning is enabled then look for the last non empty column. - if toAutoSpanColumns in FOptions.AutoOptions then - begin - InitialColumn := HitInfo.HitColumn; - // Search to the left of the hit column for empty columns. - while (HitInfo.HitColumn > NoColumn) and ColumnIsEmpty(HitInfo.HitNode, HitInfo.HitColumn) do - begin - NextColumn := FHeader.Columns.GetPreviousVisibleColumn(HitInfo.HitColumn); - if NextColumn = InvalidColumn then - Break; - HitInfo.HitColumn := NextColumn; - Dec(ColLeft, FHeader.Columns[NextColumn].Width); - end; - // Search to the right of the hit column for empty columns. - repeat - InitialColumn := FHeader.Columns.GetNextVisibleColumn(InitialColumn); - if (InitialColumn = InvalidColumn) or not ColumnIsEmpty(HitInfo.HitNode, InitialColumn) then - Break; - Inc(ColRight, FHeader.Columns[InitialColumn].Width); - until False; - end; - // Make the X position and the right border relative to the start of the column. - Dec(X, ColLeft); - Dec(ColRight, ColLeft); - end - else - begin - HitInfo.HitColumn := NoColumn; - ColRight := Max(FRangeX, ClientWidth); - end; - ColLeft := 0; +//---------------------------------------------------------------------------------------------------------------------- - if HitInfo.HitColumn = InvalidColumn then - Include(HitInfo.HitPositions, hiNowhere) - else - begin - // From now on X is in "column" coordinates (relative to the left column border). - HitInfo.HitPositions := [hiOnItem]; +function TBaseVirtualTree.EndEditNode: Boolean; - // Avoid getting the display rect if this is not necessary. - if toNodeHeightResize in FOptions.MiscOptions then - begin - NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, False); - if Y <= (NodeRect.Top - FOffsetY + 1) then - Include(HitInfo.HitPositions, hiUpperSplitter) - else - if Y >= (NodeRect.Bottom - FOffsetY - 3) then - Include(HitInfo.HitPositions, hiLowerSplitter); - end; +// Called to finish a current edit action or stop the edit timer if an edit operation is pending. +// Returns True if editing was successfully ended or the control was not in edit mode +// Returns False if the control could not leave the edit mode e.g. due to an invalid value that was entered. - if HitInfo.HitColumn <= NoColumn then - begin - CurrentBidiMode := BidiMode; - CurrentAlignment := Alignment; - end - else - begin - CurrentBidiMode := FHeader.Columns[HitInfo.HitColumn].BidiMode; - CurrentAlignment := FHeader.Columns[HitInfo.HitColumn].Alignment; - end; +begin + if [tsEditing, tsEditPending] * FStates <> [] then + Result := DoEndEdit + else + Result := True; +end; - if CurrentBidiMode = bdLeftToRight then - DetermineHitPositionLTR(HitInfo, X, ColRight, CurrentAlignment) - else - DetermineHitPositionRTL(HitInfo, X, ColRight, CurrentAlignment); - end; - end; +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.EndSynch; + +begin + if FSynchUpdateCount > 0 then + Dec(FSynchUpdateCount); + + if not (csDestroying in ComponentState) then + begin + if FSynchUpdateCount = 0 then + begin + DoStateChange([], [tsSynchMode]); + DoUpdating(usEndSynch); + end + else + DoUpdating(usSynch); end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetLast(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = False): PVirtualNode; - -// Returns the very last node in the tree branch given by Node and initializes the nodes all the way down including the -// result. toChildrenAbove is optionally considered. By using Node = nil the very last node in the tree is returned. +procedure TBaseVirtualTree.EndUpdate; var - Next: PVirtualNode; + NewSize: Integer; begin - Result := GetLastChild(Node); - if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then - while Assigned(Result) do + if FUpdateCount > 0 then + Dec(FUpdateCount); + + if not (csDestroying in ComponentState) then + begin + if (FUpdateCount = 0) and (tsUpdating in FStates) then begin - // Test if there is a next last child. If not keep the node from the last run. - // Otherwise use the next last child. - Next := GetLastChild(Result); - if Next = nil then - Break; - Result := Next; - end; -end; + if tsUpdateHiddenChildrenNeeded in FStates then + begin + DetermineHiddenChildrenFlagAllNodes; + Exclude(FStates, tsUpdateHiddenChildrenNeeded); + end; + + DoStateChange([], [tsUpdating]); + + NewSize := PackArray(FSelection, FSelectionCount); + if NewSize > -1 then + begin + FSelectionCount := NewSize; + SetLength(FSelection, FSelectionCount); + end; -//---------------------------------------------------------------------------------------------------------------------- + InvalidateCache; + ValidateCache; + if HandleAllocated then + UpdateScrollBars(False); -function TBaseVirtualTree.GetLastInitialized(Node: PVirtualNode = nil; - ConsiderChildrenAbove: Boolean = False): PVirtualNode; + if tsStructureChangePending in FStates then + DoStructureChange(FLastStructureChangeNode, FLastStructureChangeReason); + try + if tsChangePending in FStates then + DoChange(FLastChangedNode); + finally + if toAutoSort in FOptions.AutoOptions then + SortTree(FHeader.SortColumn, FHeader.SortDirection, True); -// Returns the very last initialized child node in the tree branch given by Node. + SetUpdateState(False); + if HandleAllocated then + Invalidate; + UpdateDesigner; + end; + end; -begin - Result := GetLastNoInit(Node, ConsiderChildrenAbove); - if Assigned(Result) and not (vsInitialized in Result.States) then - Result := GetPreviousInitialized(Result, ConsiderChildrenAbove); + if FUpdateCount = 0 then begin + DoUpdating(usEnd); + EnsureNodeSelected(); + end + else + DoUpdating(usUpdate); + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetLastNoInit(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = False): PVirtualNode; - -// Returns the very last node in the tree branch given by Node without initialization. +function TBaseVirtualTree.ExecuteAction(Action: TBasicAction): Boolean; -var - Next: PVirtualNode; +// Some support for standard actions. begin - Result := GetLastChildNoInit(Node); - if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then - while Assigned(Result) do + Result := inherited ExecuteAction(Action); + + if not Result then + begin + Result := Action is TEditSelectAll; + if Result then + SelectAll(False) + else begin - // Test if there is a next last child. If not keep the node from the last run. - // Otherwise use the next last child. - Next := GetLastChildNoInit(Result); - if Next = nil then - Break; - Result := Next; + Result := Action is TEditCopy; + if Result then + CopyToClipboard + else + if not (toReadOnly in FOptions.MiscOptions) then + begin + Result := Action is TEditCut; + if Result then + CutToClipboard + else + begin + Result := Action is TEditPaste; + if Result then + PasteFromClipboard + else + begin + Result := Action is TEditDelete; + if Result then + DeleteSelectedNodes; + end; + end; + end; end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetLastChild(Node: PVirtualNode): PVirtualNode; +procedure TBaseVirtualTree.FinishCutOrCopy; -// Determines the last child of the given node and initializes it if there is one. +// Deletes nodes which are marked as being cutted. + +var + Run: PVirtualNode; begin - if (Node = nil) or (Node = FRoot) then - Result := FRoot.LastChild - else + if tsCutPending in FStates then begin - if not (vsInitialized in Node.States) then - InitNode(Node); - if vsHasChildren in Node.States then + Run := FRoot.FirstChild; + while Assigned(Run) do begin - if Node.ChildCount = 0 then - InitChildren(Node); - Result := Node.LastChild; - end - else - Result := nil; + if vsCutOrCopy in Run.States then + DeleteNode(Run); + Run := GetNextNoInit(Run); + end; + DoStateChange([], [tsCutPending]); end; - - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetLastChildNoInit(Node: PVirtualNode): PVirtualNode; +procedure TBaseVirtualTree.FlushClipboard; -// Determines the last child of the given node but does not initialize it. +// Used to render the data which is currently on the clipboard (finishes delayed rendering). begin - if (Node = nil) or (Node = FRoot) then - Result := FRoot.LastChild - else + if ClipboardStates * FStates <> [] then begin - if vsHasChildren in Node.States then - Result := Node.LastChild - else - Result := nil; + DoStateChange([tsClipboardFlushing]); + OleFlushClipboard; + CancelCutOrCopy; + DoStateChange([], [tsClipboardFlushing]); end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetLastVisible(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True; - IncludeFiltered: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.FullCollapse(Node: PVirtualNode = nil); -// Returns the very last visible node in the tree while optionally considering toChildrenAbove. -// The nodes are intialized all the way up including the result node. +// This routine collapses all expanded nodes in the subtree given by Node or the whole tree if Node is FRoot or nil. +// Only nodes which are expanded will be collapsed. This excludes uninitialized nodes but nodes marked as visible +// will still be collapsed if they are expanded. var - Run: PVirtualNode; + Stop: PVirtualNode; begin - Result := GetLastVisibleNoInit(Node, ConsiderChildrenAbove); - - Run := Result; - while Assigned(Run) and (Run <> Node) and (Run <> RootNode) do + if FRoot.TotalCount > 1 then begin - if not (vsInitialized in Run.States) then - InitNode(Run); - Run := Run.Parent; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetLastVisibleChild(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; - -// Determines the last visible child of the given node and initializes it if necessary. + if Node = FRoot then + Node := nil; -begin - if (Node = nil) or (Node = FRoot) then - Result := GetLastChild(FRoot) - else - if FullyVisible[Node] and (vsExpanded in Node.States) then - Result := GetLastChild(Node) - else - Result := nil; + DoStateChange([tsCollapsing]); + BeginUpdate; + try + Stop := Node; + Node := GetLastVisibleNoInit(Node, True); - if Assigned(Result) and (not (vsVisible in Result.States) or - (not IncludeFiltered and IsEffectivelyFiltered[Result])) then - Result := GetPreviousVisibleSibling(Result, IncludeFiltered); + if Assigned(Node) then + begin + repeat + if [vsHasChildren, vsExpanded] * Node.States = [vsHasChildren, vsExpanded] then + ToggleNode(Node); + Node := GetPreviousNoInit(Node, True); + until (Node = Stop) or not Assigned(Node); - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); + // Collapse the start node too. + if Assigned(Stop) and ([vsHasChildren, vsExpanded] * Stop.States = [vsHasChildren, vsExpanded]) then + ToggleNode(Stop); + end; + finally + EndUpdate; + DoStateChange([], [tsCollapsing]); + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetLastVisibleChildNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.FullExpand(Node: PVirtualNode = nil); -// Determines the last visible child of the given node without initialization. +// This routine expands all collapsed nodes in the subtree given by Node or the whole tree if Node is FRoot or nil. +// All nodes on the way down are initialized so this procedure might take a long time. +// Since all nodes are validated, the tree cannot make use of optimatizations. Hence it is counter productive and you +// should consider avoiding its use. + +var + Stop: PVirtualNode; begin - if (Node = nil) or (Node = FRoot) then - Result := GetLastChildNoInit(FRoot) - else - if FullyVisible[Node] and (vsExpanded in Node.States) then - Result := GetLastChildNoInit(Node) - else - Result := nil; + if FRoot.TotalCount > 1 then + begin + DoStateChange([tsExpanding]); + StartOperation(TVTOperationKind.okExpand); + BeginUpdate; + try + if Node = nil then + begin + Node := FRoot.FirstChild; + Stop := nil; + end + else + begin + Stop := Node.NextSibling; + if Stop = nil then + begin + Stop := Node; + repeat + Stop := Stop.Parent; + until (Stop = FRoot) or Assigned(Stop.NextSibling); + if Stop = FRoot then + Stop := nil + else + Stop := Stop.NextSibling; + end; + end; - if Assigned(Result) and (not (vsVisible in Result.States) or - (not IncludeFiltered and IsEffectivelyFiltered[Result])) then - Result := GetPreviousVisibleSiblingNoInit(Result, IncludeFiltered); -end; + // Initialize the start node. Others will be initialized in GetNext. + if not (vsInitialized in Node.States) then + InitNode(Node); -//---------------------------------------------------------------------------------------------------------------------- + repeat + if not (vsExpanded in Node.States) then + ToggleNode(Node); + Node := GetNext(Node); + until (Node = Stop) or OperationCanceled; + finally + EndOperation(TVTOperationKind.okExpand); + EndUpdate; + DoStateChange([], [tsExpanding]); + end; + end; +end; -function TBaseVirtualTree.GetLastVisibleNoInit(Node: PVirtualNode = nil; - ConsiderChildrenAbove: Boolean = True; IncludeFiltered: Boolean = False): PVirtualNode; +//---------------------------------------------------------------------------------------------------------------------- -// Returns the very last visible node in the tree while optionally considering toChildrenAbove. -// No initialization is performed. -var - Next: PVirtualNode; +function TBaseVirtualTree.GetControlsAlignment: TAlignment; begin - Result := GetLastVisibleChildNoInit(Node, IncludeFiltered); - if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then - while Assigned(Result) and (vsExpanded in Result.States) do - begin - // Test if there is a next last child. If not keep the node from the last run. - // Otherwise use the next last child. - Next := GetLastChildNoInit(Result); - if Assigned(Next) and (not (vsVisible in Next.States) or - (not IncludeFiltered and IsEffectivelyFiltered[Next])) then - Next := GetPreviousVisibleSiblingNoInit(Next, IncludeFiltered); - if Next = nil then - Break; - Result := Next; - end; + Result := FAlignment; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetMaxColumnWidth(Column: TColumnIndex; UseSmartColumnWidth: Boolean = False): Integer; +function TBaseVirtualTree.GetDisplayRect(Node: PVirtualNode; Column: TColumnIndex; TextOnly: Boolean; + Unclipped: Boolean = False; ApplyCellContentMargin: Boolean = False): TRect; -// This method determines the width of the largest node in the given column. -// If UseSmartColumnWidth is True then only the visible nodes which are in view will be considered -// Note: If UseSmartColumnWidth is False then every visible node in the tree will be initialized contradicting so -// the virtual paradigm. +// Determines the client coordinates the given node covers, depending on scrolling, expand state etc. +// If the given node cannot be found (because one of its parents is collapsed or it is invisible) then an empty +// rectangle is returned. +// If TextOnly is True then only the text bounds are returned, that is, the resulting rectangle's left and right border +// are updated according to bidi mode, alignment and text width of the node. +// If Unclipped is True (which only makes sense if also TextOnly is True) then the calculated text rectangle is +// not clipped if the text does not entirely fit into the text space. This is special handling needed for hints. +// If ApplyCellContentMargin is True (which only makes sense if also TextOnly is True) then the calculated text +// rectangle respects the cell content margin. +// If Column is -1 then the entire client width is used before determining the node's width otherwise the bounds of the +// particular column are used. +// Note: Column must be a valid column and is used independent of whether the header is visible or not. var - Run, - LastNode, - NextNode: PVirtualNode; - TextLeft, - CurrentWidth: Integer; + Temp: PVirtualNode; + LeftOffset: Cardinal; + TopOffset: Cardinal; + CacheIsAvailable: Boolean; + TextWidth: Integer; + CurrentBidiMode: TBidiMode; + CurrentAlignment: TAlignment; + MaxUnclippedHeight: Integer; + TM: TTextMetric; + ExtraVerticalMargin: Integer; lOffsets: TVTOffsets; begin - if OperationCanceled then - begin - // Behave non-destructive. - Result := FHeader.Columns[Column].Width; - Exit; - end - else - Result := 0; - - StartOperation(okGetMaxColumnWidth); - try - if Assigned(FOnBeforeGetMaxColumnWidth) then - FOnBeforeGetMaxColumnWidth(FHeader, Column, UseSmartColumnWidth); + Assert(Assigned(Node), 'Node must not be nil.'); + Assert(Node <> FRoot, 'Node must not be the hidden root node.'); - if UseSmartColumnWidth then // Get first visible node which is in view. - Run := GetTopNode - else - Run := GetFirstVisible(nil, True); + if not (vsInitialized in Node.States) then + InitNode(Node); - // Decide where to stop. - if UseSmartColumnWidth then - LastNode := GetNextVisible(BottomNode) - else - LastNode := nil; + Result := Rect(0, 0, 0, 0); - if hoAutoResizeInclCaption in FHeader.Options then - Result := Result + (2 * Header.Columns[Column].Margin + Header.Columns[Column].CaptionWidth + 2); + // Check whether the node is visible (determine indentation level btw.). + if not IsEffectivelyVisible[Node] then + Exit; - while Assigned(Run) and not OperationCanceled do + // Here we know the node is visible. + TopOffset := 0; + CacheIsAvailable := False; + if tsUseCache in FStates then + begin + // If we can use the position cache then do a binary search to find a cached node which is as close as possible + // to the current node. Iterate then through all following and visible nodes and sum up their heights. + Temp := FindInPositionCache(Node, TopOffset); + CacheIsAvailable := Assigned(Temp); + while Assigned(Temp) and (Temp <> Node) do begin - GetOffsets(Run, lOffsets, TVTElement.ofsLabel, Column); - TextLeft := lOffsets[TVTElement.ofsLabel]; - CurrentWidth := DoGetNodeWidth(Run, Column); - Inc(CurrentWidth, DoGetNodeExtraWidth(Run, Column)); - Inc(CurrentWidth, DoGetCellContentMargin(Run, Column).X); - - // Background for fix: - // DoGetNodeWidth works correctly to return just the - // headerwidth in vsMultiline state of the node. But the - // following code was adding TextLeft unnecessarily. This - // caused a width increase each time a column splitter - // was double-clicked for the option hoDblClickResize that - // really does not apply for vsMultiline case. - // Fix: If the node is multiline, leave the current width as - // it is as returned by DoGetNodeWidth logic above. - if (Column > NoColumn) and (vsMultiline in Run.States) then - Result := CurrentWidth - else - if Result < (TextLeft + CurrentWidth) then - Result := TextLeft + CurrentWidth; - - // Get next visible node and update left node position if needed. - NextNode := GetNextVisible(Run, True); - if NextNode = LastNode then - Break; - Run := NextNode; + Inc(TopOffset, NodeHeight[Temp]); + Temp := GetNextVisibleNoInit(Temp, True); end; - if toShowVertGridLines in FOptions.PaintOptions then - Inc(Result); - - if Assigned(FOnAfterGetMaxColumnWidth) then - FOnAfterGetMaxColumnWidth(FHeader, Column, Result); - - finally - EndOperation(okGetMaxColumnWidth); end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetNext(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; + if not CacheIsAvailable then + begin + // If the cache is not available then go straight through all nodes up to the root and sum up their heights. + Temp := Node; + repeat + Temp := GetPreviousVisibleNoInit(Temp, True); + if Temp = nil then + Break; + Inc(TopOffset, NodeHeight[Temp]); + until False; + end; -// Returns next node in tree while optionally considering toChildrenAbove. The Result will be initialized if needed. + Result := Rect(0, TopOffset, Max(FRangeX, ClientWidth), TopOffset + NodeHeight[Node]); -begin - Result := Node; - if Assigned(Result) then + // Limit left and right bounds to the given column (if any) and move bounds according to current scroll state. + if Column > NoColumn then begin - Assert(Result <> FRoot, 'Node must not be the hidden root node.'); + FHeader.Columns.GetColumnBounds(Column, Result.Left, Result.Right); + // The right column border is not part of this cell. + Dec(Result.Right); + OffsetRect(Result, 0, FOffsetY); + end + else + OffsetRect(Result, -FEffectiveOffsetX, FOffsetY); - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then + // Limit left and right bounds further if only the text area is required. + if TextOnly then + begin + // If the text of a node is involved then we have to consider directionality and alignment too. + if Column <= NoColumn then begin - // If this node has no siblings use the parent. - if not Assigned(Result.NextSibling) then - begin - Result := Result.Parent; - if Result = FRoot then - begin - Result := nil; - end; - end - else - begin - // There is at least one sibling so take it. - Result := Result.NextSibling; - - // Has this node got children? Initialize them if necessary. - if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then - InitChildren(Result); + CurrentBidiMode := BidiMode; + CurrentAlignment := Alignment; + end + else + begin + CurrentBidiMode := FHeader.Columns[Column].BidiMode; + CurrentAlignment := FHeader.Columns[Column].Alignment; + end; - // Now take a look at the children. - while Assigned(Result.FirstChild) do - begin - Result := Result.FirstChild; - if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then - InitChildren(Result); - end; - end; + GetOffsets(Node, lOffsets, TVTElement.ofsLabel, Column); + LeftOffset := lOffSets[TVTElement.ofsLabel]; + // Offset contains now the distance from the left or right border of the rectangle (depending on bidi mode). + // Now consider the alignment too and calculate the final result. + if CurrentBidiMode = bdLeftToRight then + begin + Inc(Result.Left, LeftOffset); + // Left-to-right reading does not need any special adjustment of the alignment. end else begin - // Has this node got children? - if vsHasChildren in Result.States then - begin - // Yes, there are child nodes. Initialize them if necessary. - if Result.ChildCount = 0 then - InitChildren(Result); - end; + Dec(Result.Right, LeftOffset); - // if there is no child node try siblings - if Assigned(Result.FirstChild) then - Result := Result.FirstChild - else - begin - repeat - // Is there a next sibling? - if Assigned(Result.NextSibling) then - begin - Result := Result.NextSibling; - Break; - end - else + // Consider bidi mode here. In RTL context does left alignment actually mean right alignment and vice versa. + ChangeBiDiModeAlignment(CurrentAlignment); + end; + + TextWidth := DoGetNodeWidth(Node, Column); + + // Keep cell height before applying cell content margin in order to increase cell height if text does not fit + // and Unclipped it true (see below). + MaxUnclippedHeight := Result.Bottom - Result.Top; + + if ApplyCellContentMargin then + DoBeforeCellPaint(Self.Canvas, Node, Column, cpmGetContentMargin, Result, Result); + + if Unclipped then + begin + // The caller requested the text coordinates unclipped. This means they must be calculated so as would + // there be enough space, regardless of column bounds etc. + // The layout still depends on the available space too, because this determines the position + // of the unclipped text rectangle. + if Result.Right - Result.Left < TextWidth - 1 then + if CurrentBidiMode = bdLeftToRight then + CurrentAlignment := taLeftJustify + else + CurrentAlignment := taRightJustify; + + // Increase cell height (up to MaxUnclippedHeight determined above) if text does not fit. + GetTextMetrics(Self.Canvas.Handle, TM); + ExtraVerticalMargin := System.Math.Min(TM.tmHeight, MaxUnclippedHeight) - (Result.Bottom - Result.Top); + if ExtraVerticalMargin > 0 then + InflateRect(Result, 0, (ExtraVerticalMargin + 1) div 2); + + case CurrentAlignment of + taCenter: begin - // No sibling anymore, so use the parent's next sibling. - if Result.Parent <> FRoot then - Result := Result.Parent - else - begin - // There are no further nodes to examine, hence there is no further visible node. - Result := nil; - Break; - end; + Result.Left := (Result.Left + Result.Right - TextWidth) div 2; + Result.Right := Result.Left + TextWidth; end; - until False; + taRightJustify: + Result.Left := Result.Right - TextWidth; + else // taLeftJustify + Result.Right := Result.Left + TextWidth - 1; end; - end; + end + else + // Modify rectangle only if the text fits entirely into the given room. + if Result.Right - Result.Left > TextWidth then + case CurrentAlignment of + taCenter: + begin + Result.Left := (Result.Left + Result.Right - TextWidth) div 2; + Result.Right := Result.Left + TextWidth; + end; + taRightJustify: + Result.Left := Result.Right - TextWidth; + else // taLeftJustify + Result.Right := Result.Left + TextWidth; + end; end; - - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextChecked(Node: PVirtualNode; State: TCheckState = csCheckedNormal; - ConsiderChildrenAbove: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetEffectivelyFiltered(Node: PVirtualNode): Boolean; + +// Checks if a node is effectively filtered out. This depends on the nodes state and the paint options. begin - if (Node = nil) or (Node = FRoot) then - Result := GetFirstNoInit(ConsiderChildrenAbove) + if Assigned(Node) then + Result := (vsFiltered in Node.States) and not (toShowFilteredNodes in FOptions.PaintOptions) else - Result := GetNextNoInit(Node, ConsiderChildrenAbove); - - while Assigned(Result) and (GetCheckState(Result) <> State) do - Result := GetNextNoInit(Result, ConsiderChildrenAbove); - - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); + Result := False; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextChecked(Node: PVirtualNode; ConsiderChildrenAbove: Boolean): PVirtualNode; +function TBaseVirtualTree.GetEffectivelyVisible(Node: PVirtualNode): Boolean; + begin - Result := Self.GetNextChecked(Node, csCheckedNormal, ConsiderChildrenAbove); + Result := (vsVisible in Node.States) and not IsEffectivelyFiltered[Node]; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextCutCopy(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetFirst(ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Returns the next node in the tree which is currently marked for a clipboard operation. Since only visible nodes can -// be marked (or they are hidden after they have been marked) it is not necessary to initialize nodes to check for -// child nodes. The result, however, is initialized if necessary. +// Returns the first node in the tree while optionally considering toChildrenAbove. begin - if ClipboardStates * FStates <> [] then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin - if (Node = nil) or (Node = FRoot) then - Result := GetFirstNoInit(ConsiderChildrenAbove) + if vsHasChildren in FRoot.States then + begin + Result := FRoot; + + // Child nodes are the first choice if possible. + if Assigned(Result.FirstChild) then + begin + while Assigned(Result.FirstChild) do + begin + Result := Result.FirstChild; + if not (vsInitialized in Result.States) then + InitNode(Result); + + if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then + InitChildren(Result); + end; + end + else + Result := nil; + end else - Result := GetNextNoInit(Node, ConsiderChildrenAbove); - while Assigned(Result) and not (vsCutOrCopy in Result.States) do - Result := GetNextNoInit(Result, ConsiderChildrenAbove); - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); + Result := nil; end else - Result := nil; + Result := FRoot.FirstChild; + + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextInitialized(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetFirstChecked(State: TCheckState = csCheckedNormal; + ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Returns the next node in tree which is initialized. +// Returns the first node in the tree with the given check state. begin - Result := Node; - repeat - Result := GetNextNoInit(Result, ConsiderChildrenAbove); - until (Result = nil) or (vsInitialized in Result.States); + Result := GetNextChecked(nil, State, ConsiderChildrenAbove); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextLeaf(Node: PVirtualNode): PVirtualNode; +function TBaseVirtualTree.GetFirstChild(Node: PVirtualNode): PVirtualNode; -// Returns the next node in the tree which has currently no children. -// The result is initialized if necessary. +// Returns the first child of the given node. The result node is initialized before exit. begin if (Node = nil) or (Node = FRoot) then Result := FRoot.FirstChild else - Result := GetNext(Node); - while Assigned(Result) and (vsHasChildren in Result.States) do - Result := GetNext(Result); + begin + if not (vsInitialized in Node.States) then + InitNode(Node); + if vsHasChildren in Node.States then + begin + if Node.ChildCount = 0 then + InitChildren(Node); + Result := Node.FirstChild; + end + else + Result := nil; + end; + if Assigned(Result) and not (vsInitialized in Result.States) then InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextLevel(Node: PVirtualNode; NodeLevel: Cardinal): PVirtualNode; - -// Returns the next node in the tree on a specific level. -// The result is initialized if necessary. - -var - StartNodeLevel: Cardinal; +function TBaseVirtualTree.GetFirstChildNoInit(Node: PVirtualNode): PVirtualNode; +// Determines the first child of the given node but does not initialize it. begin - Result := nil; - - if Assigned(Node) and (Node <> FRoot) then + if (Node = nil) or (Node = FRoot) then + Result := FRoot.FirstChild + else begin - StartNodeLevel := GetNodeLevel(Node); - - if StartNodeLevel < NodeLevel then - begin - Result := GetNext(Node); - if Assigned(Result) and (GetNodeLevel(Result) <> NodeLevel) then - Result := GetNextLevel(Result, NodeLevel); - end + if vsHasChildren in Node.States then + Result := Node.FirstChild else - if StartNodeLevel = NodeLevel then - begin - Result := Node.NextSibling; - if not Assigned(Result) then // i.e. start node was a last sibling - begin - Result := Node.Parent; - if Assigned(Result) then - begin - // go to next anchestor of the start node which has a next sibling (if exists) - while Assigned(Result) and not Assigned(Result.NextSibling) do - Result := Result.Parent; - if Assigned(Result) then - Result := GetNextLevel(Result.NextSibling, NodeLevel); - end; - end; - end - else - // i.e. StartNodeLevel > NodeLevel - Result := GetNextLevel(Node.Parent, NodeLevel); + Result := nil; end; - - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean): PVirtualNode; +function TBaseVirtualTree.GetFirstCutCopy(ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Optimized version of GetNext performing no initialization, but optionally considering toChildrenAbove. +// Returns the first node in the tree which is currently marked for a clipboard operation. +// See also GetNextCutCopy for comments on initialization. begin - Result := Node; - if Assigned(Result) then - begin - Assert(Result <> FRoot, 'Node must not be the hidden root node.'); + Result := GetNextCutCopy(nil, ConsiderChildrenAbove); +end; - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then - begin - // If this node has no siblings use the parent. - if not Assigned(Result.NextSibling) then - begin - Result := Result.Parent; - if Result = FRoot then - begin - Result := nil; - end; - end - else - begin - // There is at least one sibling so take it. - Result := Result.NextSibling; +//---------------------------------------------------------------------------------------------------------------------- - // Now take a look at the children. - while Assigned(Result.FirstChild) do - begin - Result := Result.FirstChild; - end; - end; - end - else - begin - // If there is no child node try siblings. - if Assigned(Result.FirstChild) then - Result := Result.FirstChild - else - begin - repeat - // Is there a next sibling? - if Assigned(Result.NextSibling) then - begin - Result := Result.NextSibling; - Break; - end - else - begin - // No sibling anymore, so use the parent's next sibling. - if Result.Parent <> FRoot then - Result := Result.Parent - else - begin - // There are no further nodes to examine, hence there is no further visible node. - Result := nil; - Break; - end; - end; - until False; - end; - end; - end; +function TBaseVirtualTree.GetFirstInitialized(ConsiderChildrenAbove: Boolean = False): PVirtualNode; + +// Returns the first node which is already initialized. + +begin + Result := GetFirstNoInit(ConsiderChildrenAbove); + if Assigned(Result) and not (vsInitialized in Result.States) then + Result := GetNextInitialized(Result, ConsiderChildrenAbove); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextSelected(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetFirstLeaf: PVirtualNode; -// Returns the next node in the tree which is currently selected. Since children of unitialized nodes cannot be -// in the current selection (because they simply do not exist yet) it is not necessary to initialize nodes here. -// The result however is initialized if necessary. +// Returns the first node in the tree which has currently no children. +// The result is initialized if necessary. begin - if FSelectionCount > 0 then - begin - if (Node = nil) or (Node = FRoot) then - Result := GetFirstNoInit(ConsiderChildrenAbove) - else - Result := GetNextNoInit(Node, ConsiderChildrenAbove); - while Assigned(Result) and not (vsSelected in Result.States) do - Result := GetNextNoInit(Result, ConsiderChildrenAbove); - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); - end - else - Result := nil; + Result := GetNextLeaf(nil); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextSibling(Node: PVirtualNode): PVirtualNode; +function TBaseVirtualTree.GetFirstLevel(NodeLevel: Cardinal): PVirtualNode; -// Returns the next sibling of Node and initializes it if necessary. +// Returns the first node in the tree on a specific level. +// The result is initialized if necessary. begin - Result := Node; - if Assigned(Result) then - begin - Assert(Result <> FRoot, 'Node must not be the hidden root node.'); + Result := GetFirstNoInit(True); + while Assigned(Result) and (GetNodeLevel(Result) <> NodeLevel) do + Result := GetNextNoInit(Result, True); - Result := Result.NextSibling; - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); - end; + if Assigned(Result) and (GetNodeLevel(Result) <> NodeLevel) then // i.e. there is no node with the desired level in the tree + Result := nil; + + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); end; -function TBaseVirtualTree.GetNextSiblingNoInit(Node: PVirtualNode): PVirtualNode; +//---------------------------------------------------------------------------------------------------------------------- -// Returns the next sibling of Node. +function TBaseVirtualTree.GetFirstNoInit(ConsiderChildrenAbove: Boolean = False): PVirtualNode; + +// Returns the first node in the tree while optionally considering toChildrenAbove. +// No initialization is performed. begin - Result := Node; - if Assigned(Result) then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin - Assert(Result <> FRoot, 'Node must not be the hidden root node.'); + if vsHasChildren in FRoot.States then + begin + Result := FRoot; - Result := Result.NextSibling; - end; + // Child nodes are the first choice if possible. + if Assigned(Result.FirstChild) then + begin + while Assigned(Result.FirstChild) do + Result := Result.FirstChild; + end + else + Result := nil; + end + else + Result := nil; + end + else + Result := FRoot.FirstChild; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextVisible(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode; +function TBaseVirtualTree.GetFirstSelected(ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Returns next node in tree, with regard to Node, which is visible. -// Nodes which need an initialization (including the result) are initialized. -// toChildrenAbove is optionally considered which is the default here. +// Returns the first node in the current selection while optionally considering toChildrenAbove. -var - ForceSearch: Boolean; +begin + Result := GetNextSelected(nil, ConsiderChildrenAbove); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetFirstVisible(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True; + IncludeFiltered: Boolean = False): PVirtualNode; + +// Returns the first visible node in the tree while optionally considering toChildrenAbove. +// If necessary nodes are initialized on demand. begin Result := Node; - if Assigned(Result) then + if not Assigned(Result) then + Result := FRoot; + + if vsHasChildren in Result.States then begin - Assert(Result <> FRoot, 'Node must not be the hidden root node.'); + if Result.ChildCount = 0 then + InitChildren(Result); - repeat - // If the given node is not visible then look for a parent node which is visible, otherwise we will - // likely go unnecessarily through a whole bunch of invisible nodes. - if not FullyVisible[Result] then - Result := GetVisibleParent(Result, True); + // Child nodes are the first choice if possible. + if Assigned(Result.FirstChild) then + begin + Result := GetFirstChild(Result); if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin repeat - // If there a no siblings anymore, go up one level. - if not Assigned(Result.NextSibling) then + // Search the first visible sibling. + while Assigned(Result.NextSibling) and not (vsVisible in Result.States) do + begin + Result := Result.NextSibling; + // Init node on demand as this might change the visibility. + if not (vsInitialized in Result.States) then + InitNode(Result); + end; + + // If there are no visible siblings take the parent. + if not (vsVisible in Result.States) then begin Result := Result.Parent; if Result = FRoot then - begin Result := nil; - Break; - end; - - if not (vsInitialized in Result.States) then - InitNode(Result); - if vsVisible in Result.States then - Break; + Break; end else begin - // There is at least one sibling so take it. - Result := Result.NextSibling; - if not (vsInitialized in Result.States) then - InitNode(Result); - if not (vsVisible in Result.States) then - Continue; - - // Now take a look at the children. - // As the children are initialized while toggling, we don't need to do this here. - while (vsExpanded in Result.States) and Assigned(Result.FirstChild) do - begin - Result := Result.FirstChild; - if not (vsInitialized in Result.States) then - InitNode(Result); - if not (vsVisible in Result.States) then - Break; - end; - - // If we found a visible node we don't need to search any longer. - if vsVisible in Result.States then + if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then + InitChildren(Result); + if (not Assigned(Result.FirstChild)) or (not (vsExpanded in Result.States)) then Break; end; + + Result := Result.FirstChild; + if not (vsInitialized in Result.States) then + InitNode(Result); until False; end else begin - // Has this node got children? - if [vsHasChildren, vsExpanded] * Result.States = [vsHasChildren, vsExpanded] then - begin - // Yes, there are child nodes. Initialize them if necessary. - if Result.ChildCount = 0 then - InitChildren(Result); - end; - - // Child nodes are the first choice if possible. - if (vsExpanded in Result.States) and Assigned(Result.FirstChild) then - begin - Result := GetFirstChild(Result); - ForceSearch := False; - end - else - ForceSearch := True; - // If there are no children or the first child is not visible then search the sibling nodes or traverse parents. - if Assigned(Result) and (ForceSearch or not (vsVisible in Result.States)) then + if not (vsVisible in Result.States) then begin repeat // Is there a next sibling? if Assigned(Result.NextSibling) then begin Result := Result.NextSibling; + // The visible state can be removed during initialization so init the node first. if not (vsInitialized in Result.States) then InitNode(Result); if vsVisible in Result.States then @@ -28600,81 +19322,94 @@ function TBaseVirtualTree.GetNextVisible(Node: PVirtualNode; ConsiderChildrenAbo until False; end; end; - until not Assigned(Result) or IsEffectivelyVisible[Result]; - end; + end + else + Result := nil; + end + else + Result := nil; + + if Assigned(Result) and not IncludeFiltered and IsEffectivelyFiltered[Result] then + Result := GetNextVisible(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextVisibleNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode; +function TBaseVirtualTree.GetFirstVisibleChild(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; -// Returns the next node in tree, with regard to Node, which is visible. -// toChildrenAbove is optionally considered (which is the default). No initialization is done. +// Returns the first visible child node of Node. If necessary nodes are initialized on demand. -var - ForceSearch: Boolean; +begin + if Node = nil then + Node := FRoot; + Result := GetFirstChild(Node); + + if Assigned(Result) and (not (vsVisible in Result.States) or + (not IncludeFiltered and IsEffectivelyFiltered[Result])) then + Result := GetNextVisibleSibling(Result, IncludeFiltered); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetFirstVisibleChildNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; + +// Returns the first visible child node of Node. + +begin + if Node = nil then + Node := FRoot; + Result := Node.FirstChild; + if Assigned(Result) and (not (vsVisible in Result.States) or + (not IncludeFiltered and IsEffectivelyFiltered[Result])) then + Result := GetNextVisibleSiblingNoInit(Result, IncludeFiltered); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetFirstVisibleNoInit(Node: PVirtualNode = nil; + ConsiderChildrenAbove: Boolean = True; IncludeFiltered: Boolean = False): PVirtualNode; + +// Returns the first visible node in the tree or given subtree while optionally considering toChildrenAbove. +// No initialization is performed. begin Result := Node; - if Assigned(Result) then + if not Assigned(Result) then + Result := FRoot; + + if vsHasChildren in Result.States then begin - Assert(Result <> FRoot, 'Node must not be the hidden root node.'); + // Child nodes are the first choice if possible. + if Assigned(Result.FirstChild) then + begin + Result := Result.FirstChild; - repeat if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin repeat - // If there a no siblings anymore, go up one level. - if not Assigned(Result.NextSibling) then + // Search the first visible sibling. + while Assigned(Result.NextSibling) and not (vsVisible in Result.States) do + Result := Result.NextSibling; + + // If there a no visible siblings take the parent. + if not (vsVisible in Result.States) then begin Result := Result.Parent; - if Result = FRoot then - begin - Result := nil; - Break; - end; - if vsVisible in Result.States then - Break; + if Result = FRoot then + Result := nil; + Break; end else - begin - // There is at least one sibling so take it. - Result := Result.NextSibling; - if not (vsVisible in Result.States) then - Continue; - - // Now take a look at the children. - while (vsExpanded in Result.States) and Assigned(Result.FirstChild) do - begin - Result := Result.FirstChild; - if not (vsVisible in Result.States) then - Break; - end; - - // If we found a visible node we don't need to search any longer. - if vsVisible in Result.States then + if (not Assigned(Result.FirstChild)) or (not (vsExpanded in Result.States))then Break; - end; + + Result := Result.FirstChild; until False; end else begin - // If the given node is not visible then look for a parent node which is visible, otherwise we will - // likely go unnecessarily through a whole bunch of invisible nodes. - if not FullyVisible[Result] then - Result := GetVisibleParent(Result, True); - - // Child nodes are the first choice if possible. - if (vsExpanded in Result.States) and Assigned(Result.FirstChild) then - begin - Result := Result.FirstChild; - ForceSearch := False; - end - else - ForceSearch := True; - // If there are no children or the first child is not visible then search the sibling nodes or traverse parents. - if ForceSearch or not (vsVisible in Result.States) then + if not (vsVisible in Result.States) then begin repeat // Is there a next sibling? @@ -28699,228 +19434,436 @@ function TBaseVirtualTree.GetNextVisibleNoInit(Node: PVirtualNode; ConsiderChild until False; end; end; - until not Assigned(Result) or IsEffectivelyVisible[Result]; - end; + end + else + Result := nil; + end + else + Result := nil; + + if Assigned(Result) and not IncludeFiltered and IsEffectivelyFiltered[Result] then + Result := GetNextVisibleNoInit(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNextVisibleSibling(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; +procedure TBaseVirtualTree.GetHitTestInfoAt(X, Y: Integer; Relative: Boolean; var HitInfo: THitInfo); -// Returns the next visible sibling after Node. Initialization is done implicitly. +// Determines the node that occupies the specified point or nil if there's none. The parameter Relative determines +// whether to consider X and Y as being client coordinates (if True) or as being absolute tree coordinates. +// HitInfo is filled with flags describing the hit further. + +var + ColLeft, + ColRight: Integer; + NodeTop: Integer; + InitialColumn, + NextColumn: TColumnIndex; + CurrentBidiMode: TBidiMode; + CurrentAlignment: TAlignment; + NodeRect: TRect; begin - Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); + HitInfo.HitNode := nil; + HitInfo.HitPositions := []; + HitInfo.HitColumn := NoColumn; - Result := Node; - repeat - Result := GetNextSibling(Result); - until not Assigned(Result) or ((vsVisible in Result.States) and - (IncludeFiltered or not IsEffectivelyFiltered[Result])); -end; + // Determine if point lies in the tree's client area. + if X < 0 then + Include(HitInfo.HitPositions, hiToLeft) + else + if X > Max(FRangeX, ClientWidth) then + Include(HitInfo.HitPositions, hiToRight); -//---------------------------------------------------------------------------------------------------------------------- + if Y < 0 then + Include(HitInfo.HitPositions, hiAbove) + else + if Y > Max(FRangeY, ClientHeight) then + Include(HitInfo.HitPositions, hiBelow); -function TBaseVirtualTree.GetNextVisibleSiblingNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; + // Convert position into absolute coordinate if necessary. + if Relative then + begin + if X >= Header.Columns.GetVisibleFixedWidth then + Inc(X, FEffectiveOffsetX); + Inc(Y, -FOffsetY); + end; + HitInfo.HitPoint.X := X; + HitInfo.HitPoint.Y := Y; -// Returns the next visible sibling after Node. + // If the point is in the tree area then check the nodes. + if HitInfo.HitPositions = [] then + begin + HitInfo.HitNode := GetNodeAt(X, Y, False, NodeTop); + if HitInfo.HitNode = nil then + Include(HitInfo.HitPositions, hiNowhere) + else + begin + // At this point we need some info about the node, so it must be initialized. + if not (vsInitialized in HitInfo.HitNode.States) then + InitNode(HitInfo.HitNode); -begin - Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); + if FHeader.UseColumns then + begin + HitInfo.HitColumn := TVirtualTreeColumnsCracker(FHeader.Columns).GetColumnAndBounds(Point(X, Y), ColLeft, ColRight, False); + // If auto column spanning is enabled then look for the last non empty column. + if toAutoSpanColumns in FOptions.AutoOptions then + begin + InitialColumn := HitInfo.HitColumn; + // Search to the left of the hit column for empty columns. + while (HitInfo.HitColumn > NoColumn) and ColumnIsEmpty(HitInfo.HitNode, HitInfo.HitColumn) do + begin + NextColumn := FHeader.Columns.GetPreviousVisibleColumn(HitInfo.HitColumn); + if NextColumn = InvalidColumn then + Break; + HitInfo.HitColumn := NextColumn; + Dec(ColLeft, FHeader.Columns[NextColumn].Width); + end; + // Search to the right of the hit column for empty columns. + repeat + InitialColumn := FHeader.Columns.GetNextVisibleColumn(InitialColumn); + if (InitialColumn = InvalidColumn) or not ColumnIsEmpty(HitInfo.HitNode, InitialColumn) then + Break; + Inc(ColRight, FHeader.Columns[InitialColumn].Width); + until False; + end; + // Make the X position and the right border relative to the start of the column. + Dec(X, ColLeft); + Dec(ColRight, ColLeft); + end + else + begin + HitInfo.HitColumn := NoColumn; + ColRight := Max(FRangeX, ClientWidth); + end; + ColLeft := 0; - Result := Node; - repeat - Result := Result.NextSibling; - until not Assigned(Result) or ((vsVisible in Result.States) and - (IncludeFiltered or not IsEffectivelyFiltered[Result])); + if HitInfo.HitColumn = InvalidColumn then + Include(HitInfo.HitPositions, hiNowhere) + else + begin + // From now on X is in "column" coordinates (relative to the left column border). + HitInfo.HitPositions := [hiOnItem]; + + // Avoid getting the display rect if this is not necessary. + if toNodeHeightResize in FOptions.MiscOptions then + begin + NodeRect := GetDisplayRect(HitInfo.HitNode, HitInfo.HitColumn, False); + if Y <= (NodeRect.Top - FOffsetY + 1) then + Include(HitInfo.HitPositions, hiUpperSplitter) + else + if Y >= (NodeRect.Bottom - FOffsetY - 3) then + Include(HitInfo.HitPositions, hiLowerSplitter); + end; + + if HitInfo.HitColumn <= NoColumn then + begin + CurrentBidiMode := BidiMode; + CurrentAlignment := Alignment; + end + else + begin + CurrentBidiMode := FHeader.Columns[HitInfo.HitColumn].BidiMode; + CurrentAlignment := FHeader.Columns[HitInfo.HitColumn].Alignment; + end; + + if CurrentBidiMode = bdLeftToRight then + DetermineHitPositionLTR(HitInfo, X, ColRight, CurrentAlignment) + else + DetermineHitPositionRTL(HitInfo, X, ColRight, CurrentAlignment); + end; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNodeAt(X, Y: Integer): PVirtualNode; +function TBaseVirtualTree.GetLast(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Overloaded variant of GetNodeAt to easy life of application developers which do not need to have the exact -// top position returned and always use client coordinates. +// Returns the very last node in the tree branch given by Node and initializes the nodes all the way down including the +// result. toChildrenAbove is optionally considered. By using Node = nil the very last node in the tree is returned. var - Dummy: Integer; + Next: PVirtualNode; begin - Result := GetNodeAt(X, Y, True, Dummy); + Result := GetLastChild(Node); + if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then + while Assigned(Result) do + begin + // Test if there is a next last child. If not keep the node from the last run. + // Otherwise use the next last child. + Next := GetLastChild(Result); + if Next = nil then + Break; + Result := Next; + end; end; -function TBaseVirtualTree.GetNodeAt(const P: TPoint): PVirtualNode; +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetLastInitialized(Node: PVirtualNode = nil; + ConsiderChildrenAbove: Boolean = False): PVirtualNode; + +// Returns the very last initialized child node in the tree branch given by Node. + begin - Result := GetNodeAt(P.X, P.Y); + Result := GetLastNoInit(Node, ConsiderChildrenAbove); + if Assigned(Result) and not (vsInitialized in Result.States) then + Result := GetPreviousInitialized(Result, ConsiderChildrenAbove); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNodeAt(X, Y: Integer; Relative: Boolean; var NodeTop: Integer): PVirtualNode; +function TBaseVirtualTree.GetLastNoInit(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// This method returns the node that occupies the specified point, or nil if there's none. -// If Releative is True then X and Y are given in client coordinates otherwise they are considered as being -// absolute values into the virtual tree image (regardless of the current offsets in the tree window). -// NodeTop gets the absolute or relative top position of the node returned or is untouched if no node -// could be found. +// Returns the very last node in the tree branch given by Node without initialization. var - AbsolutePos, - CurrentPos: Cardinal; + Next: PVirtualNode; begin - if Y < 0 then - Y := 0; - - AbsolutePos := Y; - if Relative then - Inc(AbsolutePos, -FOffsetY); - - // CurrentPos tracks a running term of the current position to test for. - // It corresponds always to the top position of the currently considered node. - CurrentPos := 0; - - // If the cache is available then use it. - if tsUseCache in FStates then - Result := FindInPositionCache(AbsolutePos, CurrentPos) - else - Result := GetFirstVisibleNoInit(nil, True); + Result := GetLastChildNoInit(Node); + if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then + while Assigned(Result) do + begin + // Test if there is a next last child. If not keep the node from the last run. + // Otherwise use the next last child. + Next := GetLastChildNoInit(Result); + if Next = nil then + Break; + Result := Next; + end; +end; - // Determine node, of which position and height corresponds to the scroll position most closely. - while Assigned(Result) and (Result <> FRoot) do - begin - if AbsolutePos < (CurrentPos + NodeHeight[Result]) then - Break; - Inc(CurrentPos, NodeHeight[Result]); - Result := GetNextVisibleNoInit(Result, True); - end; +//---------------------------------------------------------------------------------------------------------------------- - if Result = FRoot then - Result := nil; +function TBaseVirtualTree.GetLastChild(Node: PVirtualNode): PVirtualNode; - // Since the given vertical position is likely not the same as the top position - // of the found node this top position is returned. - if Assigned(Result) then +// Determines the last child of the given node and initializes it if there is one. + +begin + if (Node = nil) or (Node = FRoot) then + Result := FRoot.LastChild + else begin - NodeTop := CurrentPos; - if Relative then - Inc(NodeTop, FOffsetY); + if not (vsInitialized in Node.States) then + InitNode(Node); + if vsHasChildren in Node.States then + begin + if Node.ChildCount = 0 then + InitChildren(Node); + Result := Node.LastChild; + end + else + Result := nil; end; + + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- +function TBaseVirtualTree.GetLastChildNoInit(Node: PVirtualNode): PVirtualNode; -function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer; - -// Returns the address of the user defined data area in the node. +// Determines the last child of the given node but does not initialize it. begin - Assert((FNodeDataSize > 0) or not Assigned(Node), 'NodeDataSize not initialized.'); - if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then - Result := nil + if (Node = nil) or (Node = FRoot) then + Result := FRoot.LastChild else begin - Result := @Node.Data; - Include(Node.States, vsOnFreeNodeCallRequired); // We now need to call OnFreeNode, see bug #323 + if vsHasChildren in Node.States then + Result := Node.LastChild + else + Result := nil; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNodeData(pNode: PVirtualNode): T; +function TBaseVirtualTree.GetLastVisible(Node: PVirtualNode = nil; ConsiderChildrenAbove: Boolean = True; + IncludeFiltered: Boolean = False): PVirtualNode; -// Returns the associated data converted to the class given in the generic part of the function. +// Returns the very last visible node in the tree while optionally considering toChildrenAbove. +// The nodes are intialized all the way up including the result node. var - P: Pointer; + Run: PVirtualNode; + begin - P := Self.GetNodeData(pNode); - if Assigned(P) then - Exit(T(P^)) - else - Exit(Default(T)); + Result := GetLastVisibleNoInit(Node, ConsiderChildrenAbove); + + Run := Result; + while Assigned(Run) and (Run <> Node) and (Run <> RootNode) do + begin + if not (vsInitialized in Run.States) then + InitNode(Run); + Run := Run.Parent; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetInterfaceFromNodeData(pNode: PVirtualNode): T; +function TBaseVirtualTree.GetLastVisibleChild(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; + +// Determines the last visible child of the given node and initializes it if necessary. + begin - if Assigned(pNode) then - Result := T(Self.GetNodeData(pNode)^) + if (Node = nil) or (Node = FRoot) then + Result := GetLastChild(FRoot) else - Result := nil; + if FullyVisible[Node] and (vsExpanded in Node.States) then + Result := GetLastChild(Node) + else + Result := nil; + + if Assigned(Result) and (not (vsVisible in Result.States) or + (not IncludeFiltered and IsEffectivelyFiltered[Result])) then + Result := GetPreviousVisibleSibling(Result, IncludeFiltered); + + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNodeDataAt(pXCoord, pYCoord: Integer): T; +function TBaseVirtualTree.GetLastVisibleChildNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; -// Returns the associated data at the specified coordinates converted to the type given in the generic part of the function. +// Determines the last visible child of the given node without initialization. -var - lNode: PVirtualNode; begin - lNode := GetNodeAt(pXCoord, pYCoord); - Result := Self.GetNodeData(lNode); + if (Node = nil) or (Node = FRoot) then + Result := GetLastChildNoInit(FRoot) + else + if FullyVisible[Node] and (vsExpanded in Node.States) then + Result := GetLastChildNoInit(Node) + else + Result := nil; + + if Assigned(Result) and (not (vsVisible in Result.States) or + (not IncludeFiltered and IsEffectivelyFiltered[Result])) then + Result := GetPreviousVisibleSiblingNoInit(Result, IncludeFiltered); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetFirstSelectedNodeData(): T; +function TBaseVirtualTree.GetLastVisibleNoInit(Node: PVirtualNode = nil; + ConsiderChildrenAbove: Boolean = True; IncludeFiltered: Boolean = False): PVirtualNode; -// Returns of the first selected node associated data converted to the type given in the generic part of the function. +// Returns the very last visible node in the tree while optionally considering toChildrenAbove. +// No initialization is performed. +var + Next: PVirtualNode; begin - Result := Self.GetNodeData(GetFirstSelected()); + Result := GetLastVisibleChildNoInit(Node, IncludeFiltered); + if not ConsiderChildrenAbove or not (toChildrenAbove in FOptions.PaintOptions) then + while Assigned(Result) and (vsExpanded in Result.States) do + begin + // Test if there is a next last child. If not keep the node from the last run. + // Otherwise use the next last child. + Next := GetLastChildNoInit(Result); + if Assigned(Next) and (not (vsVisible in Next.States) or + (not IncludeFiltered and IsEffectivelyFiltered[Next])) then + Next := GetPreviousVisibleSiblingNoInit(Next, IncludeFiltered); + if Next = nil then + Break; + Result := Next; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetNodeLevel(Node: PVirtualNode): Cardinal; +function TBaseVirtualTree.GetMaxColumnWidth(Column: TColumnIndex; UseSmartColumnWidth: Boolean = False): Integer; -// returns the level of the given node +// This method determines the width of the largest node in the given column. +// If UseSmartColumnWidth is True then only the visible nodes which are in view will be considered +// Note: If UseSmartColumnWidth is False then every visible node in the tree will be initialized contradicting so +// the virtual paradigm. var - Run: PVirtualNode; - + Run, + LastNode, + NextNode: PVirtualNode; + TextLeft, + CurrentWidth: Integer; + lOffsets: TVTOffsets; begin - Result := 0; - if Assigned(Node) and (Node <> FRoot) then + if OperationCanceled then begin - Run := Node.Parent; - while Run <> FRoot do + // Behave non-destructive. + Result := FHeader.Columns[Column].Width; + Exit; + end + else + Result := 0; + + StartOperation(okGetMaxColumnWidth); + try + if Assigned(FOnBeforeGetMaxColumnWidth) then + FOnBeforeGetMaxColumnWidth(FHeader, Column, UseSmartColumnWidth); + + if UseSmartColumnWidth then // Get first visible node which is in view. + Run := GetTopNode + else + Run := GetFirstVisible(nil, True); + + // Decide where to stop. + if UseSmartColumnWidth then + LastNode := GetNextVisible(BottomNode) + else + LastNode := nil; + + if hoAutoResizeInclCaption in FHeader.Options then + Result := Result + (2 * Header.Columns[Column].Margin + Header.Columns[Column].CaptionWidth + 2); + + while Assigned(Run) and not OperationCanceled do begin - Run := Run.Parent; - Inc(Result); + GetOffsets(Run, lOffsets, TVTElement.ofsLabel, Column); + TextLeft := lOffsets[TVTElement.ofsLabel]; + CurrentWidth := DoGetNodeWidth(Run, Column); + Inc(CurrentWidth, DoGetNodeExtraWidth(Run, Column)); + Inc(CurrentWidth, DoGetCellContentMargin(Run, Column).X); + + // Background for fix: + // DoGetNodeWidth works correctly to return just the + // headerwidth in vsMultiline state of the node. But the + // following code was adding TextLeft unnecessarily. This + // caused a width increase each time a column splitter + // was double-clicked for the option hoDblClickResize that + // really does not apply for vsMultiline case. + // Fix: If the node is multiline, leave the current width as + // it is as returned by DoGetNodeWidth logic above. + if (Column > NoColumn) and (vsMultiline in Run.States) then + Result := CurrentWidth + else + if Result < (TextLeft + CurrentWidth) then + Result := TextLeft + CurrentWidth; + + // Get next visible node and update left node position if needed. + NextNode := GetNextVisible(Run, True); + if NextNode = LastNode then + Break; + Run := NextNode; end; - end; -end; + if toShowVertGridLines in FOptions.PaintOptions then + Inc(Result); + if Assigned(FOnAfterGetMaxColumnWidth) then + FOnAfterGetMaxColumnWidth(FHeader, Column, Result); -//---------------------------------------------------------------------------------------------------------------------- -// Function introduced to avoid spaghetti code to fix setting of FLastSelectionLevel -// at various places that now needs to avoid setting it for a disabled node -function TBaseVirtualTree.GetNodeLevelForSelectConstraint(Node: PVirtualNode): integer; -begin - if Assigned(Node) and not (vsDisabled in Node.States) then - result := GetNodeLevel(Node) - else - result := -1; + finally + EndOperation(okGetMaxColumnWidth); + end; end; - //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPrevious(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; - -// Returns previous node in tree. If ConsiderChildrenAbove is True the function considers -// whether toChildrenAbove is currently set, otherwise the result will always be the previous -// node in top-down order regardless of the current PaintOptions. -// The Result will be initialized if needed. +function TBaseVirtualTree.GetNext(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; -var - Run: PVirtualNode; +// Returns next node in tree while optionally considering toChildrenAbove. The Result will be initialized if needed. begin Result := Node; @@ -28930,50 +19873,69 @@ function TBaseVirtualTree.GetPrevious(Node: PVirtualNode; ConsiderChildrenAbove: if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin - // Has this node got children? Initialize them if necessary. - if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then - InitChildren(Result); - - // If there is a last child, take it; if not try the previous sibling. - if Assigned(Result.LastChild) then - Result := Result.LastChild - else - if Assigned(Result.PrevSibling) then - Result := Result.PrevSibling + // If this node has no siblings use the parent. + if not Assigned(Result.NextSibling) then + begin + Result := Result.Parent; + if Result = FRoot then + begin + Result := nil; + end; + end else begin - // If neither a last child nor a previous sibling exist, go the tree upwards and - // look, wether one of the parent nodes have a previous sibling. If not the result - // will ne nil. - repeat - Result := Result.Parent; - Run := nil; - if Result <> FRoot then - Run := Result.PrevSibling - else - Result := nil; - until Assigned(Run) or (Result = nil); + // There is at least one sibling so take it. + Result := Result.NextSibling; - if Assigned(Run) then - Result := Run; + // Has this node got children? Initialize them if necessary. + if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then + InitChildren(Result); + + // Now take a look at the children. + while Assigned(Result.FirstChild) do + begin + Result := Result.FirstChild; + if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then + InitChildren(Result); + end; end; end else begin - // Is there a previous sibling? - if Assigned(Node.PrevSibling) then + // Has this node got children? + if vsHasChildren in Result.States then begin - // Go down and find the last child node. - Result := GetLast(Node.PrevSibling); - if Result = nil then - Result := Node.PrevSibling; - end + // Yes, there are child nodes. Initialize them if necessary. + if Result.ChildCount = 0 then + InitChildren(Result); + end; + + // if there is no child node try siblings + if Assigned(Result.FirstChild) then + Result := Result.FirstChild else - // no previous sibling so the parent of the node is the previous visible node - if Node.Parent <> FRoot then - Result := Node.Parent - else - Result := nil; + begin + repeat + // Is there a next sibling? + if Assigned(Result.NextSibling) then + begin + Result := Result.NextSibling; + Break; + end + else + begin + // No sibling anymore, so use the parent's next sibling. + if Result.Parent <> FRoot then + Result := Result.Parent + else + begin + // There are no further nodes to examine, hence there is no further visible node. + Result := nil; + Break; + end; + end; + until False; + end; end; end; @@ -28983,17 +19945,17 @@ function TBaseVirtualTree.GetPrevious(Node: PVirtualNode; ConsiderChildrenAbove: //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousChecked(Node: PVirtualNode; State: TCheckState = csCheckedNormal; +function TBaseVirtualTree.GetNextChecked(Node: PVirtualNode; State: TCheckState = csCheckedNormal; ConsiderChildrenAbove: Boolean = False): PVirtualNode; begin if (Node = nil) or (Node = FRoot) then - Result := GetLastNoInit(nil, ConsiderChildrenAbove) + Result := GetFirstNoInit(ConsiderChildrenAbove) else - Result := GetPreviousNoInit(Node, ConsiderChildrenAbove); + Result := GetNextNoInit(Node, ConsiderChildrenAbove); while Assigned(Result) and (GetCheckState(Result) <> State) do - Result := GetPreviousNoInit(Result, ConsiderChildrenAbove); + Result := GetNextNoInit(Result, ConsiderChildrenAbove); if Assigned(Result) and not (vsInitialized in Result.States) then InitNode(Result); @@ -29001,9 +19963,16 @@ function TBaseVirtualTree.GetPreviousChecked(Node: PVirtualNode; State: TCheckSt //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousCutCopy(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetNextChecked(Node: PVirtualNode; ConsiderChildrenAbove: Boolean): PVirtualNode; +begin + Result := Self.GetNextChecked(Node, csCheckedNormal, ConsiderChildrenAbove); +end; -// Returns the previous node in the tree which is currently marked for a clipboard operation. Since only visible nodes can +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetNextCutCopy(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; + +// Returns the next node in the tree which is currently marked for a clipboard operation. Since only visible nodes can // be marked (or they are hidden after they have been marked) it is not necessary to initialize nodes to check for // child nodes. The result, however, is initialized if necessary. @@ -29011,11 +19980,11 @@ function TBaseVirtualTree.GetPreviousCutCopy(Node: PVirtualNode; ConsiderChildre if ClipboardStates * FStates <> [] then begin if (Node = nil) or (Node = FRoot) then - Result := GetLastNoInit(nil, ConsiderChildrenAbove) + Result := GetFirstNoInit(ConsiderChildrenAbove) else - Result := GetPreviousNoInit(Node, ConsiderChildrenAbove); + Result := GetNextNoInit(Node, ConsiderChildrenAbove); while Assigned(Result) and not (vsCutOrCopy in Result.States) do - Result := GetPreviousNoInit(Result, ConsiderChildrenAbove); + Result := GetNextNoInit(Result, ConsiderChildrenAbove); if Assigned(Result) and not (vsInitialized in Result.States) then InitNode(Result); end @@ -29025,45 +19994,44 @@ function TBaseVirtualTree.GetPreviousCutCopy(Node: PVirtualNode; ConsiderChildre //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousInitialized(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetNextInitialized(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Returns the previous node in tree which is initialized. +// Returns the next node in tree which is initialized. begin Result := Node; repeat - Result := GetPreviousNoInit(Result, ConsiderChildrenAbove); + Result := GetNextNoInit(Result, ConsiderChildrenAbove); until (Result = nil) or (vsInitialized in Result.States); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousLeaf(Node: PVirtualNode): PVirtualNode; +function TBaseVirtualTree.GetNextLeaf(Node: PVirtualNode): PVirtualNode; -// Returns the previous node in the tree which has currently no children. +// Returns the next node in the tree which has currently no children. // The result is initialized if necessary. begin if (Node = nil) or (Node = FRoot) then - Result := FRoot.LastChild + Result := FRoot.FirstChild else - Result := GetPrevious(Node); + Result := GetNext(Node); while Assigned(Result) and (vsHasChildren in Result.States) do - Result := GetPrevious(Result); + Result := GetNext(Result); if Assigned(Result) and not (vsInitialized in Result.States) then InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousLevel(Node: PVirtualNode; NodeLevel: Cardinal): PVirtualNode; +function TBaseVirtualTree.GetNextLevel(Node: PVirtualNode; NodeLevel: Cardinal): PVirtualNode; -// Returns the previous node in the tree on a specific level. +// Returns the next node in the tree on a specific level. // The result is initialized if necessary. var StartNodeLevel: Cardinal; - Run: PVirtualNode; begin Result := nil; @@ -29074,45 +20042,30 @@ function TBaseVirtualTree.GetPreviousLevel(Node: PVirtualNode; NodeLevel: Cardin if StartNodeLevel < NodeLevel then begin - Result := Node.PrevSibling; - if Assigned(Result) then - begin - // go to last descendant of previous sibling with desired node level (if exists) - Run := Result; - while Assigned(Run) and (GetNodeLevel(Run) < NodeLevel) do - begin - Result := Run; - Run := GetLastChild(Run); - end; - if Assigned(Run) and (GetNodeLevel(Run) = NodeLevel) then - Result := Run - else - begin - if Assigned(Result.PrevSibling) then - Result := GetPreviousLevel(Result, NodeLevel) - else - if Assigned(Result) and (Result.Parent <> FRoot) then - Result := GetPreviousLevel(Result.Parent, NodeLevel) - else - Result := nil; - end; - end - else - Result := GetPreviousLevel(Node.Parent, NodeLevel); + Result := GetNext(Node); + if Assigned(Result) and (GetNodeLevel(Result) <> NodeLevel) then + Result := GetNextLevel(Result, NodeLevel); end else if StartNodeLevel = NodeLevel then begin - Result := Node.PrevSibling; - if not Assigned(Result) then // i.e. start node was a first sibling + Result := Node.NextSibling; + if not Assigned(Result) then // i.e. start node was a last sibling begin Result := Node.Parent; if Assigned(Result) then - Result := GetPreviousLevel(Result, NodeLevel); + begin + // go to next anchestor of the start node which has a next sibling (if exists) + while Assigned(Result) and not Assigned(Result.NextSibling) do + Result := Result.Parent; + if Assigned(Result) then + Result := GetNextLevel(Result.NextSibling, NodeLevel); + end; end; end - else // i.e. StartNodeLevel > NodeLevel - Result := GetPreviousLevel(Node.Parent, NodeLevel); + else + // i.e. StartNodeLevel > NodeLevel + Result := GetNextLevel(Node.Parent, NodeLevel); end; if Assigned(Result) and not (vsInitialized in Result.States) then @@ -29121,12 +20074,9 @@ function TBaseVirtualTree.GetPreviousLevel(Node: PVirtualNode; NodeLevel: Cardin //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; - -// Returns previous node in tree, optionally considering toChildrenAbove. No initialization is performed. +function TBaseVirtualTree.GetNextNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean): PVirtualNode; -var - Run: PVirtualNode; +// Optimized version of GetNext performing no initialization, but optionally considering toChildrenAbove. begin Result := Node; @@ -29136,55 +20086,64 @@ function TBaseVirtualTree.GetPreviousNoInit(Node: PVirtualNode; ConsiderChildren if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin - // If there is a last child, take it; if not try the previous sibling. - if Assigned(Result.LastChild) then - Result := Result.LastChild - else - if Assigned(Result.PrevSibling) then - Result := Result.PrevSibling - else + // If this node has no siblings use the parent. + if not Assigned(Result.NextSibling) then + begin + Result := Result.Parent; + if Result = FRoot then begin - // If neither a last child nor a previous sibling exist, go the tree upwards and - // look, wether one of the parent nodes have a previous sibling. If not the result - // will ne nil. - repeat - Result := Result.Parent; - Run := nil; - if Result <> FRoot then - Run := Result.PrevSibling - else - Result := nil; - until Assigned(Run) or (Result = nil); + Result := nil; + end; + end + else + begin + // There is at least one sibling so take it. + Result := Result.NextSibling; - if Assigned(Run) then - Result := Run; + // Now take a look at the children. + while Assigned(Result.FirstChild) do + begin + Result := Result.FirstChild; end; + end; end else begin - // Is there a previous sibling? - if Assigned(Node.PrevSibling) then - begin - // Go down and find the last child node. - Result := GetLastNoInit(Node.PrevSibling); - if Result = nil then - Result := Node.PrevSibling; - end + // If there is no child node try siblings. + if Assigned(Result.FirstChild) then + Result := Result.FirstChild else - // No previous sibling so the parent of the node is the previous node. - if Node.Parent <> FRoot then - Result := Node.Parent - else - Result := nil; + begin + repeat + // Is there a next sibling? + if Assigned(Result.NextSibling) then + begin + Result := Result.NextSibling; + Break; + end + else + begin + // No sibling anymore, so use the parent's next sibling. + if Result.Parent <> FRoot then + Result := Result.Parent + else + begin + // There are no further nodes to examine, hence there is no further visible node. + Result := nil; + Break; + end; + end; + until False; + end; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousSelected(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetNextSelected(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Returns the previous node in the tree which is currently selected. Since children of unitialized nodes cannot be +// Returns the next node in the tree which is currently selected. Since children of unitialized nodes cannot be // in the current selection (because they simply do not exist yet) it is not necessary to initialize nodes here. // The result however is initialized if necessary. @@ -29192,11 +20151,11 @@ function TBaseVirtualTree.GetPreviousSelected(Node: PVirtualNode; ConsiderChildr if FSelectionCount > 0 then begin if (Node = nil) or (Node = FRoot) then - Result := FRoot.LastChild + Result := GetFirstNoInit(ConsiderChildrenAbove) else - Result := GetPreviousNoInit(Node, ConsiderChildrenAbove); + Result := GetNextNoInit(Node, ConsiderChildrenAbove); while Assigned(Result) and not (vsSelected in Result.States) do - Result := GetPreviousNoInit(Result, ConsiderChildrenAbove); + Result := GetNextNoInit(Result, ConsiderChildrenAbove); if Assigned(Result) and not (vsInitialized in Result.States) then InitNode(Result); end @@ -29206,9 +20165,9 @@ function TBaseVirtualTree.GetPreviousSelected(Node: PVirtualNode; ConsiderChildr //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousSibling(Node: PVirtualNode): PVirtualNode; +function TBaseVirtualTree.GetNextSibling(Node: PVirtualNode): PVirtualNode; -// Returns the previous sibling of Node and initializes it if necessary. +// Returns the next sibling of Node and initializes it if necessary. begin Result := Node; @@ -29216,15 +20175,15 @@ function TBaseVirtualTree.GetPreviousSibling(Node: PVirtualNode): PVirtualNode; begin Assert(Result <> FRoot, 'Node must not be the hidden root node.'); - Result := Result.PrevSibling; + Result := Result.NextSibling; if Assigned(Result) and not (vsInitialized in Result.States) then InitNode(Result); end; end; -function TBaseVirtualTree.GetPreviousSiblingNoInit(Node: PVirtualNode): PVirtualNode; +function TBaseVirtualTree.GetNextSiblingNoInit(Node: PVirtualNode): PVirtualNode; -// Returns the previous sibling of Node +// Returns the next sibling of Node. begin Result := Node; @@ -29232,20 +20191,20 @@ function TBaseVirtualTree.GetPreviousSiblingNoInit(Node: PVirtualNode): PVirtual begin Assert(Result <> FRoot, 'Node must not be the hidden root node.'); - Result := Result.PrevSibling; + Result := Result.NextSibling; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousVisible(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode; +function TBaseVirtualTree.GetNextVisible(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode; -// Returns the previous node in tree, with regard to Node, which is visible. +// Returns next node in tree, with regard to Node, which is visible. // Nodes which need an initialization (including the result) are initialized. // toChildrenAbove is optionally considered which is the default here. var - Marker: PVirtualNode; + ForceSearch: Boolean; begin Result := Node; @@ -29254,92 +20213,101 @@ function TBaseVirtualTree.GetPreviousVisible(Node: PVirtualNode; ConsiderChildre Assert(Result <> FRoot, 'Node must not be the hidden root node.'); repeat - // If the given node is not visible then look for a parent node which is visible and use its last visible - // child or the parent node (if there is no visible child) as result. + // If the given node is not visible then look for a parent node which is visible, otherwise we will + // likely go unnecessarily through a whole bunch of invisible nodes. if not FullyVisible[Result] then - begin Result := GetVisibleParent(Result, True); - if Result = FRoot then - Result := nil; - Marker := GetLastVisible(Result, True); - if Assigned(Marker) then - Result := Marker; - end - else + + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then - begin - repeat - if Assigned(Result.LastChild) and (vsExpanded in Result.States) then + repeat + // If there a no siblings anymore, go up one level. + if not Assigned(Result.NextSibling) then + begin + Result := Result.Parent; + if Result = FRoot then begin - Result := Result.LastChild; + Result := nil; + Break; + end; + + if not (vsInitialized in Result.States) then + InitNode(Result); + if vsVisible in Result.States then + Break; + end + else + begin + // There is at least one sibling so take it. + Result := Result.NextSibling; + if not (vsInitialized in Result.States) then + InitNode(Result); + if not (vsVisible in Result.States) then + Continue; + + // Now take a look at the children. + // As the children are initialized while toggling, we don't need to do this here. + while (vsExpanded in Result.States) and Assigned(Result.FirstChild) do + begin + Result := Result.FirstChild; if not (vsInitialized in Result.States) then InitNode(Result); - - if vsVisible in Result.States then + if not (vsVisible in Result.States) then Break; - end - else - if Assigned(Result.PrevSibling) then - begin - if not (vsInitialized in Result.PrevSibling.States) then - InitNode(Result.PrevSibling); + end; - if vsVisible in Result.PrevSibling.States then - begin - Result := Result.PrevSibling; - Break; - end; - end - else - begin - Marker := nil; - repeat - Result := Result.Parent; - if Result <> FRoot then - Marker := GetPreviousVisibleSibling(Result, True) - else - Result := nil; - until Assigned(Marker) or (Result = nil); - if Assigned(Marker) then - Result := Marker; + // If we found a visible node we don't need to search any longer. + if vsVisible in Result.States then + Break; + end; + until False; + end + else + begin + // Has this node got children? + if [vsHasChildren, vsExpanded] * Result.States = [vsHasChildren, vsExpanded] then + begin + // Yes, there are child nodes. Initialize them if necessary. + if Result.ChildCount = 0 then + InitChildren(Result); + end; - Break; - end; - until False; + // Child nodes are the first choice if possible. + if (vsExpanded in Result.States) and Assigned(Result.FirstChild) then + begin + Result := GetFirstChild(Result); + ForceSearch := False; end else + ForceSearch := True; + + // If there are no children or the first child is not visible then search the sibling nodes or traverse parents. + if Assigned(Result) and (ForceSearch or not (vsVisible in Result.States)) then begin repeat - // Is there a previous sibling node? - if Assigned(Result.PrevSibling) then + // Is there a next sibling? + if Assigned(Result.NextSibling) then begin - Result := Result.PrevSibling; - // Initialize the new node and check its visibility. + Result := Result.NextSibling; if not (vsInitialized in Result.States) then InitNode(Result); if vsVisible in Result.States then - begin - // If there are visible child nodes then use the last one. - Marker := GetLastVisible(Result, True, True); - if Assigned(Marker) then - Result := Marker; Break; - end; end else begin - // No previous sibling there so the parent node is the nearest previous node. - Result := Result.Parent; - if Result = FRoot then + // No sibling anymore, so use the parent's next sibling. + if Result.Parent <> FRoot then + Result := Result.Parent + else + begin + // There are no further nodes to examine, hence there is no further visible node. Result := nil; - Break; + Break; + end; end; until False; end; - - if Assigned(Result) and not (vsInitialized in Result.States) then - InitNode(Result); end; until not Assigned(Result) or IsEffectivelyVisible[Result]; end; @@ -29347,14 +20315,13 @@ function TBaseVirtualTree.GetPreviousVisible(Node: PVirtualNode; ConsiderChildre //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousVisibleNoInit(Node: PVirtualNode; - ConsiderChildrenAbove: Boolean = True): PVirtualNode; +function TBaseVirtualTree.GetNextVisibleNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode; -// Returns the previous node in tree, with regard to Node, which is visible. -// toChildrenAbove is optionally considered which is the default here. +// Returns the next node in tree, with regard to Node, which is visible. +// toChildrenAbove is optionally considered (which is the default). No initialization is done. var - Marker: PVirtualNode; + ForceSearch: Boolean; begin Result := Node; @@ -29363,79 +20330,80 @@ function TBaseVirtualTree.GetPreviousVisibleNoInit(Node: PVirtualNode; Assert(Result <> FRoot, 'Node must not be the hidden root node.'); repeat - // If the given node is not visible then look for a parent node which is visible and use its last visible - // child or the parent node (if there is no visible child) as result. - if not FullyVisible[Result] then + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then begin - Result := GetVisibleParent(Result, True); - if Result = FRoot then - Result := nil; - Marker := GetLastVisibleNoInit(Result, True); - if Assigned(Marker) then - Result := Marker; + repeat + // If there a no siblings anymore, go up one level. + if not Assigned(Result.NextSibling) then + begin + Result := Result.Parent; + if Result = FRoot then + begin + Result := nil; + Break; + end; + if vsVisible in Result.States then + Break; + end + else + begin + // There is at least one sibling so take it. + Result := Result.NextSibling; + if not (vsVisible in Result.States) then + Continue; + + // Now take a look at the children. + while (vsExpanded in Result.States) and Assigned(Result.FirstChild) do + begin + Result := Result.FirstChild; + if not (vsVisible in Result.States) then + Break; + end; + + // If we found a visible node we don't need to search any longer. + if vsVisible in Result.States then + Break; + end; + until False; end else begin - if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then + // If the given node is not visible then look for a parent node which is visible, otherwise we will + // likely go unnecessarily through a whole bunch of invisible nodes. + if not FullyVisible[Result] then + Result := GetVisibleParent(Result, True); + + // Child nodes are the first choice if possible. + if (vsExpanded in Result.States) and Assigned(Result.FirstChild) then begin - repeat - // Is the current node expanded and has children? - if (vsExpanded in Result.States) and Assigned(Result.LastChild) then - begin - Result := Result.LastChild; - if vsVisible in Result.States then - Break; - end - else - if Assigned(Result.PrevSibling) then - begin - // No children anymore, so take the previous sibling. - Result := Result.PrevSibling; - if vsVisible in Result.States then - Break; - end - else - begin - // No children and no previous siblings, so walk up the tree and look wether - // a parent has a previous visible sibling. If that is the case take it, - // otherwise there is no previous visible node. - Marker := nil; - repeat - Result := Result.Parent; - if Result <> FRoot then - Marker := GetPreviousVisibleSiblingNoInit(Result, True) - else - Result := nil; - until Assigned(Marker) or (Result = nil); - if Assigned(Marker) then - Result := Marker; - Break; - end; - until False; + Result := Result.FirstChild; + ForceSearch := False; end else + ForceSearch := True; + + // If there are no children or the first child is not visible then search the sibling nodes or traverse parents. + if ForceSearch or not (vsVisible in Result.States) then begin repeat - // Is there a previous sibling node? - if Assigned(Result.PrevSibling) then + // Is there a next sibling? + if Assigned(Result.NextSibling) then begin - Result := Result.PrevSibling; + Result := Result.NextSibling; if vsVisible in Result.States then - begin - // If there are visible child nodes then use the last one. - Marker := GetLastVisibleNoInit(Result, True, True); - if Assigned(Marker) then - Result := Marker; Break; - end; end else begin - // No previous sibling there so the parent node is the nearest previous node. - Result := Result.Parent; - if Result = FRoot then + // No sibling anymore, so use the parent's next sibling. + if Result.Parent <> FRoot then + Result := Result.Parent + else + begin + // There are no further nodes to examine, hence there is no further visible node. Result := nil; - Break; + Break; + end; end; until False; end; @@ -29446,4149 +20414,4242 @@ function TBaseVirtualTree.GetPreviousVisibleNoInit(Node: PVirtualNode; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousVisibleSibling(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetNextVisibleSibling(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; -// Returns the previous visible sibling before Node. Initialization is done implicitly. +// Returns the next visible sibling after Node. Initialization is done implicitly. begin Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); Result := Node; repeat - Result := GetPreviousSibling(Result); + Result := GetNextSibling(Result); until not Assigned(Result) or ((vsVisible in Result.States) and (IncludeFiltered or not IsEffectivelyFiltered[Result])); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetPreviousVisibleSiblingNoInit(Node: PVirtualNode; - IncludeFiltered: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetNextVisibleSiblingNoInit(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; -// Returns the previous visible sibling before Node. +// Returns the next visible sibling after Node. begin Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); Result := Node; repeat - Result := Result.PrevSibling; + Result := Result.NextSibling; until not Assigned(Result) or ((vsVisible in Result.States) and - (IncludeFiltered or not IsEffectivelyFiltered[Result])); + (IncludeFiltered or not IsEffectivelyFiltered[Result])); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.Nodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetNodeAt(X, Y: Integer): PVirtualNode; -// Enumeration for all nodes +// Overloaded variant of GetNodeAt to easy life of application developers which do not need to have the exact +// top position returned and always use client coordinates. + +var + Dummy: Integer; begin - Result.FMode := vneAll; - Result.FTree := Self; - Result.FConsiderChildrenAbove := ConsiderChildrenAbove; + Result := GetNodeAt(X, Y, True, Dummy); +end; + +function TBaseVirtualTree.GetNodeAt(const P: TPoint): PVirtualNode; +begin + Result := GetNodeAt(P.X, P.Y); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.CheckedNodes(State: TCheckState; ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetNodeAt(X, Y: Integer; Relative: Boolean; var NodeTop: Integer): PVirtualNode; -// Enumeration for all checked nodes +// This method returns the node that occupies the specified point, or nil if there's none. +// If Releative is True then X and Y are given in client coordinates otherwise they are considered as being +// absolute values into the virtual tree image (regardless of the current offsets in the tree window). +// NodeTop gets the absolute or relative top position of the node returned or is untouched if no node +// could be found. + +var + AbsolutePos, + CurrentPos: Cardinal; begin - Result.FMode := vneChecked; - Result.FTree := Self; - Result.FState := State; - Result.FConsiderChildrenAbove := ConsiderChildrenAbove; + if Y < 0 then + Y := 0; + + AbsolutePos := Y; + if Relative then + Inc(AbsolutePos, -FOffsetY); + + // CurrentPos tracks a running term of the current position to test for. + // It corresponds always to the top position of the currently considered node. + CurrentPos := 0; + + // If the cache is available then use it. + if tsUseCache in FStates then + Result := FindInPositionCache(AbsolutePos, CurrentPos) + else + Result := GetFirstVisibleNoInit(nil, True); + + // Determine node, of which position and height corresponds to the scroll position most closely. + while Assigned(Result) and (Result <> FRoot) do + begin + if AbsolutePos < (CurrentPos + NodeHeight[Result]) then + Break; + Inc(CurrentPos, NodeHeight[Result]); + Result := GetNextVisibleNoInit(Result, True); + end; + + if Result = FRoot then + Result := nil; + + // Since the given vertical position is likely not the same as the top position + // of the found node this top position is returned. + if Assigned(Result) then + begin + NodeTop := CurrentPos; + if Relative then + Inc(NodeTop, FOffsetY); + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.ChildNodes(Node: PVirtualNode): TVTVirtualNodeEnumeration; -// Enumeration for child nodes +function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer; + +// Returns the address of the user defined data area in the node. begin - Result.FMode := vneChild; - Result.FTree := Self; - Result.FNode := Node; + Assert((FNodeDataSize > 0) or not Assigned(Node), 'NodeDataSize not initialized.'); + if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then + Result := nil + else + begin + Result := @Node.Data; + Include(Node.States, vsOnFreeNodeCallRequired); // We now need to call OnFreeNode, see bug #323 + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.CutCopyNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetNodeData(pNode: PVirtualNode): T; -// Enumeration for cut copy node +// Returns the associated data converted to the class given in the generic part of the function. + +var + P: Pointer; +begin + P := Self.GetNodeData(pNode); + if Assigned(P) then + Exit(T(P^)) + else + Exit(Default(T)); +end; +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetInterfaceFromNodeData(pNode: PVirtualNode): T; begin - Result.FMode := vneCutCopy; - Result.FTree := Self; - Result.FConsiderChildrenAbove := ConsiderChildrenAbove; + if Assigned(pNode) then + Result := T(Self.GetNodeData(pNode)^) + else + Result := nil; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.InitializedNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetNodeDataAt(pXCoord, pYCoord: Integer): T; -// Enumeration for initialized nodes +// Returns the associated data at the specified coordinates converted to the type given in the generic part of the function. +var + lNode: PVirtualNode; begin - Result.FMode := vneInitialized; - Result.FTree := Self; - Result.FConsiderChildrenAbove := ConsiderChildrenAbove; + lNode := GetNodeAt(pXCoord, pYCoord); + Result := Self.GetNodeData(lNode); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.LeafNodes: TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetFirstSelectedNodeData(): T; -// Enumeration for leaf nodes +// Returns of the first selected node associated data converted to the type given in the generic part of the function. begin - Result.FMode := vneLeaf; - Result.FTree := Self; + Result := Self.GetNodeData(GetFirstSelected()); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.LevelNodes(NodeLevel: Cardinal): TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetNodeLevel(Node: PVirtualNode): Cardinal; -// Enumeration for level nodes +// returns the level of the given node + +var + Run: PVirtualNode; begin - Result.FMode := vneLevel; - Result.FTree := Self; - Result.FNodeLevel := NodeLevel; + Result := 0; + if Assigned(Node) and (Node <> FRoot) then + begin + Run := Node.Parent; + while Run <> FRoot do + begin + Run := Run.Parent; + Inc(Result); + end; + end; +end; + + +//---------------------------------------------------------------------------------------------------------------------- +// Function introduced to avoid spaghetti code to fix setting of FLastSelectionLevel +// at various places that now needs to avoid setting it for a disabled node +function TBaseVirtualTree.GetNodeLevelForSelectConstraint(Node: PVirtualNode): integer; +begin + if Assigned(Node) and not (vsDisabled in Node.States) then + result := GetNodeLevel(Node) + else + result := -1; end; + //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.NoInitNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetPrevious(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; + +// Returns previous node in tree. If ConsiderChildrenAbove is True the function considers +// whether toChildrenAbove is currently set, otherwise the result will always be the previous +// node in top-down order regardless of the current PaintOptions. +// The Result will be initialized if needed. + +var + Run: PVirtualNode; + +begin + Result := Node; + if Assigned(Result) then + begin + Assert(Result <> FRoot, 'Node must not be the hidden root node.'); + + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then + begin + // Has this node got children? Initialize them if necessary. + if (vsHasChildren in Result.States) and (Result.ChildCount = 0) then + InitChildren(Result); + + // If there is a last child, take it; if not try the previous sibling. + if Assigned(Result.LastChild) then + Result := Result.LastChild + else + if Assigned(Result.PrevSibling) then + Result := Result.PrevSibling + else + begin + // If neither a last child nor a previous sibling exist, go the tree upwards and + // look, wether one of the parent nodes have a previous sibling. If not the result + // will ne nil. + repeat + Result := Result.Parent; + Run := nil; + if Result <> FRoot then + Run := Result.PrevSibling + else + Result := nil; + until Assigned(Run) or (Result = nil); + + if Assigned(Run) then + Result := Run; + end; + end + else + begin + // Is there a previous sibling? + if Assigned(Node.PrevSibling) then + begin + // Go down and find the last child node. + Result := GetLast(Node.PrevSibling); + if Result = nil then + Result := Node.PrevSibling; + end + else + // no previous sibling so the parent of the node is the previous visible node + if Node.Parent <> FRoot then + Result := Node.Parent + else + Result := nil; + end; + end; -// Enumeration for no init nodes -begin - Result.FMode := vneNoInit; - Result.FTree := Self; - Result.FConsiderChildrenAbove := ConsiderChildrenAbove; + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.SelectedNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; - -// Enumeration for selected nodes +function TBaseVirtualTree.GetPreviousChecked(Node: PVirtualNode; State: TCheckState = csCheckedNormal; + ConsiderChildrenAbove: Boolean = False): PVirtualNode; begin - Result.FMode := vneSelected; - Result.FTree := Self; - Result.FConsiderChildrenAbove := ConsiderChildrenAbove; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.VisibleNodes(Node: PVirtualNode; ConsiderChildrenAbove: Boolean; - IncludeFiltered: Boolean): TVTVirtualNodeEnumeration; + if (Node = nil) or (Node = FRoot) then + Result := GetLastNoInit(nil, ConsiderChildrenAbove) + else + Result := GetPreviousNoInit(Node, ConsiderChildrenAbove); -// Enumeration for visible nodes + while Assigned(Result) and (GetCheckState(Result) <> State) do + Result := GetPreviousNoInit(Result, ConsiderChildrenAbove); -begin - Result.FMode := vneVisible; - Result.FTree := Self; - Result.FNode := Node; - Result.FConsiderChildrenAbove := ConsiderChildrenAbove; - Result.FIncludeFiltered := IncludeFiltered; + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.VisibleChildNodes(Node: PVirtualNode; IncludeFiltered: Boolean): TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetPreviousCutCopy(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Enumeration for visible child nodes +// Returns the previous node in the tree which is currently marked for a clipboard operation. Since only visible nodes can +// be marked (or they are hidden after they have been marked) it is not necessary to initialize nodes to check for +// child nodes. The result, however, is initialized if necessary. begin - Result.FMode := vneVisibleChild; - Result.FTree := Self; - Result.FNode := Node; - Result.FIncludeFiltered := IncludeFiltered; + if ClipboardStates * FStates <> [] then + begin + if (Node = nil) or (Node = FRoot) then + Result := GetLastNoInit(nil, ConsiderChildrenAbove) + else + Result := GetPreviousNoInit(Node, ConsiderChildrenAbove); + while Assigned(Result) and not (vsCutOrCopy in Result.States) do + Result := GetPreviousNoInit(Result, ConsiderChildrenAbove); + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); + end + else + Result := nil; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.VisibleChildNoInitNodes(Node: PVirtualNode; IncludeFiltered: Boolean): TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetPreviousInitialized(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Enumeration for visible child no init nodes +// Returns the previous node in tree which is initialized. begin - Result.FMode := vneVisibleNoInitChild; - Result.FTree := Self; - Result.FNode := Node; - Result.FIncludeFiltered := IncludeFiltered; + Result := Node; + repeat + Result := GetPreviousNoInit(Result, ConsiderChildrenAbove); + until (Result = nil) or (vsInitialized in Result.States); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.VisibleNoInitNodes(Node: PVirtualNode; ConsiderChildrenAbove: Boolean; - IncludeFiltered: Boolean): TVTVirtualNodeEnumeration; +function TBaseVirtualTree.GetPreviousLeaf(Node: PVirtualNode): PVirtualNode; -// Enumeration for visible no init nodes +// Returns the previous node in the tree which has currently no children. +// The result is initialized if necessary. begin - Result.FMode := vneVisibleNoInit; - Result.FTree := Self; - Result.FNode := Node; - Result.FConsiderChildrenAbove := ConsiderChildrenAbove; - Result.FIncludeFiltered := IncludeFiltered; + if (Node = nil) or (Node = FRoot) then + Result := FRoot.LastChild + else + Result := GetPrevious(Node); + while Assigned(Result) and (vsHasChildren in Result.States) do + Result := GetPrevious(Result); + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetSortedCutCopySet(Resolve: Boolean): TNodeArray; +function TBaseVirtualTree.GetPreviousLevel(Node: PVirtualNode; NodeLevel: Cardinal): PVirtualNode; -// Same as GetSortedSelection but with nodes marked as being part in the current cut/copy set (e.g. for clipboard). +// Returns the previous node in the tree on a specific level. +// The result is initialized if necessary. var + StartNodeLevel: Cardinal; Run: PVirtualNode; - Counter: Cardinal; - - //--------------- local function -------------------------------------------- - - procedure IncludeThisNode(Node: PVirtualNode); - - // adds the given node to the result - var - Len: Cardinal; +begin + Result := nil; + if Assigned(Node) and (Node <> FRoot) then begin - Len := Length(Result); - if Counter = Len then - begin - if Len < 100 then - Len := 100 - else - Len := Len + Len div 10; - SetLength(Result, Len); - end; - Result[Counter] := Node; - Inc(Counter); - end; - - //--------------- end local function ---------------------------------------- + StartNodeLevel := GetNodeLevel(Node); -begin - Run := FRoot.FirstChild; - Counter := 0; - if Resolve then - begin - // Resolving is actually easy: just find the first cutted node in logical order - // and then never go deeper in level than this node as long as there's a sibling node. - // Restart the search for a cutted node (at any level) if there are no further siblings. - while Assigned(Run) do + if StartNodeLevel < NodeLevel then begin - if vsCutOrCopy in Run.States then + Result := Node.PrevSibling; + if Assigned(Result) then begin - IncludeThisNode(Run); - if Assigned(Run.NextSibling) then - Run := Run.NextSibling + // go to last descendant of previous sibling with desired node level (if exists) + Run := Result; + while Assigned(Run) and (GetNodeLevel(Run) < NodeLevel) do + begin + Result := Run; + Run := GetLastChild(Run); + end; + if Assigned(Run) and (GetNodeLevel(Run) = NodeLevel) then + Result := Run else begin - // If there are no further siblings then go up one or more levels until a node is - // found or all nodes have been processed. Although we consider here only initialized - // nodes we don't need to make any special checks as only initialized nodes can also be selected. - repeat - Run := Run.Parent; - until (Run = FRoot) or Assigned(Run.NextSibling); - if Run = FRoot then - Break + if Assigned(Result.PrevSibling) then + Result := GetPreviousLevel(Result, NodeLevel) else - Run := Run.NextSibling; + if Assigned(Result) and (Result.Parent <> FRoot) then + Result := GetPreviousLevel(Result.Parent, NodeLevel) + else + Result := nil; end; end else - Run := GetNextNoInit(Run); - end; - end - else - while Assigned(Run) do - begin - if vsCutOrCopy in Run.States then - IncludeThisNode(Run); - Run := GetNextNoInit(Run); - end; - - // set the resulting array to its real length - SetLength(Result, Counter); -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.GetSortedSelection(Resolve: Boolean): TNodeArray; - -// Returns a list of selected nodes sorted in logical order, that is, as they appear in the tree. -// If Resolve is True then nodes which are children of other selected nodes are not put into the new array. -// This feature is in particuar important when doing drag'n drop as in this case all selected node plus their children -// need to be considered. A selected node which is child (grand child etc.) of another selected node is then -// automatically included and doesn't need to be explicitely mentioned in the returned selection array. -// -// Note: The caller is responsible for freeing the array. Allocation is done here. Usually, though, freeing the array -// doesn't need additional attention as it is automatically freed by Delphi when it gets out of scope. - -var - Run: PVirtualNode; - Counter: Cardinal; - -begin - SetLength(Result, FSelectionCount); - if FSelectionCount > 0 then - begin - Run := FRoot.FirstChild; - Counter := 0; - if Resolve then - begin - // Resolving is actually easy: just find the first selected node in logical order - // and then never go deeper in level than this node as long as there's a sibling node. - // Restart the search for a selected node (at any level) if there are no further siblings. - while Assigned(Run) do - begin - if vsSelected in Run.States then - begin - Result[Counter] := Run; - Inc(Counter); - if Assigned(Run.NextSibling) then - Run := Run.NextSibling - else - begin - // If there are no further siblings then go up one or more levels until a node is - // found or all nodes have been processed. Although we consider here only initialized - // nodes we don't need to make any special checks as only initialized nodes can also be selected. - repeat - Run := Run.Parent; - until (Run = FRoot) or Assigned(Run.NextSibling); - if Run = FRoot then - Break - else - Run := Run.NextSibling; - end; - end - else - Run := GetNextNoInit(Run); - end; + Result := GetPreviousLevel(Node.Parent, NodeLevel); end else - while Assigned(Run) do + if StartNodeLevel = NodeLevel then begin - if vsSelected in Run.States then + Result := Node.PrevSibling; + if not Assigned(Result) then // i.e. start node was a first sibling begin - Result[Counter] := Run; - Inc(Counter); - end; - Run := GetNextNoInit(Run); - end; - - // Since we may have skipped some nodes the result array is likely to be smaller than the - // selection array, hence shorten the result to true length. - if Integer(Counter) < Length(Result) then - SetLength(Result, Counter); + Result := Node.Parent; + if Assigned(Result) then + Result := GetPreviousLevel(Result, NodeLevel); + end; + end + else // i.e. StartNodeLevel > NodeLevel + Result := GetPreviousLevel(Node.Parent, NodeLevel); end; + + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.GetTextInfo(Node: PVirtualNode; Column: TColumnIndex; const AFont: TFont; var R: TRect; - var Text: string); +function TBaseVirtualTree.GetPreviousNoInit(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Generic base method for editors, hint windows etc. to get some info about a node. +// Returns previous node in tree, optionally considering toChildrenAbove. No initialization is performed. + +var + Run: PVirtualNode; begin - R := Rect(0, 0, 0, 0); - Text := ''; - if Assigned(Font) then // 1 EConvertError due to Font being nil seen here in 01/2019, See issue #878 - AFont.Assign(Font); + Result := Node; + if Assigned(Result) then + begin + Assert(Result <> FRoot, 'Node must not be the hidden root node.'); + + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then + begin + // If there is a last child, take it; if not try the previous sibling. + if Assigned(Result.LastChild) then + Result := Result.LastChild + else + if Assigned(Result.PrevSibling) then + Result := Result.PrevSibling + else + begin + // If neither a last child nor a previous sibling exist, go the tree upwards and + // look, wether one of the parent nodes have a previous sibling. If not the result + // will ne nil. + repeat + Result := Result.Parent; + Run := nil; + if Result <> FRoot then + Run := Result.PrevSibling + else + Result := nil; + until Assigned(Run) or (Result = nil); + + if Assigned(Run) then + Result := Run; + end; + end + else + begin + // Is there a previous sibling? + if Assigned(Node.PrevSibling) then + begin + // Go down and find the last child node. + Result := GetLastNoInit(Node.PrevSibling); + if Result = nil then + Result := Node.PrevSibling; + end + else + // No previous sibling so the parent of the node is the previous node. + if Node.Parent <> FRoot then + Result := Node.Parent + else + Result := nil; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetTreeRect: TRect; +function TBaseVirtualTree.GetPreviousSelected(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = False): PVirtualNode; -// Returns the true size of the tree in pixels. This size is at least ClientHeight x ClientWidth and depends on -// the expand state, header size etc. -// Note: if no columns are used then the width of the tree is determined by the largest node which is currently in the -// client area. This might however not be the largest node in the entire tree. +// Returns the previous node in the tree which is currently selected. Since children of unitialized nodes cannot be +// in the current selection (because they simply do not exist yet) it is not necessary to initialize nodes here. +// The result however is initialized if necessary. begin - Result := Rect(0, 0, Max(FRangeX, ClientWidth), Max(FRangeY, ClientHeight)); + if FSelectionCount > 0 then + begin + if (Node = nil) or (Node = FRoot) then + Result := FRoot.LastChild + else + Result := GetPreviousNoInit(Node, ConsiderChildrenAbove); + while Assigned(Result) and not (vsSelected in Result.States) do + Result := GetPreviousNoInit(Result, ConsiderChildrenAbove); + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); + end + else + Result := nil; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetVisibleParent(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; +function TBaseVirtualTree.GetPreviousSibling(Node: PVirtualNode): PVirtualNode; -// Returns the first (nearest) parent node of Node which is visible. -// This method is one of the seldom cases where the hidden root node could be returned. +// Returns the previous sibling of Node and initializes it if necessary. begin - Assert(Assigned(Node), 'Node must not be nil.'); - Assert(Node <> FRoot, 'Node must not be the hidden root node.'); + Result := Node; + if Assigned(Result) then + begin + Assert(Result <> FRoot, 'Node must not be the hidden root node.'); - Result := Node.Parent; - while (Result <> FRoot) and (not FullyVisible[Result] or (not IncludeFiltered and IsEffectivelyFiltered[Result])) do - Result := Result.Parent; + Result := Result.PrevSibling; + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); + end; end; -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.HasAsParent(Node, PotentialParent: PVirtualNode): Boolean; - -// Determines whether Node has got PotentialParent as one of its parents. +function TBaseVirtualTree.GetPreviousSiblingNoInit(Node: PVirtualNode): PVirtualNode; -var - Run: PVirtualNode; +// Returns the previous sibling of Node begin - Result := Assigned(Node) and Assigned(PotentialParent) and (Node <> PotentialParent); - if Result then + Result := Node; + if Assigned(Result) then begin - Run := Node; - while (Run <> FRoot) and (Run <> PotentialParent) do - Run := Run.Parent; - Result := Run = PotentialParent; + Assert(Result <> FRoot, 'Node must not be the hidden root node.'); + + Result := Result.PrevSibling; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.InsertNode(Node: PVirtualNode; Mode: TVTNodeAttachMode; UserData: Pointer = nil): PVirtualNode; +function TBaseVirtualTree.GetPreviousVisible(Node: PVirtualNode; ConsiderChildrenAbove: Boolean = True): PVirtualNode; -// Adds a new node relative to Node. The final position is determined by Mode. -// UserData can be used to set the first SizeOf(Pointer) bytes of the user data area to an initial value which can be used -// in OnInitNode and will also cause to trigger the OnFreeNode event (if <> nil) even if the node is not yet -// "officially" initialized. -// InsertNode is a compatibility method and will implicitly validate the given node if the new node -// is to be added as child node. This is however against the virtual paradigm and hence I dissuade from its usage. +// Returns the previous node in tree, with regard to Node, which is visible. +// Nodes which need an initialization (including the result) are initialized. +// toChildrenAbove is optionally considered which is the default here. + +var + Marker: PVirtualNode; begin - if Mode <> amNoWhere then + Result := Node; + if Assigned(Result) then begin - CancelEditNode; + Assert(Result <> FRoot, 'Node must not be the hidden root node.'); - if Node = nil then - Node := FRoot; - // we need a new node... - Result := MakeNewNode; - // avoid erronous attach modes - if Node = FRoot then - begin - case Mode of - amInsertBefore: - Mode := amAddChildFirst; - amInsertAfter: - Mode := amAddChildLast; - end; - end; + repeat + // If the given node is not visible then look for a parent node which is visible and use its last visible + // child or the parent node (if there is no visible child) as result. + if not FullyVisible[Result] then + begin + Result := GetVisibleParent(Result, True); + if Result = FRoot then + Result := nil; + Marker := GetLastVisible(Result, True); + if Assigned(Marker) then + Result := Marker; + end + else + begin + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then + begin + repeat + if Assigned(Result.LastChild) and (vsExpanded in Result.States) then + begin + Result := Result.LastChild; + if not (vsInitialized in Result.States) then + InitNode(Result); - // Validate given node in case the new node becomes its child. - if (Mode in [amAddChildFirst, amAddChildLast]) and not (vsInitialized in Node.States) then - InitNode(Node); - InternalConnectNode(Result, Node, Self, Mode); + if vsVisible in Result.States then + Break; + end + else + if Assigned(Result.PrevSibling) then + begin + if not (vsInitialized in Result.PrevSibling.States) then + InitNode(Result.PrevSibling); - // Check if there is initial user data and there is also enough user data space allocated. - if Assigned(UserData) then - SetNodeData(Result, UserData); + if vsVisible in Result.PrevSibling.States then + begin + Result := Result.PrevSibling; + Break; + end; + end + else + begin + Marker := nil; + repeat + Result := Result.Parent; + if Result <> FRoot then + Marker := GetPreviousVisibleSibling(Result, True) + else + Result := nil; + until Assigned(Marker) or (Result = nil); + if Assigned(Marker) then + Result := Marker; - if FUpdateCount = 0 then - begin - case Mode of - amInsertBefore, - amInsertAfter: - begin - // Here no initialization is necessary because *if* a node has already got children then it - // must also be initialized. - // Note: Node can never be FRoot at this point. - StructureChange(Result, crNodeAdded); - // If auto sort is enabled then sort the node or its parent (depending on the insert mode). - if (toAutoSort in FOptions.AutoOptions) and (FHeader.SortColumn > InvalidColumn) then - Sort(Node.Parent, FHeader.SortColumn, FHeader.SortDirection, True); - InvalidateToBottom(Result) - end; - amAddChildFirst, - amAddChildLast: + Break; + end; + until False; + end + else begin - StructureChange(Node, crChildAdded); - // If auto sort is enabled then sort the node or its parent (depending on the insert mode). - if (toAutoSort in FOptions.AutoOptions) and (FHeader.SortColumn > InvalidColumn) then - Sort(Node, FHeader.SortColumn, FHeader.SortDirection, True); - InvalidateToBottom(Node); + repeat + // Is there a previous sibling node? + if Assigned(Result.PrevSibling) then + begin + Result := Result.PrevSibling; + // Initialize the new node and check its visibility. + if not (vsInitialized in Result.States) then + InitNode(Result); + if vsVisible in Result.States then + begin + // If there are visible child nodes then use the last one. + Marker := GetLastVisible(Result, True, True); + if Assigned(Marker) then + Result := Marker; + Break; + end; + end + else + begin + // No previous sibling there so the parent node is the nearest previous node. + Result := Result.Parent; + if Result = FRoot then + Result := nil; + Break; + end; + until False; end; + + if Assigned(Result) and not (vsInitialized in Result.States) then + InitNode(Result); end; - InvalidateCache(); - UpdateScrollBars(True); - end; - end - else - Result := nil; + until not Assigned(Result) or IsEffectivelyVisible[Result]; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InvalidateChildren(Node: PVirtualNode; Recursive: Boolean); +function TBaseVirtualTree.GetPreviousVisibleNoInit(Node: PVirtualNode; + ConsiderChildrenAbove: Boolean = True): PVirtualNode; -// Invalidates Node and its immediate children. -// If Recursive is True then all grandchildren are invalidated as well. -// The node itself is initialized if necessary and its child nodes are created (and initialized too if -// Recursive is True). +// Returns the previous node in tree, with regard to Node, which is visible. +// toChildrenAbove is optionally considered which is the default here. var - Run: PVirtualNode; + Marker: PVirtualNode; begin - if Assigned(Node) then + Result := Node; + if Assigned(Result) then begin - if not (vsInitialized in Node.States) then - InitNode(Node); - InvalidateNode(Node); - if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then - InitChildren(Node); - Run := Node.FirstChild; - end - else - Run := FRoot.FirstChild; + Assert(Result <> FRoot, 'Node must not be the hidden root node.'); - while Assigned(Run) do - begin - InvalidateNode(Run); - if Recursive then - InvalidateChildren(Run, True); - Run := Run.NextSibling; + repeat + // If the given node is not visible then look for a parent node which is visible and use its last visible + // child or the parent node (if there is no visible child) as result. + if not FullyVisible[Result] then + begin + Result := GetVisibleParent(Result, True); + if Result = FRoot then + Result := nil; + Marker := GetLastVisibleNoInit(Result, True); + if Assigned(Marker) then + Result := Marker; + end + else + begin + if ConsiderChildrenAbove and (toChildrenAbove in FOptions.PaintOptions) then + begin + repeat + // Is the current node expanded and has children? + if (vsExpanded in Result.States) and Assigned(Result.LastChild) then + begin + Result := Result.LastChild; + if vsVisible in Result.States then + Break; + end + else + if Assigned(Result.PrevSibling) then + begin + // No children anymore, so take the previous sibling. + Result := Result.PrevSibling; + if vsVisible in Result.States then + Break; + end + else + begin + // No children and no previous siblings, so walk up the tree and look wether + // a parent has a previous visible sibling. If that is the case take it, + // otherwise there is no previous visible node. + Marker := nil; + repeat + Result := Result.Parent; + if Result <> FRoot then + Marker := GetPreviousVisibleSiblingNoInit(Result, True) + else + Result := nil; + until Assigned(Marker) or (Result = nil); + if Assigned(Marker) then + Result := Marker; + Break; + end; + until False; + end + else + begin + repeat + // Is there a previous sibling node? + if Assigned(Result.PrevSibling) then + begin + Result := Result.PrevSibling; + if vsVisible in Result.States then + begin + // If there are visible child nodes then use the last one. + Marker := GetLastVisibleNoInit(Result, True, True); + if Assigned(Marker) then + Result := Marker; + Break; + end; + end + else + begin + // No previous sibling there so the parent node is the nearest previous node. + Result := Result.Parent; + if Result = FRoot then + Result := nil; + Break; + end; + until False; + end; + end; + until not Assigned(Result) or IsEffectivelyVisible[Result]; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InvalidateColumn(Column: TColumnIndex); - -// Invalidates the client area part of a column. +function TBaseVirtualTree.GetPreviousVisibleSibling(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; -var - R: TRect; +// Returns the previous visible sibling before Node. Initialization is done implicitly. begin - if (FUpdateCount = 0) and HandleAllocated and FHeader.Columns.IsValidColumn(Column) then - begin - R := ClientRect; - FHeader.Columns.GetColumnBounds(Column, R.Left, R.Right); - InvalidateRect(Handle, @R, False); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -function TBaseVirtualTree.InvalidateNode(Node: PVirtualNode): TRect; - -// Initiates repaint of the given node and returns the just invalidated rectangle. + Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); -begin - Assert(Assigned(Node), 'Node must not be nil.'); - Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be chnaged in UI thread.'); - // Reset height measured flag too to cause a re-issue of the OnMeasureItem event. - Exclude(Node.States, vsHeightMeasured); - if (FUpdateCount = 0) and HandleAllocated then - begin - Result := GetDisplayRect(Node, NoColumn, False); - InvalidateRect(Handle, @Result, False); - end - else - result := Rect(-1,-1,-1,-1); + Result := Node; + repeat + Result := GetPreviousSibling(Result); + until not Assigned(Result) or ((vsVisible in Result.States) and + (IncludeFiltered or not IsEffectivelyFiltered[Result])); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InvalidateToBottom(Node: PVirtualNode); - -// Initiates repaint of client area starting at given node. If this node is not visible or not yet initialized -// then nothing happens. +function TBaseVirtualTree.GetPreviousVisibleSiblingNoInit(Node: PVirtualNode; + IncludeFiltered: Boolean = False): PVirtualNode; -var - R: TRect; +// Returns the previous visible sibling before Node. begin - if (FUpdateCount = 0) and HandleAllocated then - begin - if (Node = nil) or (Node = FRoot) then - Invalidate - else - if (vsInitialized in Node.States) and IsEffectivelyVisible[Node] then - begin - R := GetDisplayRect(Node, NoColumn, False); - if R.Top < ClientHeight then - begin - if (toChildrenAbove in FOptions.PaintOptions) and (vsExpanded in Node.States) then - Dec(R.Top, Node.TotalHeight + NodeHeight[Node]); - R.Bottom := ClientHeight; - InvalidateRect(Handle, @R, False); - end; - end; - end; + Assert(Assigned(Node) and (Node <> FRoot), 'Invalid parameter.'); + + Result := Node; + repeat + Result := Result.PrevSibling; + until not Assigned(Result) or ((vsVisible in Result.States) and + (IncludeFiltered or not IsEffectivelyFiltered[Result])); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.InvertSelection(VisibleOnly: Boolean); - -// Inverts the current selection (so nodes which are selected become unselected and vice versa). -// If VisibleOnly is True then only visible nodes are considered. - -var - Run: PVirtualNode; - NewSize: Integer; - NextFunction: TGetNextNodeProc; - TriggerChange: Boolean; +function TBaseVirtualTree.Nodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; -begin - if not FSelectionLocked and (toMultiSelect in FOptions.SelectionOptions) then - begin - Run := FRoot.FirstChild; - ClearTempCache; - if VisibleOnly then - NextFunction := GetNextVisibleNoInit - else - NextFunction := GetNextNoInit; - while Assigned(Run) do - begin - if vsSelected in Run.States then - InternalRemoveFromSelection(Run) - else - InternalCacheNode(Run); - Run := NextFunction(Run); - end; +// Enumeration for all nodes - // do some housekeeping - // Need to trigger the OnChange event from here if nodes were only deleted but not added. - TriggerChange := False; - NewSize := PackArray(FSelection, FSelectionCount); - if NewSize > -1 then - begin - FSelectionCount := NewSize; - SetLength(FSelection, FSelectionCount); - TriggerChange := True; - end; - if FTempNodeCount > 0 then - begin - AddToSelection(FTempNodeCache, FTempNodeCount); - ClearTempCache; - TriggerChange := False; - end; - Invalidate; - if TriggerChange then - Change(nil); - if Self.SelectedCount = 0 then - FNextNodeToSelect := nil;//Ensure that no other node is selected now - end; +begin + Result.FMode := vneAll; + Result.FTree := Self; + Result.FConsiderChildrenAbove := ConsiderChildrenAbove; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.IsEditing: Boolean; +function TBaseVirtualTree.CheckedNodes(State: TCheckState; ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; + +// Enumeration for all checked nodes begin - Result := tsEditing in FStates; + Result.FMode := vneChecked; + Result.FTree := Self; + Result.FState := State; + Result.FConsiderChildrenAbove := ConsiderChildrenAbove; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.IsMouseSelecting: Boolean; +function TBaseVirtualTree.ChildNodes(Node: PVirtualNode): TVTVirtualNodeEnumeration; -begin - Result := (tsDrawSelPending in FStates) or (tsDrawSelecting in FStates); -end; +// Enumeration for child nodes -function TBaseVirtualTree.IsUpdating: Boolean; -// The tree does currently not update its window because a BeginUpdate has not yet ended. begin - Exit(UpdateCount > 0); + Result.FMode := vneChild; + Result.FTree := Self; + Result.FNode := Node; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.IterateSubtree(Node: PVirtualNode; Callback: TVTGetNodeProc; Data: Pointer; - Filter: TVirtualNodeStates = []; DoInit: Boolean = False; ChildNodesOnly: Boolean = False): PVirtualNode; - -// Iterates through the all children and grandchildren etc. of Node (or the entire tree if Node = nil) -// and calls for each node the provided callback method (which must not be empty). -// Filter determines which nodes to consider (an empty set denotes all nodes). -// If DoInit is True then nodes which aren't initialized yet will be initialized. -// Note: During execution of the callback the application can set Abort to True. In this case the iteration is stopped -// and the last accessed node (the one on which the callback set Abort to True) is returned to the caller. -// Otherwise (no abort) nil is returned. +function TBaseVirtualTree.CutCopyNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; -var - Stop: PVirtualNode; - Abort: Boolean; - GetNextNode: TGetNextNodeProc; - WasIterating: Boolean; +// Enumeration for cut copy node begin - Assert(Node <> FRoot, 'Node must not be the hidden root node.'); + Result.FMode := vneCutCopy; + Result.FTree := Self; + Result.FConsiderChildrenAbove := ConsiderChildrenAbove; +end; - WasIterating := tsIterating in FStates; - DoStateChange([tsIterating]); - try - // prepare function to be used when advancing - if DoInit then - GetNextNode := GetNext - else - GetNextNode := GetNextNoInit; +//---------------------------------------------------------------------------------------------------------------------- - Abort := False; - if Node = nil then - Stop := nil - else - begin - if not (vsInitialized in Node.States) and DoInit then - InitNode(Node); +function TBaseVirtualTree.InitializedNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; - // The stopper does not need to be initialized since it is not taken into the enumeration. - Stop := Node.NextSibling; - if Stop = nil then - begin - Stop := Node; - repeat - Stop := Stop.Parent; - until (Stop = FRoot) or Assigned(Stop.NextSibling); - if Stop = FRoot then - Stop := nil - else - Stop := Stop.NextSibling; - end; - end; +// Enumeration for initialized nodes - // Use first node if we start with the root. - if Node = nil then - Node := GetFirstNoInit; +begin + Result.FMode := vneInitialized; + Result.FTree := Self; + Result.FConsiderChildrenAbove := ConsiderChildrenAbove; +end; - if Assigned(Node) then - begin - if not (vsInitialized in Node.States) and DoInit then - InitNode(Node); +//---------------------------------------------------------------------------------------------------------------------- - // Skip given node if only the child nodes are requested. - if ChildNodesOnly then - begin - if Node.ChildCount = 0 then - Node := nil - else - Node := GetNextNode(Node); - end; +function TBaseVirtualTree.LeafNodes: TVTVirtualNodeEnumeration; - if Filter = [] then - begin - // unfiltered loop - while Assigned(Node) and (Node <> Stop) do - begin - Callback(Self, Node, Data, Abort); - if Abort then - Break; - Node := GetNextNode(Node); - end; - end - else - begin - // filtered loop - while Assigned(Node) and (Node <> Stop) do - begin - if Node.States * Filter = Filter then - Callback(Self, Node, Data, Abort); - if Abort then - Break; - Node := GetNextNode(Node); - end; - end; - end; +// Enumeration for leaf nodes - if Abort then - Result := Node - else - Result := nil; - finally - if not WasIterating then - DoStateChange([], [tsIterating]); - end; +begin + Result.FMode := vneLeaf; + Result.FTree := Self; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.LoadFromFile(const FileName: TFileName); +function TBaseVirtualTree.LevelNodes(NodeLevel: Cardinal): TVTVirtualNodeEnumeration; -var - FileStream: TFileStream; +// Enumeration for level nodes begin - FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); - try - LoadFromStream(FileStream); - finally - FileStream.Free; - end; + Result.FMode := vneLevel; + Result.FTree := Self; + Result.FNodeLevel := NodeLevel; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.LoadFromStream(Stream: TStream); +function TBaseVirtualTree.NoInitNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; -// Clears the current content of the tree and loads a new structure from the given stream. +// Enumeration for no init nodes +begin + Result.FMode := vneNoInit; + Result.FTree := Self; + Result.FConsiderChildrenAbove := ConsiderChildrenAbove; +end; -var - ThisID: TMagicID; - Version, - Count: Cardinal; - Node: PVirtualNode; +//---------------------------------------------------------------------------------------------------------------------- -begin - if not (toReadOnly in FOptions.MiscOptions) then - begin - Clear; - // Check first whether this is a stream we can read. - if Stream.Read(ThisID, SizeOf(TMagicID)) < SizeOf(TMagicID) then - ShowError(SStreamTooSmall, hcTFStreamTooSmall); +function TBaseVirtualTree.SelectedNodes(ConsiderChildrenAbove: Boolean): TVTVirtualNodeEnumeration; - if (ThisID[0] = MagicID[0]) and - (ThisID[1] = MagicID[1]) and - (ThisID[2] = MagicID[2]) and - (ThisID[5] = MagicID[5]) then - begin - Version := Word(ThisID[3]); - if Version <= VTTreeStreamVersion then - begin - BeginUpdate; - try - if Version < 2 then - Count := MaxInt - else - Stream.ReadBuffer(Count, SizeOf(Count)); +// Enumeration for selected nodes - while (Stream.Position < Stream.Size) and (Count > 0) do - begin - Dec(Count); - Node := MakeNewNode; - InternalConnectNode(Node, FRoot, Self, amAddChildLast); - InternalAddFromStream(Stream, Version, Node); - end; - DoNodeCopied(nil); - if Assigned(FOnLoadTree) then - FOnLoadTree(Self, Stream); - finally - EndUpdate; - end; - end - else - ShowError(SWrongStreamVersion, hcTFWrongStreamVersion); - end - else - ShowError(SWrongStreamFormat, hcTFWrongStreamFormat); - end; +begin + Result.FMode := vneSelected; + Result.FTree := Self; + Result.FConsiderChildrenAbove := ConsiderChildrenAbove; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.MeasureItemHeight(const Canvas: TCanvas; Node: PVirtualNode); +function TBaseVirtualTree.VisibleNodes(Node: PVirtualNode; ConsiderChildrenAbove: Boolean; + IncludeFiltered: Boolean): TVTVirtualNodeEnumeration; -// If the height of the given node has not yet been measured then do it now. +// Enumeration for visible nodes -var - NewNodeHeight: Integer; +begin + Result.FMode := vneVisible; + Result.FTree := Self; + Result.FNode := Node; + Result.FConsiderChildrenAbove := ConsiderChildrenAbove; + Result.FIncludeFiltered := IncludeFiltered; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.VisibleChildNodes(Node: PVirtualNode; IncludeFiltered: Boolean): TVTVirtualNodeEnumeration; + +// Enumeration for visible child nodes begin - if not (vsHeightMeasured in Node.States) then - begin - Include(Node.States, vsHeightMeasured); - if (toVariableNodeHeight in FOptions.MiscOptions) then - begin - NewNodeHeight := Node.NodeHeight; - // Anonymous methods help to make this thread safe easily. - if (MainThreadId <> GetCurrentThreadId) then - TThread.Synchronize(nil, - procedure - begin - DoMeasureItem(Canvas, Node, NewNodeHeight); - SetNodeHeight(Node, NewNodeHeight); - end - ) - else - begin - DoMeasureItem(Canvas, Node, NewNodeHeight); - SetNodeHeight(Node, NewNodeHeight); - end; - end; - end; + Result.FMode := vneVisibleChild; + Result.FTree := Self; + Result.FNode := Node; + Result.FIncludeFiltered := IncludeFiltered; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.MoveTo(Node: PVirtualNode; Tree: TBaseVirtualTree; Mode: TVTNodeAttachMode; - ChildrenOnly: Boolean); +function TBaseVirtualTree.VisibleChildNoInitNodes(Node: PVirtualNode; IncludeFiltered: Boolean): TVTVirtualNodeEnumeration; -// A simplified method to allow to move nodes to the root of another tree. +// Enumeration for visible child no init nodes begin - MoveTo(Node, Tree.FRoot, Mode, ChildrenOnly); + Result.FMode := vneVisibleNoInitChild; + Result.FTree := Self; + Result.FNode := Node; + Result.FIncludeFiltered := IncludeFiltered; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.MoveTo(Source, Target: PVirtualNode; Mode: TVTNodeAttachMode; ChildrenOnly: Boolean); +function TBaseVirtualTree.VisibleNoInitNodes(Node: PVirtualNode; ConsiderChildrenAbove: Boolean; + IncludeFiltered: Boolean): TVTVirtualNodeEnumeration; -// Moves the given node (and all its children) to Target. Source must belong to the tree instance which calls this -// MoveTo method. Mode determines how to connect Source to Target. -// This method might involve a change of the tree if Target belongs to a different tree than Source. +// Enumeration for visible no init nodes + +begin + Result.FMode := vneVisibleNoInit; + Result.FTree := Self; + Result.FNode := Node; + Result.FConsiderChildrenAbove := ConsiderChildrenAbove; + Result.FIncludeFiltered := IncludeFiltered; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.GetSortedCutCopySet(Resolve: Boolean): TNodeArray; + +// Same as GetSortedSelection but with nodes marked as being part in the current cut/copy set (e.g. for clipboard). var - TargetTree: TBaseVirtualTree; - Allowed: Boolean; - NewNode: PVirtualNode; - Stream: TMemoryStream; + Run: PVirtualNode; + Counter: Cardinal; -begin - Assert(TreeFromNode(Source) = Self, 'The source tree must contain the source node.'); + //--------------- local function -------------------------------------------- - // When moving nodes then source and target must not be the same node unless only the source's children are - // moved and they are inserted before or after the node itself. - Allowed := (Source <> Target) or ((Mode in [amInsertBefore, amInsertAfter]) and ChildrenOnly); + procedure IncludeThisNode(Node: PVirtualNode); - if Allowed and (Mode <> amNoWhere) and Assigned(Source) and (Source <> FRoot) and - not (toReadOnly in FOptions.MiscOptions) then - begin - // Assume that an empty destination means the root in this (the source) tree. - if Target = nil then - begin - TargetTree := Self; - Target := FRoot; - Mode := amAddChildFirst; - end - else - TargetTree := TreeFromNode(Target); + // adds the given node to the result - if Target = TargetTree.FRoot then + var + Len: Cardinal; + + begin + Len := Length(Result); + if Counter = Len then begin - case Mode of - amInsertBefore: - Mode := amAddChildFirst; - amInsertAfter: - Mode := amAddChildLast; - end; + if Len < 100 then + Len := 100 + else + Len := Len + Len div 10; + SetLength(Result, Len); end; + Result[Counter] := Node; + Inc(Counter); + end; - // Make sure the target node is initialized. - if not (vsInitialized in Target.States) then - TargetTree.InitNode(Target) - else - if (vsHasChildren in Target.States) and (Target.ChildCount = 0) then - TargetTree.InitChildren(Target); + //--------------- end local function ---------------------------------------- - if TargetTree = Self then +begin + Run := FRoot.FirstChild; + Counter := 0; + if Resolve then + begin + // Resolving is actually easy: just find the first cutted node in logical order + // and then never go deeper in level than this node as long as there's a sibling node. + // Restart the search for a cutted node (at any level) if there are no further siblings. + while Assigned(Run) do begin - // Simple case: move node(s) within the same tree. - if Target = FRoot then - Allowed := DoNodeMoving(Source, nil) - else - Allowed := DoNodeMoving(Source, Target); - if Allowed then + if vsCutOrCopy in Run.States then begin - // Check first that Source is not added as new child to a target node which - // is already a child of Source. - // Consider the case Source and Target are the same node, but only child nodes are moved. - if (Source <> Target) and HasAsParent(Target, Source) then - ShowError(SWrongMoveError, hcTFWrongMoveError); - - if not ChildrenOnly then - begin - // Disconnect from old location. - InternalDisconnectNode(Source, True); - // Connect to new location. - InternalConnectNode(Source, Target, Self, Mode); - DoNodeMoved(Source); - end + IncludeThisNode(Run); + if Assigned(Run.NextSibling) then + Run := Run.NextSibling else begin - // Only child nodes should be moved. Insertion order depends on move mode. - if Mode = amAddChildFirst then - begin - Source := Source.LastChild; - while Assigned(Source) do - begin - NewNode := Source.PrevSibling; - // Disconnect from old location. - InternalDisconnectNode(Source, True, False); - // Connect to new location. - InternalConnectNode(Source, Target, Self, Mode); - DoNodeMoved(Source); - Source := NewNode; - end; - end + // If there are no further siblings then go up one or more levels until a node is + // found or all nodes have been processed. Although we consider here only initialized + // nodes we don't need to make any special checks as only initialized nodes can also be selected. + repeat + Run := Run.Parent; + until (Run = FRoot) or Assigned(Run.NextSibling); + if Run = FRoot then + Break else - begin - Source := Source.FirstChild; - while Assigned(Source) do - begin - NewNode := Source.NextSibling; - // Disconnect from old location. - InternalDisconnectNode(Source, True, False); - // Connect to new location. - InternalConnectNode(Source, Target, Self, Mode); - DoNodeMoved(Source); - Source := NewNode; - end; - end; + Run := Run.NextSibling; end; - end; - end - else - begin - // Difficult case: move node(s) to another tree. - // In opposition to node copying we ask only once if moving is allowed because - // we cannot take back a move once done. - if Target = TargetTree.FRoot then - Allowed := DoNodeMoving(Source, nil) + end else - Allowed := DoNodeMoving(Source, Target); - - if Allowed then - begin - Stream := TMemoryStream.Create; - try - // Write all nodes into a temporary stream depending on the ChildrenOnly flag. - if not ChildrenOnly then - WriteNode(Stream, Source) - else - begin - Source := Source.FirstChild; - while Assigned(Source) do - begin - WriteNode(Stream, Source); - Source := Source.NextSibling; - end; - end; - // Now load the serialized nodes into the target node (tree). - TargetTree.BeginUpdate; - try - Stream.Position := 0; - while Stream.Position < Stream.Size do - begin - NewNode := TargetTree.MakeNewNode; - InternalConnectNode(NewNode, Target, TargetTree, Mode); - TargetTree.InternalAddFromStream(Stream, VTTreeStreamVersion, NewNode); - DoNodeMoved(NewNode); - end; - finally - TargetTree.EndUpdate; - end; - finally - Stream.Free; - end; - // finally delete original nodes - BeginUpdate; - try - if ChildrenOnly then - DeleteChildren(Source) - else - DeleteNode(Source); - finally - EndUpdate; - end; - end; - end; - - InvalidateCache; - if (FUpdateCount = 0) and Allowed then - begin - ValidateCache; - UpdateScrollBars(True); - Invalidate; - if TargetTree <> Self then - TargetTree.Invalidate; + Run := GetNextNoInit(Run); + end; + end + else + while Assigned(Run) do + begin + if vsCutOrCopy in Run.States then + IncludeThisNode(Run); + Run := GetNextNoInit(Run); end; - StructureChange(Source, crNodeMoved); - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Target: TPoint; - PaintOptions: TVTInternalPaintOptions; PixelFormat: TPixelFormat); - -// This is the core paint routine of the tree. It is responsible for maintaining the paint cycles per node as well -// as coordinating drawing of the various parts of the tree image. -// TargetCanvas is the canvas to which to draw the tree image. This is usually the tree window itself but could well -// be a bitmap or printer canvas. -// Window determines which part of the entire tree image to draw. The full size of the virtual image is determined -// by GetTreeRect. -// Target is the position in TargetCanvas where to draw the tree part specified by Window. -// PaintOptions determines what of the tree to draw. For different tasks usually different parts need to be drawn, with -// a full image in the window, selected only nodes for a drag image etc. - -const - ImageKind: array[Boolean] of TVTImageKind = (ikNormal, ikSelected); - -var - DrawSelectionRect, - UseBackground, - ShowCheckImages, - UseColumns, - IsMainColumn: Boolean; - IndentSize, - ButtonY: Integer; // Y position of toggle button within the node's rect - LineImage: TLineImage; - PaintInfo: TVTPaintInfo; // all necessary information about a node to pass to the paint routines + // set the resulting array to its real length + SetLength(Result, Counter); +end; - R, // the area of an entire node in its local coordinate - TargetRect, // the area of a node (part) in the target canvas - SelectionRect, // ordered rectangle used for drawing the selection focus rect - ClipRect: TRect; // area to which the canvas will be clipped when painting a node's content - NextColumn: TColumnIndex; - BaseOffset: Integer; // top position of the top node to draw given in absolute tree coordinates - NodeBitmap: TBitmap; // small buffer to draw flicker free - MaximumRight, // maximum horizontal target position - MaximumBottom: Integer; // maximum vertical target position - SelectLevel: Integer; // > 0 if current node is selected or child/grandchild etc. of a selected node - FirstColumn: TColumnIndex; // index of first column which is at least partially visible in the given window +//---------------------------------------------------------------------------------------------------------------------- - MaxRight, - ColLeft, - ColRight: Integer; +function TBaseVirtualTree.GetSortedSelection(Resolve: Boolean): TNodeArray; - SavedTargetDC: Integer; - PaintWidth: Integer; - CurrentNodeHeight: Integer; - lUseSelectedBkColor: Boolean; // determines if the dotted grid lines need to be painted in selection color of background color +// Returns a list of selected nodes sorted in logical order, that is, as they appear in the tree. +// If Resolve is True then nodes which are children of other selected nodes are not put into the new array. +// This feature is in particuar important when doing drag'n drop as in this case all selected node plus their children +// need to be considered. A selected node which is child (grand child etc.) of another selected node is then +// automatically included and doesn't need to be explicitely mentioned in the returned selection array. +// +// Note: The caller is responsible for freeing the array. Allocation is done here. Usually, though, freeing the array +// doesn't need additional attention as it is automatically freed by Delphi when it gets out of scope. - CellIsTouchingClientRight: Boolean; - CellIsInLastColumn: Boolean; - ColumnIsFixed: Boolean; +var + Run: PVirtualNode; + Counter: Cardinal; begin - if not (tsPainting in FStates) then + SetLength(Result, FSelectionCount); + if FSelectionCount > 0 then begin - DoStateChange([tsPainting]); - try - DoBeforePaint(TargetCanvas); - - if poUnbuffered in PaintOptions then - SavedTargetDC := SaveDC(TargetCanvas.Handle) - else - SavedTargetDC := 0; - - // Prepare paint info structure. - ZeroMemory(@PaintInfo, SizeOf(PaintInfo)); - - PaintWidth := Window.Right - Window.Left; - - if not (poUnbuffered in PaintOptions) then + Run := FRoot.FirstChild; + Counter := 0; + if Resolve then + begin + // Resolving is actually easy: just find the first selected node in logical order + // and then never go deeper in level than this node as long as there's a sibling node. + // Restart the search for a selected node (at any level) if there are no further siblings. + while Assigned(Run) do begin - // Create small bitmaps and initialize default values. - // The bitmaps are used to paint one node at a time and to draw the result to the target (e.g. screen) in one step, - // to prevent flickering. - NodeBitmap := TBitmap.Create; - // For alpha blending we need the 32 bit pixel format. For other targets there might be a need for a certain - // pixel format (e.g. printing). - if ((FDrawSelectionMode = smBlendedRectangle) or (tsUseThemes in FStates) or - (toUseBlendedSelection in FOptions.PaintOptions)) then - NodeBitmap.PixelFormat := pf32Bit + if vsSelected in Run.States then + begin + Result[Counter] := Run; + Inc(Counter); + if Assigned(Run.NextSibling) then + Run := Run.NextSibling + else + begin + // If there are no further siblings then go up one or more levels until a node is + // found or all nodes have been processed. Although we consider here only initialized + // nodes we don't need to make any special checks as only initialized nodes can also be selected. + repeat + Run := Run.Parent; + until (Run = FRoot) or Assigned(Run.NextSibling); + if Run = FRoot then + Break + else + Run := Run.NextSibling; + end; + end else - NodeBitmap.PixelFormat := PixelFormat; - - NodeBitmap.Width := PaintWidth; - - // Make sure the buffer bitmap and target bitmap use the same transformation mode. - SetMapMode(NodeBitmap.Canvas.Handle, GetMapMode(TargetCanvas.Handle)); - PaintInfo.Canvas := NodeBitmap.Canvas; - end - else + Run := GetNextNoInit(Run); + end; + end + else + while Assigned(Run) do begin - PaintInfo.Canvas := TargetCanvas; - NodeBitmap := nil; + if vsSelected in Run.States then + begin + Result[Counter] := Run; + Inc(Counter); + end; + Run := GetNextNoInit(Run); end; - // Lock the canvas to avoid that it gets freed on the way. - PaintInfo.Canvas.Lock; - try - // Prepare the current selection rectangle once. The corner points are absolute tree coordinates. - SelectionRect := OrderRect(FNewSelRect); - DrawSelectionRect := IsMouseSelecting and not IsRectEmpty(SelectionRect) and (GetKeyState(VK_LBUTTON) < 0); + // Since we may have skipped some nodes the result array is likely to be smaller than the + // selection array, hence shorten the result to true length. + if Integer(Counter) < Length(Result) then + SetLength(Result, Counter); + end; +end; - // R represents an entire node (all columns), but is a bit unprecise when it comes to - // trees without any column defined, because FRangeX only represents the maximum width of all - // nodes in the client area (not all defined nodes). There might be, however, wider nodes somewhere. Without full - // validation I cannot better determine the width, though. By using at least the control's width it is ensured - // that the tree is fully displayed on screen. - R := Rect(0, 0, Max(FRangeX, ClientWidth), 0); +//---------------------------------------------------------------------------------------------------------------------- - // For quick checks some intermediate variables are used. - UseBackground := (toShowBackground in FOptions.PaintOptions) and Assigned(FBackground.Graphic) and - (poBackground in PaintOptions); - ShowCheckImages := Assigned(FCheckImages) and (toCheckSupport in FOptions.MiscOptions); - UseColumns := FHeader.UseColumns; +procedure TBaseVirtualTree.GetTextInfo(Node: PVirtualNode; Column: TColumnIndex; const AFont: TFont; var R: TRect; + var Text: string); - // Adjust paint options to tree settings. Hide selection if told so or the tree is unfocused. - if (toAlwaysHideSelection in FOptions.PaintOptions) or - (not Focused and (toHideSelection in FOptions.PaintOptions)) then - Exclude(PaintOptions, poDrawSelection); - if toHideFocusRect in FOptions.PaintOptions then - Exclude(PaintOptions, poDrawFocusRect); +// Generic base method for editors, hint windows etc. to get some info about a node. - // Determine node to start drawing with. - BaseOffset := 0; - PaintInfo.Node := GetNodeAt(0, Window.Top, False, BaseOffset); - if PaintInfo.Node = nil then - BaseOffset := Window.Top; +begin + R := Rect(0, 0, 0, 0); + Text := ''; + if Assigned(Font) then // 1 EConvertError due to Font being nil seen here in 01/2019, See issue #878 + AFont.Assign(Font); +end; - // Transform selection rectangle into node bitmap coordinates. - if DrawSelectionRect then - OffsetRect(SelectionRect, 0, -BaseOffset); +//---------------------------------------------------------------------------------------------------------------------- - // The target rectangle holds the coordinates of the exact area to blit in target canvas coordinates. - // It is usually smaller than an entire node and wanders while the paint loop advances. - MaximumRight := Target.X + (Window.Right - Window.Left); - MaximumBottom := Target.Y + (Window.Bottom - Window.Top); +function TBaseVirtualTree.GetTreeRect: TRect; - TargetRect := Rect(Target.X, Target.Y - (Window.Top - BaseOffset), MaximumRight, 0); - TargetRect.Bottom := TargetRect.Top; - TargetCanvas.Font := Self.Font; +// Returns the true size of the tree in pixels. This size is at least ClientHeight x ClientWidth and depends on +// the expand state, header size etc. +// Note: if no columns are used then the width of the tree is determined by the largest node which is currently in the +// client area. This might however not be the largest node in the entire tree. - // This marker gets the index of the first column which is visible in the given window. - // This is needed for column based background colors. - FirstColumn := InvalidColumn; +begin + Result := Rect(0, 0, Max(FRangeX, ClientWidth), Max(FRangeY, ClientHeight)); +end; - if Assigned(PaintInfo.Node) then - begin +//---------------------------------------------------------------------------------------------------------------------- - // ----- main node paint loop - while Assigned(PaintInfo.Node) do - begin - // Determine LineImage, SelectionLevel and IndentSize - SelectLevel := DetermineLineImageAndSelectLevel(PaintInfo.Node, LineImage); - IndentSize := Length(LineImage); +function TBaseVirtualTree.GetVisibleParent(Node: PVirtualNode; IncludeFiltered: Boolean = False): PVirtualNode; - // Initialize node if not already done. - if not (vsInitialized in PaintInfo.Node.States) then - InitNode(PaintInfo.Node); - if (vsSelected in PaintInfo.Node.States) and not (toChildrenAbove in FOptions.PaintOptions) then - Inc(SelectLevel); +// Returns the first (nearest) parent node of Node which is visible. +// This method is one of the seldom cases where the hidden root node could be returned. - // Ensure the node's height is determined. - MeasureItemHeight(PaintInfo.Canvas, PaintInfo.Node); +begin + Assert(Assigned(Node), 'Node must not be nil.'); + Assert(Node <> FRoot, 'Node must not be the hidden root node.'); - // Adjust the brush origin for dotted lines depending on the current source position. - // It is applied some lines later, as the canvas might get reallocated, when changing the node bitmap. - PaintInfo.BrushOrigin := Point(Window.Left and 1, BaseOffset and 1); - Inc(BaseOffset, PaintInfo.Node.NodeHeight); + Result := Node.Parent; + while (Result <> FRoot) and (not FullyVisible[Result] or (not IncludeFiltered and IsEffectivelyFiltered[Result])) do + Result := Result.Parent; +end; - TargetRect.Bottom := TargetRect.Top + PaintInfo.Node.NodeHeight; +//---------------------------------------------------------------------------------------------------------------------- - // If poSelectedOnly is active then do the following stuff only for selected nodes or nodes - // which are children of selected nodes. - if (SelectLevel > 0) or not (poSelectedOnly in PaintOptions) then - begin - if not (poUnbuffered in PaintOptions) then - begin - // Adjust height of temporary node bitmap. - with NodeBitmap do - begin - if Height <> PaintInfo.Node.NodeHeight then - begin - // Avoid that the VCL copies the bitmap while changing its height. - Height := 0; - Height := PaintInfo.Node.NodeHeight; - SetCanvasOrigin(Canvas, Window.Left, 0); - end; - end; - end - else - begin - SetCanvasOrigin(PaintInfo.Canvas, -TargetRect.Left + Window.Left, -TargetRect.Top); - ClipCanvas(PaintInfo.Canvas, Rect(0, 0, TargetRect.Right - TargetRect.Left, - Min(TargetRect.Bottom - TargetRect.Top, MaximumBottom - TargetRect.Top))); // See issue #579 - end; +function TBaseVirtualTree.HasAsParent(Node, PotentialParent: PVirtualNode): Boolean; + +// Determines whether Node has got PotentialParent as one of its parents. + +var + Run: PVirtualNode; + +begin + Result := Assigned(Node) and Assigned(PotentialParent) and (Node <> PotentialParent); + if Result then + begin + Run := Node; + while (Run <> FRoot) and (Run <> PotentialParent) do + Run := Run.Parent; + Result := Run = PotentialParent; + end; +end; - // Set the origin of the canvas' brush. This depends on the node heights. - with PaintInfo do - SetBrushOrigin(Canvas, BrushOrigin.X, BrushOrigin.Y); +//---------------------------------------------------------------------------------------------------------------------- - CurrentNodeHeight := PaintInfo.Node.NodeHeight; - R.Bottom := CurrentNodeHeight; +function TBaseVirtualTree.InsertNode(Node: PVirtualNode; Mode: TVTNodeAttachMode; UserData: Pointer = nil): PVirtualNode; - // Let application decide whether the node should normally be drawn or by the application itself. - if not DoBeforeItemPaint(PaintInfo.Canvas, PaintInfo.Node, R) then - begin - // Init paint options for the background painting. - PaintInfo.PaintOptions := PaintOptions; +// Adds a new node relative to Node. The final position is determined by Mode. +// UserData can be used to set the first SizeOf(Pointer) bytes of the user data area to an initial value which can be used +// in OnInitNode and will also cause to trigger the OnFreeNode event (if <> nil) even if the node is not yet +// "officially" initialized. +// InsertNode is a compatibility method and will implicitly validate the given node if the new node +// is to be added as child node. This is however against the virtual paradigm and hence I dissuade from its usage. - // The node background can contain a single color, a bitmap or can be drawn by the application. - ClearNodeBackground(PaintInfo, UseBackground, True, Rect(Window.Left, TargetRect.Top, Window.Right, - TargetRect.Bottom)); +begin + if Mode <> amNoWhere then + begin + CancelEditNode; - // Prepare column, position and node clipping rectangle. - PaintInfo.CellRect := R; - if UseColumns then - InitializeFirstColumnValues(PaintInfo); + if Node = nil then + Node := FRoot; + // we need a new node... + Result := MakeNewNode; + // avoid erronous attach modes + if Node = FRoot then + begin + case Mode of + amInsertBefore: + Mode := amAddChildFirst; + amInsertAfter: + Mode := amAddChildLast; + end; + end; - // Now go through all visible columns (there's still one run if columns aren't used). - with FHeader.Columns do - begin - while ((PaintInfo.Column > InvalidColumn) or not UseColumns) - and (PaintInfo.CellRect.Left < Window.Right) do - begin - if UseColumns then - begin - PaintInfo.Column := FPositionToIndex[PaintInfo.Position]; - if FirstColumn = InvalidColumn then - FirstColumn := PaintInfo.Column; - PaintInfo.BidiMode := Items[PaintInfo.Column].BiDiMode; - PaintInfo.Alignment := Items[PaintInfo.Column].Alignment; - end - else - begin - PaintInfo.Column := NoColumn; - PaintInfo.BidiMode := BidiMode; - PaintInfo.Alignment := FAlignment; - end; - GetOffSets(PaintInfo.Node, PaintInfo.Offsets, TVTElement.ofsText, PaintInfo.Column); + // Validate given node in case the new node becomes its child. + if (Mode in [amAddChildFirst, amAddChildLast]) and not (vsInitialized in Node.States) then + InitNode(Node); + InternalConnectNode(Result, Node, Self, Mode); - PaintInfo.PaintOptions := PaintOptions; - with PaintInfo do - begin - if (tsEditing in FStates) and (Node = FFocusedNode) and - ((Column = FEditColumn) or not UseColumns) then - Exclude(PaintOptions, poDrawSelection); - if not UseColumns or - ((vsSelected in Node.States) and (toFullRowSelect in FOptions.SelectionOptions) and - (poDrawSelection in PaintOptions)) or - (coParentColor in Items[PaintInfo.Column].Options) or - ((coStyleColor in Items[PaintInfo.Column].Options) and VclStyleEnabled) - then - Exclude(PaintOptions, poColumnColor); - end; - IsMainColumn := PaintInfo.Column = FHeader.MainColumn; + // Check if there is initial user data and there is also enough user data space allocated. + if Assigned(UserData) then + SetNodeData(Result, UserData); - // Consider bidi mode here. In RTL context means left alignment actually right alignment and vice versa. - if PaintInfo.BidiMode <> bdLeftToRight then - ChangeBiDiModeAlignment(PaintInfo.Alignment); + if FUpdateCount = 0 then + begin + case Mode of + amInsertBefore, + amInsertAfter: + begin + // Here no initialization is necessary because *if* a node has already got children then it + // must also be initialized. + // Note: Node can never be FRoot at this point. + StructureChange(Result, crNodeAdded); + // If auto sort is enabled then sort the node or its parent (depending on the insert mode). + if (toAutoSort in FOptions.AutoOptions) and (FHeader.SortColumn > InvalidColumn) then + Sort(Node.Parent, FHeader.SortColumn, FHeader.SortDirection, True); + InvalidateToBottom(Result) + end; + amAddChildFirst, + amAddChildLast: + begin + StructureChange(Node, crChildAdded); + // If auto sort is enabled then sort the node or its parent (depending on the insert mode). + if (toAutoSort in FOptions.AutoOptions) and (FHeader.SortColumn > InvalidColumn) then + Sort(Node, FHeader.SortColumn, FHeader.SortDirection, True); + InvalidateToBottom(Node); + end; + end; + InvalidateCache(); + UpdateScrollBars(True); + end; + end + else + Result := nil; +end; - // Paint the current cell if it is marked as being visible or columns aren't used and - // if this cell belongs to the main column if only the main column should be drawn. - if (not UseColumns or (coVisible in Items[PaintInfo.Column].Options)) and - (not (poMainOnly in PaintOptions) or IsMainColumn) then - begin - AdjustPaintCellRect(PaintInfo, NextColumn); +//---------------------------------------------------------------------------------------------------------------------- - // Paint the cell only if it is in the current window. - if PaintInfo.CellRect.Right > Window.Left then - begin - with PaintInfo do - begin - // Fill in remaining values in the paint info structure. - NodeWidth := DoGetNodeWidth(Node, Column, Canvas); +procedure TBaseVirtualTree.InvalidateChildren(Node: PVirtualNode; Recursive: Boolean); - if ShowCheckImages and IsMainColumn then - begin - ImageInfo[iiCheck].Index := GetCheckImage(Node); - ImageInfo[iiCheck].Images := FCheckImages; - ImageInfo[iiCheck].Ghosted := False; - end - else - ImageInfo[iiCheck].Index := -1; - GetImageIndex(PaintInfo, ikState, iiState); - GetImageIndex(PaintInfo, ImageKind[vsSelected in Node.States], iiNormal); +// Invalidates Node and its immediate children. +// If Recursive is True then all grandchildren are invalidated as well. +// The node itself is initialized if necessary and its child nodes are created (and initialized too if +// Recursive is True). - CalculateVerticalAlignments(PaintInfo, ButtonY); - // Take the space for the tree lines into account. - PaintInfo.AdjustImageCoordinates(); - if UseColumns then - begin - ClipRect := CellRect; - if poUnbuffered in PaintOptions then - begin - ClipRect.Left := Max(ClipRect.Left, Window.Left); - ClipRect.Right := Min(ClipRect.Right, Window.Right); - ClipRect.Top := Max(ClipRect.Top, Window.Top - (BaseOffset - CurrentNodeHeight)); - ClipRect.Bottom := ClipRect.Bottom - Max(TargetRect.Bottom - MaximumBottom, 0); - end; - ClipCanvas(Canvas, ClipRect); - end; +var + Run: PVirtualNode; - // Paint the horizontal grid line. - if (poGridLines in PaintOptions) and (toShowHorzGridLines in FOptions.PaintOptions) then - begin - Canvas.Font.Color := FColors.GridLineColor; - if IsMainColumn and (FLineMode = lmBands) then - begin - if BidiMode = bdLeftToRight then - begin - DrawDottedHLine(PaintInfo, CellRect.Left + IfThen(toFixedIndent in FOptions.PaintOptions, 1, IndentSize) * Integer(FIndent), CellRect.Right - 1, - CellRect.Bottom - 1); - end - else - begin - DrawDottedHLine(PaintInfo, CellRect.Left, CellRect.Right - IfThen(toFixedIndent in FOptions.PaintOptions, 1, IndentSize) * Integer(FIndent) - 1, - CellRect.Bottom - 1); - end; - end - else - DrawDottedHLine(PaintInfo, CellRect.Left, CellRect.Right, CellRect.Bottom - 1); +begin + if Assigned(Node) then + begin + if not (vsInitialized in Node.States) then + InitNode(Node); + InvalidateNode(Node); + if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then + InitChildren(Node); + Run := Node.FirstChild; + end + else + Run := FRoot.FirstChild; - Dec(CellRect.Bottom); - Dec(ContentRect.Bottom); - end; + while Assigned(Run) do + begin + InvalidateNode(Run); + if Recursive then + InvalidateChildren(Run, True); + Run := Run.NextSibling; + end; +end; - if UseColumns then - begin - // Paint vertical grid line. - if (poGridLines in PaintOptions) and (toShowVertGridLines in FOptions.PaintOptions) then - begin - // These variables and the nested if conditions shall make the logic - // easier to understand. - CellIsTouchingClientRight := PaintInfo.CellRect.Right = ClientRect.Right; - CellIsInLastColumn := Position = TColumnPosition(Count - 1); - ColumnIsFixed := coFixed in FHeader.Columns[Column].Options; +//---------------------------------------------------------------------------------------------------------------------- - // Don't draw if this is the last column and the header is in autosize mode. - if not ((hoAutoResize in FHeader.Options) and CellIsInLastColumn) then - begin - // We have to take spanned cells into account which we determine - // by checking if CellRect.Right equals the Window.Right. - // But since the PaintTree procedure is called twice in - // TBaseVirtualTree.Paint (i.e. for fixed columns and other columns. - // CellIsTouchingClientRight does not work for fixed columns.) - // we have to paint fixed column grid line anyway. - if not CellIsTouchingClientRight or ColumnIsFixed then - begin - if (BidiMode = bdLeftToRight) or not ColumnIsEmpty(Node, Column) then - begin - Canvas.Font.Color := FColors.GridLineColor; - lUseSelectedBkColor := (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.SelectionOptions) and - (vsSelected in Node.States) and not (toUseBlendedSelection in FOptions.PaintOptions) and not - (tsUseExplorerTheme in FStates); - DrawDottedVLine(PaintInfo, CellRect.Top, CellRect.Bottom, CellRect.Right - 1, lUseSelectedBkColor); - end; +procedure TBaseVirtualTree.InvalidateColumn(Column: TColumnIndex); + +// Invalidates the client area part of a column. + +var + R: TRect; + +begin + if (FUpdateCount = 0) and HandleAllocated and FHeader.Columns.IsValidColumn(Column) then + begin + R := ClientRect; + FHeader.Columns.GetColumnBounds(Column, R.Left, R.Right); + InvalidateRect(Handle, @R, False); + end; +end; - Dec(CellRect.Right); - Dec(ContentRect.Right); - end; - end; - end; - end; +//---------------------------------------------------------------------------------------------------------------------- - // Prepare background and focus rect for the current cell. - PrepareCell(PaintInfo, Window.Left, PaintWidth); +function TBaseVirtualTree.InvalidateNode(Node: PVirtualNode): TRect; - // Some parts are only drawn for the main column. - if IsMainColumn then - begin - if (toShowTreeLines in FOptions.PaintOptions) and - (not (toHideTreeLinesIfThemed in FOptions.PaintOptions) or - not (tsUseThemes in FStates)) then - PaintTreeLines(PaintInfo, IfThen(toFixedIndent in FOptions.PaintOptions, 1, - IndentSize), LineImage); - // Show node button if allowed, if there child nodes and at least one of the child - // nodes is visible or auto button hiding is disabled. - if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in Node.States) and - not ((vsAllChildrenHidden in Node.States) and - (toAutoHideButtons in TreeOptions.AutoOptions)) and - ((toShowRoot in TreeOptions.PaintOptions) or (GetNodeLevel(Node) > 0)) - then - PaintNodeButton(Canvas, Node, Column, CellRect, Offsets[ofsToggleButton], ButtonY, BidiMode); // Relative X position of toggle button is needed for proper BiDi calculation +// Initiates repaint of the given node and returns the just invalidated rectangle. - if ImageInfo[iiCheck].Index > -1 then - PaintCheckImage(Canvas, PaintInfo.ImageInfo[iiCheck], vsSelected in PaintInfo.Node.States); - end; +begin + Assert(Assigned(Node), 'Node must not be nil.'); + Assert(GetCurrentThreadId = MainThreadId, 'UI controls may only be chnaged in UI thread.'); + // Reset height measured flag too to cause a re-issue of the OnMeasureItem event. + Exclude(Node.States, vsHeightMeasured); + if (FUpdateCount = 0) and HandleAllocated then + begin + Result := GetDisplayRect(Node, NoColumn, False); + InvalidateRect(Handle, @Result, False); + end + else + result := Rect(-1,-1,-1,-1); +end; - if ImageInfo[iiState].Index > -1 then - PaintImage(PaintInfo, iiState, False); - if ImageInfo[iiNormal].Index > -1 then - PaintImage(PaintInfo, iiNormal, True); +//---------------------------------------------------------------------------------------------------------------------- - // Now let descendants or applications draw whatever they want, - // but don't draw the node if it is currently being edited. - if not ((tsEditing in FStates) and (Node = FFocusedNode) and - ((Column = FEditColumn) or not UseColumns)) then - DoPaintNode(PaintInfo); +procedure TBaseVirtualTree.InvalidateToBottom(Node: PVirtualNode); - DoAfterCellPaint(Canvas, Node, Column, CellRect); - end; - end; +// Initiates repaint of client area starting at given node. If this node is not visible or not yet initialized +// then nothing happens. - // leave after first run if columns aren't used - if not UseColumns then - Break; - end - else - NextColumn := GetNextVisibleColumn(PaintInfo.Column); +var + R: TRect; - SelectClipRgn(PaintInfo.Canvas.Handle, 0); - // Stop column loop if there are no further columns in the given window. - if (PaintInfo.CellRect.Left >= Window.Right) or (NextColumn = InvalidColumn) then - Break; +begin + if (FUpdateCount = 0) and HandleAllocated then + begin + if (Node = nil) or (Node = FRoot) then + Invalidate + else + if (vsInitialized in Node.States) and IsEffectivelyVisible[Node] then + begin + R := GetDisplayRect(Node, NoColumn, False); + if R.Top < ClientHeight then + begin + if (toChildrenAbove in FOptions.PaintOptions) and (vsExpanded in Node.States) then + Dec(R.Top, Node.TotalHeight + NodeHeight[Node]); + R.Bottom := ClientHeight; + InvalidateRect(Handle, @R, False); + end; + end; + end; +end; - // Move on to next column which might not be the one immediately following the current one - // because of auto span feature. - PaintInfo.Position := Items[NextColumn].Position; +//---------------------------------------------------------------------------------------------------------------------- - // Move clip rectangle and continue. - if coVisible in Items[NextColumn].Options then - with PaintInfo do - begin - Items[NextColumn].GetAbsoluteBounds(CellRect.Left, CellRect.Right); - CellRect.Bottom := Node.NodeHeight; - ContentRect.Bottom := Node.NodeHeight; - end; - end; - end; +procedure TBaseVirtualTree.InvertSelection(VisibleOnly: Boolean); - // This node is finished, notify descendants/application. - with PaintInfo do - begin - DoAfterItemPaint(Canvas, Node, R); +// Inverts the current selection (so nodes which are selected become unselected and vice versa). +// If VisibleOnly is True then only visible nodes are considered. - // Final touch for this node: mark it if it is the current drop target node. - if (Node = FDropTargetNode) and (toShowDropmark in FOptions.PaintOptions) and - (poDrawDropMark in PaintOptions) then - DoPaintDropMark(Canvas, Node, R); - end; - end; // if not DoBeforeItemPaint() (no custom drawing) +var + Run: PVirtualNode; + NewSize: Integer; + NextFunction: TGetNextNodeProc; + TriggerChange: Boolean; +begin + if not FSelectionLocked and (toMultiSelect in FOptions.SelectionOptions) then + begin + Run := FRoot.FirstChild; + ClearTempCache; + if VisibleOnly then + NextFunction := GetNextVisibleNoInit + else + NextFunction := GetNextNoInit; + while Assigned(Run) do + begin + if vsSelected in Run.States then + InternalRemoveFromSelection(Run) + else + InternalCacheNode(Run); + Run := NextFunction(Run); + end; - with PaintInfo.Canvas do - begin - if DrawSelectionRect then - begin - PaintSelectionRectangle(PaintInfo.Canvas, Window.Left, SelectionRect, Rect(0, 0, PaintWidth, - CurrentNodeHeight)); - end; + // do some housekeeping + // Need to trigger the OnChange event from here if nodes were only deleted but not added. + TriggerChange := False; + NewSize := PackArray(FSelection, FSelectionCount); + if NewSize > -1 then + begin + FSelectionCount := NewSize; + SetLength(FSelection, FSelectionCount); + TriggerChange := True; + end; + if FTempNodeCount > 0 then + begin + AddToSelection(FTempNodeCache, FTempNodeCount); + ClearTempCache; + TriggerChange := False; + end; + Invalidate; + if TriggerChange then + Change(nil); + if Self.SelectedCount = 0 then + FNextNodeToSelect := nil;//Ensure that no other node is selected now + end; +end; - // Put the constructed node image onto the target canvas. - if not (poUnbuffered in PaintOptions) then - with TWithSafeRect(TargetRect), NodeBitmap do - BitBlt(TargetCanvas.Handle, Left, Top, Width, Height, Canvas.Handle, Window.Left, 0, SRCCOPY); - end; - end; +//---------------------------------------------------------------------------------------------------------------------- - Inc(TargetRect.Top, PaintInfo.Node.NodeHeight); - if TargetRect.Top >= MaximumBottom then - Break; +function TBaseVirtualTree.IsEditing: Boolean; - // Keep selection rectangle coordinates in sync. - if DrawSelectionRect then - OffsetRect(SelectionRect, 0, -PaintInfo.Node.NodeHeight); +begin + Result := tsEditing in FStates; +end; - // Advance to next visible node. - PaintInfo.Node := GetNextVisible(PaintInfo.Node, True); - end; - end; +//---------------------------------------------------------------------------------------------------------------------- - // Erase rest of window not covered by a node. - if TargetRect.Top < MaximumBottom then - begin - // Keep the horizontal target position to determine the selection rectangle offset later (if necessary). - BaseOffset := Target.X; - Target := TargetRect.TopLeft; - R := Rect(TargetRect.Left, 0, TargetRect.Left, MaximumBottom - Target.Y); - TargetRect := Rect(0, 0, MaximumRight - Target.X, MaximumBottom - Target.Y); +function TBaseVirtualTree.IsMouseSelecting: Boolean; - if not (poUnbuffered in PaintOptions) then - begin - // Avoid unnecessary copying of bitmap content. This will destroy the DC handle too. - NodeBitmap.Height := 0; - NodeBitmap.PixelFormat := pf32Bit; - NodeBitmap.SetSize(TargetRect.Right - TargetRect.Left, TargetRect.Bottom - TargetRect.Top); - end; +begin + Result := (tsDrawSelPending in FStates) or (tsDrawSelecting in FStates); +end; - // Call back application/descendants whether they want to erase this area. - if not DoPaintBackground(PaintInfo.Canvas, TargetRect) then - begin - if UseBackground then - begin - SetCanvasOrigin(PaintInfo.Canvas, 0, 0); - if toStaticBackground in TreeOptions.PaintOptions then - StaticBackground(FBackground, PaintInfo.Canvas, Target, TargetRect, FColors.BackGroundColor) - else - TileBackground(FBackground, PaintInfo.Canvas, Target, TargetRect, FColors.BackGroundColor); - end - else - begin - // Consider here also colors of the columns. - SetCanvasOrigin(PaintInfo.Canvas, Target.X, 0); // This line caused issue #313 when it was placed above the if-statement - if UseColumns then - begin - with FHeader.Columns do - begin - // If there is no content in the tree then the first column has not yet been determined. - if FirstColumn = InvalidColumn then - begin - FirstColumn := GetFirstVisibleColumn; - repeat - if FirstColumn <> InvalidColumn then - begin - R.Left := Items[FirstColumn].Left; - R.Right := R.Left + Items[FirstColumn].Width; - if R.Right > TargetRect.Left then - Break; - FirstColumn := GetNextVisibleColumn(FirstColumn); - end; - until FirstColumn = InvalidColumn; - end - else - begin - R.Left := Items[FirstColumn].Left; - R.Right := R.Left + Items[FirstColumn].Width; - end; +function TBaseVirtualTree.IsUpdating: Boolean; +// The tree does currently not update its window because a BeginUpdate has not yet ended. +begin + Exit(UpdateCount > 0); +end; - // Initialize MaxRight. - MaxRight := Target.X - 1; +//---------------------------------------------------------------------------------------------------------------------- - PaintInfo.Canvas.Font.Color := FColors.GridLineColor; - while (FirstColumn <> InvalidColumn) and (MaxRight < TargetRect.Right + Target.X) do - begin - // Determine left and right coordinate of the current column - ColLeft := Items[FirstColumn].Left; - ColRight := (ColLeft + Items[FirstColumn].Width); +function TBaseVirtualTree.IterateSubtree(Node: PVirtualNode; Callback: TVTGetNodeProc; Data: Pointer; + Filter: TVirtualNodeStates = []; DoInit: Boolean = False; ChildNodesOnly: Boolean = False): PVirtualNode; - // Check wether this column needs to be painted at all. - if (ColRight >= MaxRight) then - begin - R.Left := MaxRight; // Continue where we left off - R.Right := ColRight; // Paint to the right of the column - MaxRight := ColRight; // And record were to start the next column. +// Iterates through the all children and grandchildren etc. of Node (or the entire tree if Node = nil) +// and calls for each node the provided callback method (which must not be empty). +// Filter determines which nodes to consider (an empty set denotes all nodes). +// If DoInit is True then nodes which aren't initialized yet will be initialized. +// Note: During execution of the callback the application can set Abort to True. In this case the iteration is stopped +// and the last accessed node (the one on which the callback set Abort to True) is returned to the caller. +// Otherwise (no abort) nil is returned. - if (poGridLines in PaintOptions) and - (toFullVertGridLines in FOptions.PaintOptions) and - (toShowVertGridLines in FOptions.PaintOptions) and - (not (hoAutoResize in FHeader.Options) or (Cardinal(FirstColumn) < TColumnPosition(Count - 1))) then - begin - DrawDottedVLine(PaintInfo, R.Top, R.Bottom, R.Right - 1); - Dec(R.Right); - end; +var + Stop: PVirtualNode; + Abort: Boolean; + GetNextNode: TGetNextNodeProc; + WasIterating: Boolean; - if not (coParentColor in Items[FirstColumn].Options) then - PaintInfo.Canvas.Brush.Color := Items[FirstColumn].Color - else - PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor; - PaintInfo.Canvas.FillRect(R); - end; - FirstColumn := GetNextVisibleColumn(FirstColumn); - end; +begin + Assert(Node <> FRoot, 'Node must not be the hidden root node.'); - // Erase also the part of the tree not covert by a column. - if R.Right < TargetRect.Right + Target.X then - begin - R.Left := R.Right; - R.Right := TargetRect.Right + Target.X; - // Prevent erasing the last vertical grid line. - if (poGridLines in PaintOptions) and - (toFullVertGridLines in FOptions.PaintOptions) and (toShowVertGridLines in FOptions.PaintOptions) and - (not (hoAutoResize in FHeader.Options)) then - Inc(R.Left); - PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor; - PaintInfo.Canvas.FillRect(R); - end; - end; - SetCanvasOrigin(PaintInfo.Canvas, 0, 0); - end - else - begin - // No columns nor bitmap background. Simply erase it with the tree color. - SetCanvasOrigin(PaintInfo.Canvas, 0, 0); - PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor; - PaintInfo.Canvas.FillRect(TargetRect); - end; - end; - end; - SetCanvasOrigin(PaintInfo.Canvas, 0, 0); + WasIterating := tsIterating in FStates; + DoStateChange([tsIterating]); + try + // prepare function to be used when advancing + if DoInit then + GetNextNode := GetNext + else + GetNextNode := GetNextNoInit; - if DrawSelectionRect then - begin - R := OrderRect(FNewSelRect); - // Remap the selection rectangle to the current window of the tree. - // Since Target has been used for other tasks BaseOffset got the left extent of the target position here. - OffsetRect(R, -Target.X + BaseOffset - Window.Left, -Target.Y + FOffsetY); - SetBrushOrigin(PaintInfo.Canvas, 0, Target.X and 1); - PaintSelectionRectangle(PaintInfo.Canvas, 0, R, TargetRect); - end; + Abort := False; + if Node = nil then + Stop := nil + else + begin + if not (vsInitialized in Node.States) and DoInit then + InitNode(Node); - if not (poUnBuffered in PaintOptions) then - with Target, NodeBitmap do - BitBlt(TargetCanvas.Handle, X, Y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY); - end; - finally - PaintInfo.Canvas.Unlock; - if poUnbuffered in PaintOptions then - RestoreDC(TargetCanvas.Handle, SavedTargetDC) + // The stopper does not need to be initialized since it is not taken into the enumeration. + Stop := Node.NextSibling; + if Stop = nil then + begin + Stop := Node; + repeat + Stop := Stop.Parent; + until (Stop = FRoot) or Assigned(Stop.NextSibling); + if Stop = FRoot then + Stop := nil else - NodeBitmap.Free; - end;//try..finally - - if (FEmptyListMessage <> '') and ((ChildCount[nil] = 0) or (GetFirstVisible = nil)) then + Stop := Stop.NextSibling; + end; + end; + + // Use first node if we start with the root. + if Node = nil then + Node := GetFirstNoInit; + + if Assigned(Node) then + begin + if not (vsInitialized in Node.States) and DoInit then + InitNode(Node); + + // Skip given node if only the child nodes are requested. + if ChildNodesOnly then begin - // output a message if no items are to display - Canvas.Font := Self.Font; - SetBkMode(TargetCanvas.Handle, TRANSPARENT); - R.Left := OffSetX + 2; - R.Top := 2; - R.Right := R.Left + Width - 2; - R.Bottom := Height -2; - TargetCanvas.Font.Color := clGrayText; - TargetCanvas.TextRect(R, FEmptyListMessage, [tfNoClip, tfLeft, tfWordBreak, tfExpandTabs]); + if Node.ChildCount = 0 then + Node := nil + else + Node := GetNextNode(Node); end; - DoAfterPaint(TargetCanvas); - finally - DoStateChange([], [tsPainting]); + if Filter = [] then + begin + // unfiltered loop + while Assigned(Node) and (Node <> Stop) do + begin + Callback(Self, Node, Data, Abort); + if Abort then + Break; + Node := GetNextNode(Node); + end; + end + else + begin + // filtered loop + while Assigned(Node) and (Node <> Stop) do + begin + if Node.States * Filter = Filter then + Callback(Self, Node, Data, Abort); + if Abort then + Break; + Node := GetNextNode(Node); + end; + end; end; + + if Abort then + Result := Node + else + Result := nil; + finally + if not WasIterating then + DoStateChange([], [tsIterating]); end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.PasteFromClipboard: Boolean; +procedure TBaseVirtualTree.LoadFromFile(const FileName: TFileName); -// Reads what is currently on the clipboard into the tree (if the format is supported). -// Note: If the application wants to have text or special formats to be inserted then it must implement -// its own code (OLE). Here only the native tree format is accepted. +var + FileStream: TFileStream; + +begin + FileStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + try + LoadFromStream(FileStream); + finally + FileStream.Free; + end; +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.LoadFromStream(Stream: TStream); + +// Clears the current content of the tree and loads a new structure from the given stream. var - Data: IDataObject; - Source: TBaseVirtualTree; + ThisID: TMagicID; + Version, + Count: Cardinal; + Node: PVirtualNode; begin - Result := False; if not (toReadOnly in FOptions.MiscOptions) then begin - if OleGetClipboard(Data) <> S_OK then - ShowError(SClipboardFailed, hcTFClipboardFailed) - else + Clear; + // Check first whether this is a stream we can read. + if Stream.Read(ThisID, SizeOf(TMagicID)) < SizeOf(TMagicID) then + ShowError(SStreamTooSmall, hcTFStreamTooSmall); + + if (ThisID[0] = MagicID[0]) and + (ThisID[1] = MagicID[1]) and + (ThisID[2] = MagicID[2]) and + (ThisID[5] = MagicID[5]) then begin - // Try to get the source tree of the operation to optimize the operation. - Source := GetTreeFromDataObject(Data); - Result := ProcessOLEData(Source, Data, FFocusedNode, FDefaultPasteMode, Assigned(Source) and - (tsCutPending in Source.FStates)); - if Assigned(Source) then + Version := Word(ThisID[3]); + if Version <= VTTreeStreamVersion then begin - if Source <> Self then - Source.FinishCutOrCopy - else - DoStateChange([], [tsCutPending]); - end; - end; + BeginUpdate; + try + if Version < 2 then + Count := MaxInt + else + Stream.ReadBuffer(Count, SizeOf(Count)); + + while (Stream.Position < Stream.Size) and (Count > 0) do + begin + Dec(Count); + Node := MakeNewNode; + InternalConnectNode(Node, FRoot, Self, amAddChildLast); + InternalAddFromStream(Stream, Version, Node); + end; + DoNodeCopied(nil); + if Assigned(FOnLoadTree) then + FOnLoadTree(Self, Stream); + finally + EndUpdate; + end; + end + else + ShowError(SWrongStreamVersion, hcTFWrongStreamVersion); + end + else + ShowError(SWrongStreamFormat, hcTFWrongStreamFormat); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.PrepareDragImage(HotSpot: TPoint; const DataObject: IDataObject); +procedure TBaseVirtualTree.MeasureItemHeight(const Canvas: TCanvas; Node: PVirtualNode); -// Initiates an image drag operation. HotSpot is the position of the mouse in client coordinates. +// If the height of the given node has not yet been measured then do it now. var - PaintOptions: TVTInternalPaintOptions; - TreeRect, - PaintRect: TRect; - LocalSpot, - ImagePos, - PaintTarget: TPoint; - Image: TBitmap; + NewNodeHeight: Integer; begin - if CanShowDragImage then + if not (vsHeightMeasured in Node.States) then begin - // Determine the drag rectangle which is a square around the hot spot. Operate in virtual tree space. - LocalSpot := HotSpot; - Dec(LocalSpot.X, -FEffectiveOffsetX); - Dec(LocalSpot.Y, FOffsetY); - TreeRect := Rect(LocalSpot.X - FDragWidth div 2, LocalSpot.Y - FDragHeight div 2, LocalSpot.X + FDragWidth div 2, - LocalSpot.Y + FDragHeight div 2); - - // Check that we have a valid rectangle. - PaintRect := TreeRect; - with TWithSafeRect(TreeRect) do + Include(Node.States, vsHeightMeasured); + if (toVariableNodeHeight in FOptions.MiscOptions) then begin - if Left < 0 then - begin - PaintTarget.X := -Left; - PaintRect.Left := 0; - end + NewNodeHeight := Node.NodeHeight; + // Anonymous methods help to make this thread safe easily. + if (MainThreadId <> GetCurrentThreadId) then + TThread.Synchronize(nil, + procedure + begin + DoMeasureItem(Canvas, Node, NewNodeHeight); + SetNodeHeight(Node, NewNodeHeight); + end + ) else - PaintTarget.X := 0; - if Top < 0 then begin - PaintTarget.Y := -Top; - PaintRect.Top := 0; - end - else - PaintTarget.Y := 0; + DoMeasureItem(Canvas, Node, NewNodeHeight); + SetNodeHeight(Node, NewNodeHeight); + end; end; + end; +end; - Image := TBitmap.Create; - with Image do - try - PixelFormat := pf32Bit; - SetSize(TreeRect.Right - TreeRect.Left, TreeRect.Bottom - TreeRect.Top); - // Erase the entire image with the color key value, for the case not everything - // in the image is covered by the tree image. - Canvas.Brush.Color := FColors.BackGroundColor; - Canvas.FillRect(Rect(0, 0, Width, Height)); +//---------------------------------------------------------------------------------------------------------------------- - PaintOptions := [poDrawSelection, poSelectedOnly]; - if FDragImageKind = diMainColumnOnly then - Include(PaintOptions, poMainOnly); - PaintTree(Image.Canvas, PaintRect, PaintTarget, PaintOptions); +procedure TBaseVirtualTree.MoveTo(Node: PVirtualNode; Tree: TBaseVirtualTree; Mode: TVTNodeAttachMode; + ChildrenOnly: Boolean); - // Once we have got the drag image we can convert all necessary coordinates into screen space. - OffsetRect(TreeRect, -FEffectiveOffsetX, FOffsetY); - ImagePos := ClientToScreen(TreeRect.TopLeft); - HotSpot := ClientToScreen(HotSpot); +// A simplified method to allow to move nodes to the root of another tree. - FDragImage.ColorKey := FColors.BackGroundColor; - FDragImage.PrepareDrag(Image, ImagePos, HotSpot, DataObject); - finally - Image.Free; - end; - end; +begin + MoveTo(Node, Tree.FRoot, Mode, ChildrenOnly); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.Print(Printer: TPrinter; PrintHeader: Boolean); +procedure TBaseVirtualTree.MoveTo(Source, Target: PVirtualNode; Mode: TVTNodeAttachMode; ChildrenOnly: Boolean); + +// Moves the given node (and all its children) to Target. Source must belong to the tree instance which calls this +// MoveTo method. Mode determines how to connect Source to Target. +// This method might involve a change of the tree if Target belongs to a different tree than Source. var - SaveTreeFont: TFont; // Remembers the tree's current font. - SaveHeaderFont: TFont; // Remembers the header's current font. - ImgRect, // Describes the dimensions of Image. - TreeRect, // The total VTree dimensions. - DestRect, // Dimensions of PrinterImage. - SrcRect: TRect; // Clip dimensions from Image -> PrinterImage - P: TPoint; // Used by PaintTree. - Options: TVTInternalPaintOptions; // Used by PaintTree. - Image, // Complete Tree is drawn to this image. - PrinterImage: TBitmap; // This is the image that gets printed. - SaveColor: TColor; // Remembers the VTree Color. - pTxtHeight, // Height of font in the TPrinter.Canvas - vTxtHeight, // Height of font in the VTree Canvas - vPageWidth, - vPageHeight, // Printer height in VTree resolution - xPageNum, yPageNum, // # of pages (except the occasional last one) - xPage, yPage: Integer; // Loop counter - Scale: Extended; // Scale factor between Printer Canvas and VTree Canvas - LogFont: TLogFont; + TargetTree: TBaseVirtualTree; + Allowed: Boolean; + NewNode: PVirtualNode; + Stream: TMemoryStream; begin - if Assigned(Printer) then - begin - BeginUpdate; - - // Grid lines are the only parts which are desirable when printing. - Options := [poGridLines]; + Assert(TreeFromNode(Source) = Self, 'The source tree must contain the source node.'); - // Remember the tree font. - SaveTreeFont := TFont.Create; - SaveTreeFont.Assign(Font); - // Create a new font for printing which does not use clear type output (but is antialiased, if possible) - // and which has the highest possible quality. - GetObject(Font.Handle, SizeOf(TLogFont), @LogFont); - LogFont.lfQuality := ANTIALIASED_QUALITY; - Font.Handle := CreateFontIndirect(LogFont); + // When moving nodes then source and target must not be the same node unless only the source's children are + // moved and they are inserted before or after the node itself. + Allowed := (Source <> Target) or ((Mode in [amInsertBefore, amInsertAfter]) and ChildrenOnly); - // Create an image that will hold the complete VTree - Image := TBitmap.Create; - Image.PixelFormat := pf32Bit; - PrinterImage := nil; - try - TreeRect := GetTreeRect; + if Allowed and (Mode <> amNoWhere) and Assigned(Source) and (Source <> FRoot) and + not (toReadOnly in FOptions.MiscOptions) then + begin + // Assume that an empty destination means the root in this (the source) tree. + if Target = nil then + begin + TargetTree := Self; + Target := FRoot; + Mode := amAddChildFirst; + end + else + TargetTree := TreeFromNode(Target); - Image.Width := TreeRect.Right - TreeRect.Left; - P := Point(0, 0); - if (hoVisible in FHeader.Options) and PrintHeader then - begin - Inc(TreeRect.Bottom, FHeader.Height); - Inc(P.Y, FHeader.Height); + if Target = TargetTree.FRoot then + begin + case Mode of + amInsertBefore: + Mode := amAddChildFirst; + amInsertAfter: + Mode := amAddChildLast; end; - Image.Height := TreeRect.Bottom - TreeRect.Top; + end; - ImgRect.Left := 0; - ImgRect.Top := 0; - ImgRect.Right := Image.Width; + // Make sure the target node is initialized. + if not (vsInitialized in Target.States) then + TargetTree.InitNode(Target) + else + if (vsHasChildren in Target.States) and (Target.ChildCount = 0) then + TargetTree.InitChildren(Target); - // Force the background to white color during the rendering. - SaveColor := FColors.BackGroundColor; - Color := clWhite; - // Print header if it is visible. - if (hoVisible in FHeader.Options) and PrintHeader then + if TargetTree = Self then + begin + // Simple case: move node(s) within the same tree. + if Target = FRoot then + Allowed := DoNodeMoving(Source, nil) + else + Allowed := DoNodeMoving(Source, Target); + if Allowed then begin - SaveHeaderFont := TFont.Create; - try - SaveHeaderFont.Assign(FHeader.Font); - // Create a new font for printing which does not use clear type output (but is antialiased, if possible) - // and which has the highest possible quality. - GetObject(FHeader.Font.Handle, SizeOf(TLogFont), @LogFont); - LogFont.lfQuality := ANTIALIASED_QUALITY; - FHeader.Font.Handle := CreateFontIndirect(LogFont); - ImgRect.Bottom := FHeader.Height; - FHeader.Columns.PaintHeader(Image.Canvas.Handle, ImgRect, 0); - FHeader.Font := SaveHeaderFont; - finally - SaveHeaderFont.Free; - end; - end; - // The image's height is already adjusted for the header if it is visible. - ImgRect.Bottom := Image.Height; - - PaintTree(Image.Canvas, ImgRect, P, Options, pf32Bit); - Color := SaveColor; - - // Activate the printer - Printer.BeginDoc; - Printer.Canvas.Font := Font; - - // Now we can calculate the scaling : - pTxtHeight := Printer.Canvas.TextHeight('Tj'); - vTxtHeight := Canvas.TextHeight('Tj'); - - Scale := pTxtHeight / vTxtHeight; - - // Create an Image that has the same dimensions as the printer canvas but - // scaled to the VTree resolution: - PrinterImage := TBitmap.Create; - - vPageHeight := Round(Printer.PageHeight / Scale); - vPageWidth := Round(Printer.PageWidth / Scale); - - // We do a minumum of one page. - xPageNum := Trunc(Image.Width / vPageWidth); - yPageNum := Trunc(Image.Height / vPageHeight); + // Check first that Source is not added as new child to a target node which + // is already a child of Source. + // Consider the case Source and Target are the same node, but only child nodes are moved. + if (Source <> Target) and HasAsParent(Target, Source) then + ShowError(SWrongMoveError, hcTFWrongMoveError); - PrinterImage.SetSize(vPageWidth, vPageHeight); + if not ChildrenOnly then + begin + // Disconnect from old location. + InternalDisconnectNode(Source, True); + // Connect to new location. + InternalConnectNode(Source, Target, Self, Mode); + DoNodeMoved(Source); + end + else + begin + // Only child nodes should be moved. Insertion order depends on move mode. + if Mode = amAddChildFirst then + begin + Source := Source.LastChild; + while Assigned(Source) do + begin + NewNode := Source.PrevSibling; + // Disconnect from old location. + InternalDisconnectNode(Source, True, False); + // Connect to new location. + InternalConnectNode(Source, Target, Self, Mode); + DoNodeMoved(Source); + Source := NewNode; + end; + end + else + begin + Source := Source.FirstChild; + while Assigned(Source) do + begin + NewNode := Source.NextSibling; + // Disconnect from old location. + InternalDisconnectNode(Source, True, False); + // Connect to new location. + InternalConnectNode(Source, Target, Self, Mode); + DoNodeMoved(Source); + Source := NewNode; + end; + end; + end; + end; + end + else + begin + // Difficult case: move node(s) to another tree. + // In opposition to node copying we ask only once if moving is allowed because + // we cannot take back a move once done. + if Target = TargetTree.FRoot then + Allowed := DoNodeMoving(Source, nil) + else + Allowed := DoNodeMoving(Source, Target); - // Split vertically: - for yPage := 0 to yPageNum do + if Allowed then begin - DestRect.Left := 0; - DestRect.Top := 0; - DestRect.Right := PrinterImage.Width; - DestRect.Bottom := PrinterImage.Height; - - // Split horizontally: - for xPage := 0 to xPageNum do + Stream := TMemoryStream.Create; + try + // Write all nodes into a temporary stream depending on the ChildrenOnly flag. + if not ChildrenOnly then + WriteNode(Stream, Source) + else begin - SrcRect.Left := vPageWidth * xPage; - SrcRect.Top := vPageHeight * yPage; - SrcRect.Right := vPageWidth * xPage + PrinterImage.Width; - SrcRect.Bottom := SrcRect.Top + vPageHeight; - - // Clear the image - PrinterImage.Canvas.Brush.Color := clWhite; - PrinterImage.Canvas.FillRect(Rect(0, 0, PrinterImage.Width, PrinterImage.Height)); - PrinterImage.Canvas.CopyRect(DestRect, Image.Canvas, SrcRect); - PrtStretchDrawDIB(Printer.Canvas, Rect(0, 0, Printer.PageWidth, Printer.PageHeight - 1), PrinterImage); - if xPage <> xPageNum then - Printer.NewPage; + Source := Source.FirstChild; + while Assigned(Source) do + begin + WriteNode(Stream, Source); + Source := Source.NextSibling; + end; end; - if yPage <> yPageNum then - Printer.NewPage; + // Now load the serialized nodes into the target node (tree). + TargetTree.BeginUpdate; + try + Stream.Position := 0; + while Stream.Position < Stream.Size do + begin + NewNode := TargetTree.MakeNewNode; + InternalConnectNode(NewNode, Target, TargetTree, Mode); + TargetTree.InternalAddFromStream(Stream, VTTreeStreamVersion, NewNode); + DoNodeMoved(NewNode); + end; + finally + TargetTree.EndUpdate; + end; + finally + Stream.Free; + end; + // finally delete original nodes + BeginUpdate; + try + if ChildrenOnly then + DeleteChildren(Source) + else + DeleteNode(Source); + finally + EndUpdate; + end; end; + end; - // Restore tree font. - Font := SaveTreeFont; - SaveTreeFont.Free; - Printer.EndDoc; - finally - PrinterImage.Free; - Image.Free; - EndUpdate; + InvalidateCache; + if (FUpdateCount = 0) and Allowed then + begin + ValidateCache; + UpdateScrollBars(True); + Invalidate; + if TargetTree <> Self then + TargetTree.Invalidate; end; + StructureChange(Source, crNodeMoved); end; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.ProcessDrop(const DataObject: IDataObject; TargetNode: PVirtualNode; var Effect: Integer; - Mode: TVTNodeAttachMode): Boolean; +procedure TBaseVirtualTree.PaintTree(TargetCanvas: TCanvas; Window: TRect; Target: TPoint; + PaintOptions: TVTInternalPaintOptions; PixelFormat: TPixelFormat); -// Recreates the (sub) tree structure serialized into memory and provided by DataObject. The new nodes are attached to -// the passed node or FRoot if TargetNode is nil. -// Returns True on success, i.e. the CF_VIRTUALTREE format is supported by the data object and the structure could be -// recreated, otherwise False. +// This is the core paint routine of the tree. It is responsible for maintaining the paint cycles per node as well +// as coordinating drawing of the various parts of the tree image. +// TargetCanvas is the canvas to which to draw the tree image. This is usually the tree window itself but could well +// be a bitmap or printer canvas. +// Window determines which part of the entire tree image to draw. The full size of the virtual image is determined +// by GetTreeRect. +// Target is the position in TargetCanvas where to draw the tree part specified by Window. +// PaintOptions determines what of the tree to draw. For different tasks usually different parts need to be drawn, with +// a full image in the window, selected only nodes for a drag image etc. + +const + ImageKind: array[Boolean] of TVTImageKind = (ikNormal, ikSelected); var - Source: TBaseVirtualTree; + DrawSelectionRect, + UseBackground, + ShowCheckImages, + UseColumns, + IsMainColumn: Boolean; -begin - Result := False; - if Mode = amNoWhere then - Effect := DROPEFFECT_NONE - else - begin - BeginUpdate; - // try to get the source tree of the operation - Source := GetTreeFromDataObject(DataObject); - if Assigned(Source) then - Source.BeginUpdate; - try - try - // Before adding the new nodes try to optimize the operation if source and target tree reside in - // the same application and operation is a move. - if ((Effect and DROPEFFECT_MOVE) <> 0) and Assigned(Source) then - begin - // If both copy and move are specified then prefer a copy because this is not destructing. - Result := ProcessOLEData(Source, DataObject, TargetNode, Mode, (Effect and DROPEFFECT_COPY) = 0); - // Since we made an optimized move or a copy there's no reason to act further after DoDragging returns. - Effect := DROPEFFECT_NONE; - end - else - // Act only if move or copy operation is requested. - if (Effect and (DROPEFFECT_MOVE or DROPEFFECT_COPY)) <> 0 then - Result := ProcessOLEData(Source, DataObject, TargetNode, Mode, False) - else - Result := False; - except - Effect := DROPEFFECT_NONE; - end; - finally - if Assigned(Source) then - Source.EndUpdate; - EndUpdate; - end; - end; -end; + IndentSize, + ButtonY: Integer; // Y position of toggle button within the node's rect + LineImage: TLineImage; + PaintInfo: TVTPaintInfo; // all necessary information about a node to pass to the paint routines -//---------------------------------------------------------------------------------------------------------------------- + R, // the area of an entire node in its local coordinate + TargetRect, // the area of a node (part) in the target canvas + SelectionRect, // ordered rectangle used for drawing the selection focus rect + ClipRect: TRect; // area to which the canvas will be clipped when painting a node's content + NextColumn: TColumnIndex; + BaseOffset: Integer; // top position of the top node to draw given in absolute tree coordinates + NodeBitmap: TBitmap; // small buffer to draw flicker free + MaximumRight, // maximum horizontal target position + MaximumBottom: Integer; // maximum vertical target position + SelectLevel: Integer; // > 0 if current node is selected or child/grandchild etc. of a selected node + FirstColumn: TColumnIndex; // index of first column which is at least partially visible in the given window -type - // needed to handle OLE global memory objects - TOLEMemoryStream = class(TCustomMemoryStream) - public - function Write(const Buffer; Count: Integer): Integer; override; - end; + MaxRight, + ColLeft, + ColRight: Integer; -//---------------------------------------------------------------------------------------------------------------------- + SavedTargetDC: Integer; + PaintWidth: Integer; + CurrentNodeHeight: Integer; + lUseSelectedBkColor: Boolean; // determines if the dotted grid lines need to be painted in selection color of background color -function TOLEMemoryStream.Write(const Buffer; Count: Integer): Integer; + CellIsTouchingClientRight: Boolean; + CellIsInLastColumn: Boolean; + ColumnIsFixed: Boolean; begin - raise EStreamError.CreateRes(PResStringRec(@SCantWriteResourceStreamError)); -end; + if not (tsPainting in FStates) then + begin + DoStateChange([tsPainting]); + try + DoBeforePaint(TargetCanvas); -//----------------- TBaseVirtualTree ----------------------------------------------------------------------------- + if poUnbuffered in PaintOptions then + SavedTargetDC := SaveDC(TargetCanvas.Handle) + else + SavedTargetDC := 0; -procedure TBaseVirtualTree.DoDrawHint(Canvas: TCanvas; Node: PVirtualNode; R: - TRect; Column: TColumnIndex); + // Prepare paint info structure. + ZeroMemory(@PaintInfo, SizeOf(PaintInfo)); -begin - if Assigned(FOnDrawHint) then - FOnDrawHint(Self, Canvas, Node, R, Column); -end; + PaintWidth := Window.Right - Window.Left; + + if not (poUnbuffered in PaintOptions) then + begin + // Create small bitmaps and initialize default values. + // The bitmaps are used to paint one node at a time and to draw the result to the target (e.g. screen) in one step, + // to prevent flickering. + NodeBitmap := TBitmap.Create; + // For alpha blending we need the 32 bit pixel format. For other targets there might be a need for a certain + // pixel format (e.g. printing). + if ((FDrawSelectionMode = smBlendedRectangle) or (tsUseThemes in FStates) or + (toUseBlendedSelection in FOptions.PaintOptions)) then + NodeBitmap.PixelFormat := pf32Bit + else + NodeBitmap.PixelFormat := PixelFormat; + + NodeBitmap.Width := PaintWidth; + + // Make sure the buffer bitmap and target bitmap use the same transformation mode. + SetMapMode(NodeBitmap.Canvas.Handle, GetMapMode(TargetCanvas.Handle)); + PaintInfo.Canvas := NodeBitmap.Canvas; + end + else + begin + PaintInfo.Canvas := TargetCanvas; + NodeBitmap := nil; + end; -//---------------------------------------------------------------------------------------------------------------------- + // Lock the canvas to avoid that it gets freed on the way. + PaintInfo.Canvas.Lock; + try + // Prepare the current selection rectangle once. The corner points are absolute tree coordinates. + SelectionRect := OrderRect(FNewSelRect); + DrawSelectionRect := IsMouseSelecting and not IsRectEmpty(SelectionRect) and (GetKeyState(VK_LBUTTON) < 0); -procedure TBaseVirtualTree.DoGetHintSize(Node: PVirtualNode; Column: - TColumnIndex; var R: TRect); + // R represents an entire node (all columns), but is a bit unprecise when it comes to + // trees without any column defined, because FRangeX only represents the maximum width of all + // nodes in the client area (not all defined nodes). There might be, however, wider nodes somewhere. Without full + // validation I cannot better determine the width, though. By using at least the control's width it is ensured + // that the tree is fully displayed on screen. + R := Rect(0, 0, Max(FRangeX, ClientWidth), 0); -begin - if Assigned(FOnGetHintSize) then - FOnGetHintSize(Self, Node, Column, R); -end; + // For quick checks some intermediate variables are used. + UseBackground := (toShowBackground in FOptions.PaintOptions) and Assigned(FBackground.Graphic) and + (poBackground in PaintOptions); + ShowCheckImages := Assigned(FCheckImages) and (toCheckSupport in FOptions.MiscOptions); + UseColumns := FHeader.UseColumns; -//---------------------------------------------------------------------------------------------------------------------- + // Adjust paint options to tree settings. Hide selection if told so or the tree is unfocused. + if (toAlwaysHideSelection in FOptions.PaintOptions) or + (not Focused and (toHideSelection in FOptions.PaintOptions)) then + Exclude(PaintOptions, poDrawSelection); + if toHideFocusRect in FOptions.PaintOptions then + Exclude(PaintOptions, poDrawFocusRect); -procedure TBaseVirtualTree.DoGetHintKind(Node: PVirtualNode; Column: - TColumnIndex; var Kind: TVTHintKind); + // Determine node to start drawing with. + BaseOffset := 0; + PaintInfo.Node := GetNodeAt(0, Window.Top, False, BaseOffset); + if PaintInfo.Node = nil then + BaseOffset := Window.Top; -begin - if Assigned(FOnGetHintKind) then - FOnGetHintKind(Self, Node, Column, Kind) - else - Kind := DefaultHintKind; -end; + // Transform selection rectangle into node bitmap coordinates. + if DrawSelectionRect then + OffsetRect(SelectionRect, 0, -BaseOffset); -function TBaseVirtualTree.GetDefaultHintKind: TVTHintKind; + // The target rectangle holds the coordinates of the exact area to blit in target canvas coordinates. + // It is usually smaller than an entire node and wanders while the paint loop advances. + MaximumRight := Target.X + (Window.Right - Window.Left); + MaximumBottom := Target.Y + (Window.Bottom - Window.Top); -begin - Result := vhkText; -end; + TargetRect := Rect(Target.X, Target.Y - (Window.Top - BaseOffset), MaximumRight, 0); + TargetRect.Bottom := TargetRect.Top; + TargetCanvas.Font := Self.Font; -//---------------------------------------------------------------------------------------------------------------------- + // This marker gets the index of the first column which is visible in the given window. + // This is needed for column based background colors. + FirstColumn := InvalidColumn; -function TBaseVirtualTree.ProcessOLEData(Source: TBaseVirtualTree; const DataObject: IDataObject; TargetNode: PVirtualNode; - Mode: TVTNodeAttachMode; Optimized: Boolean): Boolean; + if Assigned(PaintInfo.Node) then + begin -// Recreates the (sub) tree structure serialized into memory and provided by DataObject. The new nodes are attached to -// the passed node or FRoot if TargetNode is nil according to Mode. Optimized can be set to True if the entire operation -// happens within the same process (i.e. sender and receiver of the OLE operation are located in the same process). -// Optimize = True makes only sense if the operation to carry out is a move hence it is also the indication of the -// operation to be done here. Source is the source of the OLE data and only of use (and usually assigned) when -// an OLE operation takes place in the same application. -// Returns True on success, i.e. the CF_VIRTUALTREE format is supported by the data object and the structure could be -// recreated, otherwise False. + // ----- main node paint loop + while Assigned(PaintInfo.Node) do + begin + // Determine LineImage, SelectionLevel and IndentSize + SelectLevel := DetermineLineImageAndSelectLevel(PaintInfo.Node, LineImage); + IndentSize := Length(LineImage); -var - Medium: TStgMedium; - Stream: TStream; - Data: Pointer; - Node: PVirtualNode; - Nodes: TNodeArray; - I: Integer; - Res: HRESULT; - ChangeReason: TChangeReason; + // Initialize node if not already done. + if not (vsInitialized in PaintInfo.Node.States) then + InitNode(PaintInfo.Node); + if (vsSelected in PaintInfo.Node.States) and not (toChildrenAbove in FOptions.PaintOptions) then + Inc(SelectLevel); -begin - Nodes := nil; - // Check the data format available by the data object. - with StandardOLEFormat do - begin - // Read best format. - cfFormat := CF_VIRTUALTREE; - end; - Result := DataObject.QueryGetData(StandardOLEFormat) = S_OK; - if Result and not (toReadOnly in FOptions.MiscOptions) then - begin - BeginUpdate; - Result := False; - try - if TargetNode = nil then - TargetNode := FRoot; - if TargetNode = FRoot then - begin - case Mode of - amInsertBefore: - Mode := amAddChildFirst; - amInsertAfter: - Mode := amAddChildLast; - end; - end; + // Ensure the node's height is determined. + MeasureItemHeight(PaintInfo.Canvas, PaintInfo.Node); - // Optimized means source is known and in the same process so we can access its pointers, which avoids duplicating - // the data while doing a serialization. Can only be used with cut'n paste and drag'n drop with move effect. - if Optimized then - begin - if tsOLEDragging in Source.FStates then - Nodes := Source.FDragSelection - else - Nodes := Source.GetSortedCutCopySet(True); + // Adjust the brush origin for dotted lines depending on the current source position. + // It is applied some lines later, as the canvas might get reallocated, when changing the node bitmap. + PaintInfo.BrushOrigin := Point(Window.Left and 1, BaseOffset and 1); + Inc(BaseOffset, PaintInfo.Node.NodeHeight); - if Mode in [amInsertBefore,amAddChildLast] then - begin - for I := 0 to High(Nodes) do - if not HasAsParent(TargetNode, Nodes[I]) then - Source.MoveTo(Nodes[I], TargetNode, Mode, False); - end - else - begin - for I := High(Nodes) downto 0 do - if not HasAsParent(TargetNode, Nodes[I]) then - Source.MoveTo(Nodes[I], TargetNode, Mode, False); - end; - Result := True; - end - else - begin - if Source = Self then - ChangeReason := crNodeCopied - else - ChangeReason := crNodeAdded; - Res := DataObject.GetData(StandardOLEFormat, Medium); - if Res = S_OK then - begin - case Medium.tymed of - TYMED_ISTREAM, // IStream interface - TYMED_HGLOBAL: // global memory block + TargetRect.Bottom := TargetRect.Top + PaintInfo.Node.NodeHeight; + + // If poSelectedOnly is active then do the following stuff only for selected nodes or nodes + // which are children of selected nodes. + if (SelectLevel > 0) or not (poSelectedOnly in PaintOptions) then + begin + if not (poUnbuffered in PaintOptions) then begin - Stream := nil; - if Medium.tymed = TYMED_ISTREAM then - Stream := TOLEStream.Create(IUnknown(Medium.stm) as IStream) - else + // Adjust height of temporary node bitmap. + with NodeBitmap do begin - Data := GlobalLock(Medium.hGlobal); - if Assigned(Data) then + if Height <> PaintInfo.Node.NodeHeight then begin - // Get the total size of data to retrieve. - I := PCardinal(Data)^; - Inc(PCardinal(Data)); - Stream := TOLEMemoryStream.Create; - TOLEMemoryStream(Stream).SetPointer(Data, I); + // Avoid that the VCL copies the bitmap while changing its height. + Height := 0; + Height := PaintInfo.Node.NodeHeight; + SetCanvasOrigin(Canvas, Window.Left, 0); end; end; + end + else + begin + SetCanvasOrigin(PaintInfo.Canvas, -TargetRect.Left + Window.Left, -TargetRect.Top); + ClipCanvas(PaintInfo.Canvas, Rect(0, 0, TargetRect.Right - TargetRect.Left, + Min(TargetRect.Bottom - TargetRect.Top, MaximumBottom - TargetRect.Top))); // See issue #579 + end; - if Assigned(Stream) then - try - while Stream.Position < Stream.Size do + // Set the origin of the canvas' brush. This depends on the node heights. + with PaintInfo do + SetBrushOrigin(Canvas, BrushOrigin.X, BrushOrigin.Y); + + CurrentNodeHeight := PaintInfo.Node.NodeHeight; + R.Bottom := CurrentNodeHeight; + + // Let application decide whether the node should normally be drawn or by the application itself. + if not DoBeforeItemPaint(PaintInfo.Canvas, PaintInfo.Node, R) then + begin + // Init paint options for the background painting. + PaintInfo.PaintOptions := PaintOptions; + + // The node background can contain a single color, a bitmap or can be drawn by the application. + ClearNodeBackground(PaintInfo, UseBackground, True, Rect(Window.Left, TargetRect.Top, Window.Right, + TargetRect.Bottom)); + + // Prepare column, position and node clipping rectangle. + PaintInfo.CellRect := R; + if UseColumns then + InitializeFirstColumnValues(PaintInfo); + + // Now go through all visible columns (there's still one run if columns aren't used). + with TVirtualTreeColumnsCracker(FHeader.Columns) do + begin + while ((PaintInfo.Column > InvalidColumn) or not UseColumns) + and (PaintInfo.CellRect.Left < Window.Right) do begin - Node := MakeNewNode; - InternalConnectNode(Node, TargetNode, Self, Mode); - InternalAddFromStream(Stream, VTTreeStreamVersion, Node); - // This seems a bit strange because of the callback for granting to add the node - // which actually comes after the node has been added. The reason is that the node must - // contain valid data otherwise I don't see how the application can make a funded decision. - if not DoNodeCopying(Node, TargetNode) then + if UseColumns then begin - DeleteNode(Node); + PaintInfo.Column := PositionToIndex[PaintInfo.Position]; + if FirstColumn = InvalidColumn then + FirstColumn := PaintInfo.Column; + PaintInfo.BidiMode := Items[PaintInfo.Column].BiDiMode; + PaintInfo.Alignment := Items[PaintInfo.Column].Alignment; end else begin - DoNodeCopied(Node); - StructureChange(Node, ChangeReason); - // In order to maintain the same node order when restoring nodes in the case of amInsertAfter - // we have to move the reference node continously. Othwise we would end up with reversed node order. - if Mode = amInsertAfter then - TargetNode := Node; + PaintInfo.Column := NoColumn; + PaintInfo.BidiMode := BidiMode; + PaintInfo.Alignment := FAlignment; + end; + GetOffSets(PaintInfo.Node, PaintInfo.Offsets, TVTElement.ofsText, PaintInfo.Column); + + PaintInfo.PaintOptions := PaintOptions; + with PaintInfo do + begin + if (tsEditing in FStates) and (Node = FFocusedNode) and + ((Column = FEditColumn) or not UseColumns) then + Exclude(PaintOptions, poDrawSelection); + if not UseColumns or + ((vsSelected in Node.States) and (toFullRowSelect in FOptions.SelectionOptions) and + (poDrawSelection in PaintOptions)) or + (coParentColor in Items[PaintInfo.Column].Options) or + ((coStyleColor in Items[PaintInfo.Column].Options) and VclStyleEnabled) + then + Exclude(PaintOptions, poColumnColor); end; - end; - Result := True; - finally - Stream.Free; - if Medium.tymed = TYMED_HGLOBAL then - GlobalUnlock(Medium.hGlobal); - end; - end; - end; - ReleaseStgMedium(Medium); - end; - end; - finally - EndUpdate; - end; - end; -end; + IsMainColumn := PaintInfo.Column = FHeader.MainColumn; -//---------------------------------------------------------------------------------------------------------------------- + // Consider bidi mode here. In RTL context means left alignment actually right alignment and vice versa. + if PaintInfo.BidiMode <> bdLeftToRight then + ChangeBiDiModeAlignment(PaintInfo.Alignment); -procedure TBaseVirtualTree.ReinitChildren(Node: PVirtualNode; Recursive: Boolean); + // Paint the current cell if it is marked as being visible or columns aren't used and + // if this cell belongs to the main column if only the main column should be drawn. + if (not UseColumns or (coVisible in Items[PaintInfo.Column].Options)) and + (not (poMainOnly in PaintOptions) or IsMainColumn) then + begin + AdjustPaintCellRect(PaintInfo, NextColumn); -// Forces all child nodes of Node to be reinitialized. -// If Recursive is True then also the grandchildren are reinitialized. + // Paint the cell only if it is in the current window. + if PaintInfo.CellRect.Right > Window.Left then + begin + with PaintInfo do + begin + // Fill in remaining values in the paint info structure. + NodeWidth := DoGetNodeWidth(Node, Column, Canvas); -var - Run: PVirtualNode; + if ShowCheckImages and IsMainColumn then + begin + ImageInfo[iiCheck].Index := GetCheckImage(Node); + ImageInfo[iiCheck].Images := FCheckImages; + ImageInfo[iiCheck].Ghosted := False; + end + else + ImageInfo[iiCheck].Index := -1; + GetImageIndex(PaintInfo, ikState, iiState); + GetImageIndex(PaintInfo, ImageKind[vsSelected in Node.States], iiNormal); -begin - if Assigned(Node) then - begin - InitChildren(Node); - Run := Node.FirstChild; - end - else - begin - InitChildren(FRoot); - Run := FRoot.FirstChild; - end; + CalculateVerticalAlignments(PaintInfo, ButtonY); + // Take the space for the tree lines into account. + PaintInfo.AdjustImageCoordinates(); + if UseColumns then + begin + ClipRect := CellRect; + if poUnbuffered in PaintOptions then + begin + ClipRect.Left := Max(ClipRect.Left, Window.Left); + ClipRect.Right := Min(ClipRect.Right, Window.Right); + ClipRect.Top := Max(ClipRect.Top, Window.Top - (BaseOffset - CurrentNodeHeight)); + ClipRect.Bottom := ClipRect.Bottom - Max(TargetRect.Bottom - MaximumBottom, 0); + end; + ClipCanvas(Canvas, ClipRect); + end; - while Assigned(Run) do - begin - ReinitNode(Run, Recursive); - Run := Run.NextSibling; - end; -end; + // Paint the horizontal grid line. + if (poGridLines in PaintOptions) and (toShowHorzGridLines in FOptions.PaintOptions) then + begin + Canvas.Font.Color := FColors.GridLineColor; + if IsMainColumn and (FLineMode = lmBands) then + begin + if BidiMode = bdLeftToRight then + begin + DrawDottedHLine(PaintInfo, CellRect.Left + IfThen(toFixedIndent in FOptions.PaintOptions, 1, IndentSize) * Integer(FIndent), CellRect.Right - 1, + CellRect.Bottom - 1); + end + else + begin + DrawDottedHLine(PaintInfo, CellRect.Left, CellRect.Right - IfThen(toFixedIndent in FOptions.PaintOptions, 1, IndentSize) * Integer(FIndent) - 1, + CellRect.Bottom - 1); + end; + end + else + DrawDottedHLine(PaintInfo, CellRect.Left, CellRect.Right, CellRect.Bottom - 1); -//---------------------------------------------------------------------------------------------------------------------- + Dec(CellRect.Bottom); + Dec(ContentRect.Bottom); + end; -procedure TBaseVirtualTree.ReinitNode(Node: PVirtualNode; Recursive: Boolean); + if UseColumns then + begin + // Paint vertical grid line. + if (poGridLines in PaintOptions) and (toShowVertGridLines in FOptions.PaintOptions) then + begin + // These variables and the nested if conditions shall make the logic + // easier to understand. + CellIsTouchingClientRight := PaintInfo.CellRect.Right = ClientRect.Right; + CellIsInLastColumn := Position = TColumnPosition(Count - 1); + ColumnIsFixed := coFixed in FHeader.Columns[Column].Options; -// Forces the given node and all its children (if recursive is True) to be initialized again without -// modifying any data in the nodes nor deleting children (unless the application requests a different amount). + // Don't draw if this is the last column and the header is in autosize mode. + if not ((hoAutoResize in FHeader.Options) and CellIsInLastColumn) then + begin + // We have to take spanned cells into account which we determine + // by checking if CellRect.Right equals the Window.Right. + // But since the PaintTree procedure is called twice in + // TBaseVirtualTree.Paint (i.e. for fixed columns and other columns. + // CellIsTouchingClientRight does not work for fixed columns.) + // we have to paint fixed column grid line anyway. + if not CellIsTouchingClientRight or ColumnIsFixed then + begin + if (BidiMode = bdLeftToRight) or not ColumnIsEmpty(Node, Column) then + begin + Canvas.Font.Color := FColors.GridLineColor; + lUseSelectedBkColor := (poDrawSelection in PaintOptions) and (toFullRowSelect in FOptions.SelectionOptions) and + (vsSelected in Node.States) and not (toUseBlendedSelection in FOptions.PaintOptions) and not + (tsUseExplorerTheme in FStates); + DrawDottedVLine(PaintInfo, CellRect.Top, CellRect.Bottom, CellRect.Right - 1, lUseSelectedBkColor); + end; -begin - if Assigned(Node) and (Node <> FRoot) then - begin - // Remove dynamic styles. - Node.States := Node.States - [vsChecking, vsCutOrCopy, vsDeleting, vsHeightMeasured]; - if vsInitialized in Node.States then - InitNode(Node); - end; + Dec(CellRect.Right); + Dec(ContentRect.Right); + end; + end; + end; + end; - if Recursive then - ReinitChildren(Node, True); -end; + // Prepare background and focus rect for the current cell. + PrepareCell(PaintInfo, Window.Left, PaintWidth); -//---------------------------------------------------------------------------------------------------------------------- + // Some parts are only drawn for the main column. + if IsMainColumn then + begin + if (toShowTreeLines in FOptions.PaintOptions) and + (not (toHideTreeLinesIfThemed in FOptions.PaintOptions) or + not (tsUseThemes in FStates)) then + PaintTreeLines(PaintInfo, IfThen(toFixedIndent in FOptions.PaintOptions, 1, + IndentSize), LineImage); + // Show node button if allowed, if there child nodes and at least one of the child + // nodes is visible or auto button hiding is disabled. + if (toShowButtons in FOptions.PaintOptions) and (vsHasChildren in Node.States) and + not ((vsAllChildrenHidden in Node.States) and + (toAutoHideButtons in TreeOptions.AutoOptions)) and + ((toShowRoot in TreeOptions.PaintOptions) or (GetNodeLevel(Node) > 0)) + then + PaintNodeButton(Canvas, Node, Column, CellRect, Offsets[ofsToggleButton], ButtonY, BidiMode); // Relative X position of toggle button is needed for proper BiDi calculation -procedure TBaseVirtualTree.RepaintNode(Node: PVirtualNode); + if ImageInfo[iiCheck].Index > -1 then + PaintCheckImage(Canvas, PaintInfo.ImageInfo[iiCheck], vsSelected in PaintInfo.Node.States); + end; -// Causes an immediate repaint of the given node. + if ImageInfo[iiState].Index > -1 then + PaintImage(PaintInfo, iiState, False); + if ImageInfo[iiNormal].Index > -1 then + PaintImage(PaintInfo, iiNormal, True); -var - R: Trect; + // Now let descendants or applications draw whatever they want, + // but don't draw the node if it is currently being edited. + if not ((tsEditing in FStates) and (Node = FFocusedNode) and + ((Column = FEditColumn) or not UseColumns)) then + DoPaintNode(PaintInfo); -begin - if Assigned(Node) and (Node <> FRoot) then - begin - R := GetDisplayRect(Node, NoColumn, False); - RedrawWindow(Handle, @R, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE or RDW_VALIDATE or RDW_NOCHILDREN); - end; -end; + DoAfterCellPaint(Canvas, Node, Column, CellRect); + end; + end; -//---------------------------------------------------------------------------------------------------------------------- + // leave after first run if columns aren't used + if not UseColumns then + Break; + end + else + NextColumn := GetNextVisibleColumn(PaintInfo.Column); -procedure TBaseVirtualTree.ResetNode(Node: PVirtualNode); + SelectClipRgn(PaintInfo.Canvas.Handle, 0); + // Stop column loop if there are no further columns in the given window. + if (PaintInfo.CellRect.Left >= Window.Right) or (NextColumn = InvalidColumn) then + Break; -// Deletes all children of the given node and marks it as being uninitialized. + // Move on to next column which might not be the one immediately following the current one + // because of auto span feature. + PaintInfo.Position := Items[NextColumn].Position; -begin - DoCancelEdit; - if (Node = nil) or (Node = FRoot) then - Clear - else - begin - DoReset(Node); - DeleteChildren(Node); - // Remove initialized and other dynamic styles, keep persistent styles. - Node.States := Node.States - [vsInitialized, vsChecking, vsCutOrCopy, vsDeleting, vsHasChildren, vsExpanded, - vsHeightMeasured]; - InvalidateNode(Node); - end; -end; + // Move clip rectangle and continue. + if coVisible in Items[NextColumn].Options then + with PaintInfo do + begin + TVirtualTreeColumnCracker(Items[NextColumn]).GetAbsoluteBounds(CellRect.Left, CellRect.Right); + CellRect.Bottom := Node.NodeHeight; + ContentRect.Bottom := Node.NodeHeight; + end; + end; + end; -//---------------------------------------------------------------------------------------------------------------------- + // This node is finished, notify descendants/application. + with PaintInfo do + begin + DoAfterItemPaint(Canvas, Node, R); -procedure TBaseVirtualTree.SaveToFile(const FileName: TFileName); + // Final touch for this node: mark it if it is the current drop target node. + if (Node = FDropTargetNode) and (toShowDropmark in FOptions.PaintOptions) and + (poDrawDropMark in PaintOptions) then + DoPaintDropMark(Canvas, Node, R); + end; + end; // if not DoBeforeItemPaint() (no custom drawing) -// Saves the entire content of the tree into a file (see further notes in SaveToStream). -var - FileStream: TFileStream; + with PaintInfo.Canvas do + begin + if DrawSelectionRect then + begin + PaintSelectionRectangle(PaintInfo.Canvas, Window.Left, SelectionRect, Rect(0, 0, PaintWidth, + CurrentNodeHeight)); + end; + + // Put the constructed node image onto the target canvas. + if not (poUnbuffered in PaintOptions) then + with TWithSafeRect(TargetRect), NodeBitmap do + BitBlt(TargetCanvas.Handle, Left, Top, Width, Height, Canvas.Handle, Window.Left, 0, SRCCOPY); + end; + end; -begin - FileStream := TFileStream.Create(FileName, fmCreate); - try - SaveToStream(FileStream); - finally - FileStream.Free; - end; -end; + Inc(TargetRect.Top, PaintInfo.Node.NodeHeight); + if TargetRect.Top >= MaximumBottom then + Break; -//---------------------------------------------------------------------------------------------------------------------- + // Keep selection rectangle coordinates in sync. + if DrawSelectionRect then + OffsetRect(SelectionRect, 0, -PaintInfo.Node.NodeHeight); -procedure TBaseVirtualTree.SaveToStream(Stream: TStream; Node: PVirtualNode = nil); + // Advance to next visible node. + PaintInfo.Node := GetNextVisible(PaintInfo.Node, True); + end; + end; -// Saves Node and all its children to Stream. If Node is nil then all top level nodes will be stored. -// Note: You should be careful about assuming what is actually saved. The problem here is that we are dealing with -// virtual data. The tree can so not know what it has to save. The only fact we reliably know is the tree's -// structure. To be flexible for future enhancements as well as unknown content (unknown to the tree class which -// is saving/loading the stream) a chunk based approach is used here. Every tree class handles only those -// chunks which are not handled by an anchestor class and are known by the class. -// -// The base tree class saves only the structure of the tree along with application provided data. descendants may -// optionally add their own chunks to store additional information. See: WriteChunks. + // Erase rest of window not covered by a node. + if TargetRect.Top < MaximumBottom then + begin + // Keep the horizontal target position to determine the selection rectangle offset later (if necessary). + BaseOffset := Target.X; + Target := TargetRect.TopLeft; + R := Rect(TargetRect.Left, 0, TargetRect.Left, MaximumBottom - Target.Y); + TargetRect := Rect(0, 0, MaximumRight - Target.X, MaximumBottom - Target.Y); -var - Count: Cardinal; + if not (poUnbuffered in PaintOptions) then + begin + // Avoid unnecessary copying of bitmap content. This will destroy the DC handle too. + NodeBitmap.Height := 0; + NodeBitmap.PixelFormat := pf32Bit; + NodeBitmap.SetSize(TargetRect.Right - TargetRect.Left, TargetRect.Bottom - TargetRect.Top); + end; -begin - Stream.Write(MagicID, SizeOf(MagicID)); - if Node = nil then - begin - // Keep number of top level nodes for easy restauration. - Count := FRoot.ChildCount; - Stream.WriteBuffer(Count, SizeOf(Count)); + // Call back application/descendants whether they want to erase this area. + if not DoPaintBackground(PaintInfo.Canvas, TargetRect) then + begin + if UseBackground then + begin + SetCanvasOrigin(PaintInfo.Canvas, 0, 0); + if toStaticBackground in TreeOptions.PaintOptions then + StaticBackground(FBackground, PaintInfo.Canvas, Target, TargetRect, FColors.BackGroundColor) + else + TileBackground(FBackground, PaintInfo.Canvas, Target, TargetRect, FColors.BackGroundColor); + end + else + begin + // Consider here also colors of the columns. + SetCanvasOrigin(PaintInfo.Canvas, Target.X, 0); // This line caused issue #313 when it was placed above the if-statement + if UseColumns then + begin + with FHeader.Columns do + begin + // If there is no content in the tree then the first column has not yet been determined. + if FirstColumn = InvalidColumn then + begin + FirstColumn := GetFirstVisibleColumn; + repeat + if FirstColumn <> InvalidColumn then + begin + R.Left := Items[FirstColumn].Left; + R.Right := R.Left + Items[FirstColumn].Width; + if R.Right > TargetRect.Left then + Break; + FirstColumn := GetNextVisibleColumn(FirstColumn); + end; + until FirstColumn = InvalidColumn; + end + else + begin + R.Left := Items[FirstColumn].Left; + R.Right := R.Left + Items[FirstColumn].Width; + end; - // Save entire tree here. - Node := FRoot.FirstChild; - while Assigned(Node) do - begin - WriteNode(Stream, Node); - Node := Node.NextSibling; - end; - end - else - begin - Count := 1; - Stream.WriteBuffer(Count, SizeOf(Count)); - WriteNode(Stream, Node); - end; - if Assigned(FOnSaveTree) then - FOnSaveTree(Self, Stream); -end; + // Initialize MaxRight. + MaxRight := Target.X - 1; -//---------------------------------------------------------------------------------------------------------------------- + PaintInfo.Canvas.Font.Color := FColors.GridLineColor; + while (FirstColumn <> InvalidColumn) and (MaxRight < TargetRect.Right + Target.X) do + begin + // Determine left and right coordinate of the current column + ColLeft := Items[FirstColumn].Left; + ColRight := (ColLeft + Items[FirstColumn].Width); -function TBaseVirtualTree.ScrollIntoView(Node: PVirtualNode; Center: Boolean; Horizontally: Boolean = False): Boolean; + // Check wether this column needs to be painted at all. + if (ColRight >= MaxRight) then + begin + R.Left := MaxRight; // Continue where we left off + R.Right := ColRight; // Paint to the right of the column + MaxRight := ColRight; // And record were to start the next column. -// Scrolls the tree so that the given node is in the client area and returns True if the tree really has been -// scrolled (e.g. to avoid further updates) else returns False. If extened focus is enabled then the tree will also -// be horizontally scrolled if needed. -// Note: All collapsed parents of the node are expanded. + if (poGridLines in PaintOptions) and + (toFullVertGridLines in FOptions.PaintOptions) and + (toShowVertGridLines in FOptions.PaintOptions) and + (not (hoAutoResize in FHeader.Options) or (Cardinal(FirstColumn) < TColumnPosition(Count - 1))) then + begin + DrawDottedVLine(PaintInfo, R.Top, R.Bottom, R.Right - 1); + Dec(R.Right); + end; -var - R: TRect; - Run: PVirtualNode; - UseColumns, - HScrollBarVisible: Boolean; - ScrolledVertically, - ScrolledHorizontally: Boolean; + if not (coParentColor in Items[FirstColumn].Options) then + PaintInfo.Canvas.Brush.Color := Items[FirstColumn].Color + else + PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor; + PaintInfo.Canvas.FillRect(R); + end; + FirstColumn := GetNextVisibleColumn(FirstColumn); + end; -begin - ScrolledVertically := False; - ScrolledHorizontally := False; + // Erase also the part of the tree not covert by a column. + if R.Right < TargetRect.Right + Target.X then + begin + R.Left := R.Right; + R.Right := TargetRect.Right + Target.X; + // Prevent erasing the last vertical grid line. + if (poGridLines in PaintOptions) and + (toFullVertGridLines in FOptions.PaintOptions) and (toShowVertGridLines in FOptions.PaintOptions) and + (not (hoAutoResize in FHeader.Options)) then + Inc(R.Left); + PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor; + PaintInfo.Canvas.FillRect(R); + end; + end; + SetCanvasOrigin(PaintInfo.Canvas, 0, 0); + end + else + begin + // No columns nor bitmap background. Simply erase it with the tree color. + SetCanvasOrigin(PaintInfo.Canvas, 0, 0); + PaintInfo.Canvas.Brush.Color := FColors.BackGroundColor; + PaintInfo.Canvas.FillRect(TargetRect); + end; + end; + end; + SetCanvasOrigin(PaintInfo.Canvas, 0, 0); - if Assigned(Node) and (Node <> FRoot) and HandleAllocated then // We don't want to create the handle if it has not yet been created, see issue #897 - begin - // Make sure all parents of the node are expanded. - Run := Node.Parent; - while Run <> FRoot do - begin - if not (vsExpanded in Run.States) then - ToggleNode(Run); - Run := Run.Parent; - end; - UseColumns := FHeader.UseColumns; - if UseColumns and FHeader.Columns.IsValidColumn(FFocusedColumn) then - R := GetDisplayRect(Node, FFocusedColumn, not (toGridExtensions in FOptions.MiscOptions)) - else - R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.MiscOptions)); + if DrawSelectionRect then + begin + R := OrderRect(FNewSelRect); + // Remap the selection rectangle to the current window of the tree. + // Since Target has been used for other tasks BaseOffset got the left extent of the target position here. + OffsetRect(R, -Target.X + BaseOffset - Window.Left, -Target.Y + FOffsetY); + SetBrushOrigin(PaintInfo.Canvas, 0, Target.X and 1); + PaintSelectionRectangle(PaintInfo.Canvas, 0, R, TargetRect); + end; - // The returned rectangle can never be empty after the expand code above. - // 1) scroll vertically - if R.Top < 0 then - begin - if Center then - SetOffsetY(FOffsetY - R.Top + ClientHeight div 2) - else - SetOffsetY(FOffsetY - R.Top); - ScrolledVertically := True; - end - else - if (R.Bottom > ClientHeight) or Center then - begin - HScrollBarVisible := (ScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssBoth, System.UITypes.TScrollStyle.ssHorizontal]) and - (ScrollBarOptions.AlwaysVisible or (Integer(FRangeX) > ClientWidth)); - if Center then - SetOffsetY(FOffsetY - R.Bottom + ClientHeight div 2) + if not (poUnBuffered in PaintOptions) then + with Target, NodeBitmap do + BitBlt(TargetCanvas.Handle, X, Y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY); + end; + finally + PaintInfo.Canvas.Unlock; + if poUnbuffered in PaintOptions then + RestoreDC(TargetCanvas.Handle, SavedTargetDC) else - SetOffsetY(FOffsetY - R.Bottom + ClientHeight); - // When scrolling up and the horizontal scroll appears because of the operation - // then we have to move up the node the horizontal scrollbar's height too - // in order to avoid that the scroll bar hides the node which we wanted to have in view. - if not UseColumns and not HScrollBarVisible and (Integer(FRangeX) > ClientWidth) then - SetOffsetY(FOffsetY - GetSystemMetrics(SM_CYHSCROLL)); - ScrolledVertically := True; + NodeBitmap.Free; + end;//try..finally + + if (FEmptyListMessage <> '') and ((ChildCount[nil] = 0) or (GetFirstVisible = nil)) then + begin + // output a message if no items are to display + Canvas.Font := Self.Font; + SetBkMode(TargetCanvas.Handle, TRANSPARENT); + R.Left := OffSetX + 2; + R.Top := 2; + R.Right := R.Left + Width - 2; + R.Bottom := Height -2; + TargetCanvas.Font.Color := clGrayText; + TargetCanvas.TextRect(R, FEmptyListMessage, [tfNoClip, tfLeft, tfWordBreak, tfExpandTabs]); end; - if Horizontally then - // 2) scroll horizontally - // Center only if there is enough space for the focused column, otherwise left align, see issue #397. - ScrolledHorizontally := ScrollIntoView(FFocusedColumn, Center and (R.Width <= (ClientWidth - Header.Columns.GetVisibleFixedWidth)), Node); + DoAfterPaint(TargetCanvas); + finally + DoStateChange([], [tsPainting]); + end; end; - - Result := ScrolledVertically or ScrolledHorizontally; end; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.ScaledPixels(pPixels: Integer): Integer; - - /// Returns the given pixels scaled to the current dpi assuming that we designed at 96dpi (100%) -begin - Result := MulDiv(pPixels, {$if CompilerVersion > 31}Self.FCurrentPPI{$else}Screen.PixelsPerInch{$ifend}, 96); -end; - -function TBaseVirtualTree.ScrollIntoView(Column: TColumnIndex; Center: Boolean; Node: PVirtualNode = nil): Boolean; +function TBaseVirtualTree.PasteFromClipboard: Boolean; -// Scrolls the columns so that the given column is in the client area and returns True if the columns really have been -// scrolled (e.g. to avoid further updates) else returns False. +// Reads what is currently on the clipboard into the tree (if the format is supported). +// Note: If the application wants to have text or special formats to be inserted then it must implement +// its own code (OLE). Here only the native tree format is accepted. var - ColumnLeft, - ColumnRight: Integer; - NewOffset: Integer; - R: TRect; + Data: IDataObject; + Source: TBaseVirtualTree; begin Result := False; - - if FHeader.UseColumns and FHeader.Columns.IsValidColumn(Column) then begin - ColumnLeft := Header.Columns.Items[Column].Left; - ColumnRight := ColumnLeft + Header.Columns.Items[Column].Width; - end else if Assigned(Node) and (toCenterScrollIntoView in FOptions.SelectionOptions) then begin - Center := False; - R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.MiscOptions)); - ColumnLeft := R.Left; - ColumnRight := R.Right; - end else - Exit; - - NewOffset := FEffectiveOffsetX; - if not (FHeader.UseColumns and (coFixed in Header.Columns[Column].Options)) and (not Center) then - begin - if ColumnRight > ClientWidth then - NewOffset := FEffectiveOffsetX + Min(ColumnRight - ClientWidth, - - (Header.Columns.GetVisibleFixedWidth - ColumnLeft)) - else if ColumnLeft < Header.Columns.GetVisibleFixedWidth then - NewOffset := FEffectiveOffsetX - (Header.Columns.GetVisibleFixedWidth - ColumnLeft); - if NewOffset <> FEffectiveOffsetX then - begin - if UseRightToLeftAlignment then - SetOffsetX(-Integer(FRangeX) + ClientWidth + NewOffset) - else - SetOffsetX(-NewOffset); - end; - Result := True; - end - else if Center then + if not (toReadOnly in FOptions.MiscOptions) then begin - NewOffset := FEffectiveOffsetX + ColumnLeft - (Header.Columns.GetVisibleFixedWidth div 2) - (ClientWidth div 2) + ((ColumnRight - ColumnLeft) div 2); - if NewOffset <> FEffectiveOffsetX then + if OleGetClipboard(Data) <> S_OK then + ShowError(SClipboardFailed, hcTFClipboardFailed) + else begin - if UseRightToLeftAlignment then - SetOffsetX(-Integer(FRangeX) + ClientWidth + NewOffset) - else - SetOffsetX(-NewOffset); + // Try to get the source tree of the operation to optimize the operation. + Source := GetTreeFromDataObject(Data); + Result := ProcessOLEData(Source, Data, FFocusedNode, FDefaultPasteMode, Assigned(Source) and + (tsCutPending in Source.FStates)); + if Assigned(Source) then + begin + if Source <> Self then + Source.FinishCutOrCopy + else + DoStateChange([], [tsCutPending]); + end; end; - Result := True; - end + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.SelectAll(VisibleOnly: Boolean); +procedure TBaseVirtualTree.PrepareDragImage(HotSpot: TPoint; const DataObject: IDataObject); -// Select all nodes in the tree. -// If VisibleOnly is True then only visible nodes are selected. +// Initiates an image drag operation. HotSpot is the position of the mouse in client coordinates. var - Run: PVirtualNode; - NextFunction: TGetNextNodeProc; + PaintOptions: TVTInternalPaintOptions; + TreeRect, + PaintRect: TRect; + LocalSpot, + ImagePos, + PaintTarget: TPoint; + Image: TBitmap; + begin - if not FSelectionLocked and (toMultiSelect in FOptions.SelectionOptions) then + if CanShowDragImage then begin - ClearTempCache; - if VisibleOnly then - begin - Run := GetFirstVisible(nil, True); - NextFunction := GetNextVisible; - end - else + // Determine the drag rectangle which is a square around the hot spot. Operate in virtual tree space. + LocalSpot := HotSpot; + Dec(LocalSpot.X, -FEffectiveOffsetX); + Dec(LocalSpot.Y, FOffsetY); + TreeRect := Rect(LocalSpot.X - FDragWidth div 2, LocalSpot.Y - FDragHeight div 2, LocalSpot.X + FDragWidth div 2, + LocalSpot.Y + FDragHeight div 2); + + // Check that we have a valid rectangle. + PaintRect := TreeRect; + with TWithSafeRect(TreeRect) do begin - Run := GetFirst; - NextFunction := GetNext; + if Left < 0 then + begin + PaintTarget.X := -Left; + PaintRect.Left := 0; + end + else + PaintTarget.X := 0; + if Top < 0 then + begin + PaintTarget.Y := -Top; + PaintRect.Top := 0; + end + else + PaintTarget.Y := 0; end; - BeginUpdate(); // Improve performance, see issue #690 + + Image := TBitmap.Create; + with Image do try - while Assigned(Run) do - begin - if not(vsSelected in Run.States) then - InternalCacheNode(Run); - Run := NextFunction(Run); - end;//while - if FTempNodeCount > 0 then - AddToSelection(FTempNodeCache, FTempNodeCount); - ClearTempCache; + PixelFormat := pf32Bit; + SetSize(TreeRect.Right - TreeRect.Left, TreeRect.Bottom - TreeRect.Top); + // Erase the entire image with the color key value, for the case not everything + // in the image is covered by the tree image. + Canvas.Brush.Color := FColors.BackGroundColor; + Canvas.FillRect(Rect(0, 0, Width, Height)); + + PaintOptions := [poDrawSelection, poSelectedOnly]; + if FDragImageKind = diMainColumnOnly then + Include(PaintOptions, poMainOnly); + PaintTree(Image.Canvas, PaintRect, PaintTarget, PaintOptions); + + // Once we have got the drag image we can convert all necessary coordinates into screen space. + OffsetRect(TreeRect, -FEffectiveOffsetX, FOffsetY); + ImagePos := ClientToScreen(TreeRect.TopLeft); + HotSpot := ClientToScreen(HotSpot); + + FDragImage.ColorKey := FColors.BackGroundColor; + FDragImage.PrepareDrag(Image, ImagePos, HotSpot, DataObject); finally - EndUpdate(); - end;//try..finally - Invalidate; + Image.Free; + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.Sort(Node: PVirtualNode; Column: TColumnIndex; Direction: TSortDirection; DoInit: Boolean = True); - -// Sorts the given node. The application is queried about how to sort via the OnCompareNodes event. -// Column is simply passed to the the compare function so the application can also sort in a particular column. -// In order to free the application from taking care about the sort direction the parameter Direction is used. -// This way the application can always sort in increasing order, while this method reorders nodes according to this flag. - - //--------------- local functions ------------------------------------------- - - function MergeAscending(A, B: PVirtualNode): PVirtualNode; +procedure TBaseVirtualTree.Print(Printer: TPrinter; PrintHeader: Boolean); - // Merges A and B (which both must be sorted via Compare) into one list. +var + SaveTreeFont: TFont; // Remembers the tree's current font. + SaveHeaderFont: TFont; // Remembers the header's current font. + ImgRect, // Describes the dimensions of Image. + TreeRect, // The total VTree dimensions. + DestRect, // Dimensions of PrinterImage. + SrcRect: TRect; // Clip dimensions from Image -> PrinterImage + P: TPoint; // Used by PaintTree. + Options: TVTInternalPaintOptions; // Used by PaintTree. + Image, // Complete Tree is drawn to this image. + PrinterImage: TBitmap; // This is the image that gets printed. + SaveColor: TColor; // Remembers the VTree Color. + pTxtHeight, // Height of font in the TPrinter.Canvas + vTxtHeight, // Height of font in the VTree Canvas + vPageWidth, + vPageHeight, // Printer height in VTree resolution + xPageNum, yPageNum, // # of pages (except the occasional last one) + xPage, yPage: Integer; // Loop counter + Scale: Extended; // Scale factor between Printer Canvas and VTree Canvas + LogFont: TLogFont; - var - Dummy: TVirtualNode; - CompareResult: Integer; +begin + if Assigned(Printer) then begin - // This avoids checking for Result = nil in the loops. - Result := @Dummy; - while Assigned(A) and Assigned(B) do - begin - if OperationCanceled then - CompareResult := 0 - else - CompareResult := DoCompare(A, B, Column); - - if CompareResult <= 0 then - begin - Result.NextSibling := A; - Result := A; - A := A.NextSibling; - end - else - begin - Result.NextSibling := B; - Result := B; - B := B.NextSibling; - end; - end; - - // Just append the list which is not nil (or set end of result list to nil if both lists are nil). - if Assigned(A) then - Result.NextSibling := A - else - Result.NextSibling := B; - // return start of the new merged list - Result := Dummy.NextSibling; - end; - - //--------------------------------------------------------------------------- - - function MergeDescending(A, B: PVirtualNode): PVirtualNode; + BeginUpdate; - // Merges A and B (which both must be sorted via Compare) into one list. + // Grid lines are the only parts which are desirable when printing. + Options := [poGridLines]; - var - Dummy: TVirtualNode; - CompareResult: Integer; + // Remember the tree font. + SaveTreeFont := TFont.Create; + SaveTreeFont.Assign(Font); + // Create a new font for printing which does not use clear type output (but is antialiased, if possible) + // and which has the highest possible quality. + GetObject(Font.Handle, SizeOf(TLogFont), @LogFont); + LogFont.lfQuality := ANTIALIASED_QUALITY; + Font.Handle := CreateFontIndirect(LogFont); - begin - // this avoids checking for Result = nil in the loops - Result := @Dummy; - while Assigned(A) and Assigned(B) do - begin - if OperationCanceled then - CompareResult := 0 - else - CompareResult := DoCompare(A, B, Column); + // Create an image that will hold the complete VTree + Image := TBitmap.Create; + Image.PixelFormat := pf32Bit; + PrinterImage := nil; + try + TreeRect := GetTreeRect; - if CompareResult >= 0 then + Image.Width := TreeRect.Right - TreeRect.Left; + P := Point(0, 0); + if (hoVisible in FHeader.Options) and PrintHeader then begin - Result.NextSibling := A; - Result := A; - A := A.NextSibling; - end - else + Inc(TreeRect.Bottom, FHeader.Height); + Inc(P.Y, FHeader.Height); + end; + Image.Height := TreeRect.Bottom - TreeRect.Top; + + ImgRect.Left := 0; + ImgRect.Top := 0; + ImgRect.Right := Image.Width; + + // Force the background to white color during the rendering. + SaveColor := FColors.BackGroundColor; + Color := clWhite; + // Print header if it is visible. + if (hoVisible in FHeader.Options) and PrintHeader then begin - Result.NextSibling := B; - Result := B; - B := B.NextSibling; + SaveHeaderFont := TFont.Create; + try + SaveHeaderFont.Assign(FHeader.Font); + // Create a new font for printing which does not use clear type output (but is antialiased, if possible) + // and which has the highest possible quality. + GetObject(FHeader.Font.Handle, SizeOf(TLogFont), @LogFont); + LogFont.lfQuality := ANTIALIASED_QUALITY; + FHeader.Font.Handle := CreateFontIndirect(LogFont); + ImgRect.Bottom := FHeader.Height; + FHeader.Columns.PaintHeader(Image.Canvas.Handle, ImgRect, 0); + FHeader.Font := SaveHeaderFont; + finally + SaveHeaderFont.Free; + end; end; - end; + // The image's height is already adjusted for the header if it is visible. + ImgRect.Bottom := Image.Height; - // Just append the list which is not nil (or set end of result list to nil if both lists are nil). - if Assigned(A) then - Result.NextSibling := A - else - Result.NextSibling := B; - // Return start of the newly merged list. - Result := Dummy.NextSibling; - end; + PaintTree(Image.Canvas, ImgRect, P, Options, pf32Bit); + Color := SaveColor; - //--------------------------------------------------------------------------- + // Activate the printer + Printer.BeginDoc; + Printer.Canvas.Font := Font; - function MergeSortAscending(var Node: PVirtualNode; N: Cardinal): PVirtualNode; + // Now we can calculate the scaling : + pTxtHeight := Printer.Canvas.TextHeight('Tj'); + vTxtHeight := Canvas.TextHeight('Tj'); - // Sorts the list of nodes given by Node (which must not be nil). + Scale := pTxtHeight / vTxtHeight; - var - A, B: PVirtualNode; + // Create an Image that has the same dimensions as the printer canvas but + // scaled to the VTree resolution: + PrinterImage := TBitmap.Create; - begin - if N > 1 then - begin - A := MergeSortAscending(Node, N div 2); - B := MergeSortAscending(Node, (N + 1) div 2); - Result := MergeAscending(A, B); - end - else - begin - Result := Node; - Node := Node.NextSibling; - Result.NextSibling := nil; - end; - end; + vPageHeight := Round(Printer.PageHeight / Scale); + vPageWidth := Round(Printer.PageWidth / Scale); - //--------------------------------------------------------------------------- + // We do a minumum of one page. + xPageNum := Trunc(Image.Width / vPageWidth); + yPageNum := Trunc(Image.Height / vPageHeight); - function MergeSortDescending(var Node: PVirtualNode; N: Cardinal): PVirtualNode; + PrinterImage.SetSize(vPageWidth, vPageHeight); - // Sorts the list of nodes given by Node (which must not be nil). + // Split vertically: + for yPage := 0 to yPageNum do + begin + DestRect.Left := 0; + DestRect.Top := 0; + DestRect.Right := PrinterImage.Width; + DestRect.Bottom := PrinterImage.Height; - var - A, B: PVirtualNode; + // Split horizontally: + for xPage := 0 to xPageNum do + begin + SrcRect.Left := vPageWidth * xPage; + SrcRect.Top := vPageHeight * yPage; + SrcRect.Right := vPageWidth * xPage + PrinterImage.Width; + SrcRect.Bottom := SrcRect.Top + vPageHeight; - begin - if N > 1 then - begin - A := MergeSortDescending(Node, N div 2); - B := MergeSortDescending(Node, (N + 1) div 2); - Result := MergeDescending(A, B); - end - else - begin - Result := Node; - Node := Node.NextSibling; - Result.NextSibling := nil; + // Clear the image + PrinterImage.Canvas.Brush.Color := clWhite; + PrinterImage.Canvas.FillRect(Rect(0, 0, PrinterImage.Width, PrinterImage.Height)); + PrinterImage.Canvas.CopyRect(DestRect, Image.Canvas, SrcRect); + PrtStretchDrawDIB(Printer.Canvas, Rect(0, 0, Printer.PageWidth, Printer.PageHeight - 1), PrinterImage); + if xPage <> xPageNum then + Printer.NewPage; + end; + if yPage <> yPageNum then + Printer.NewPage; + end; + + // Restore tree font. + Font := SaveTreeFont; + SaveTreeFont.Free; + Printer.EndDoc; + finally + PrinterImage.Free; + Image.Free; + EndUpdate; end; end; +end; - //--------------- end local functions --------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- + +function TBaseVirtualTree.ProcessDrop(const DataObject: IDataObject; TargetNode: PVirtualNode; var Effect: Integer; + Mode: TVTNodeAttachMode): Boolean; + +// Recreates the (sub) tree structure serialized into memory and provided by DataObject. The new nodes are attached to +// the passed node or FRoot if TargetNode is nil. +// Returns True on success, i.e. the CF_VIRTUALTREE format is supported by the data object and the structure could be +// recreated, otherwise False. var - Run: PVirtualNode; - Index: Cardinal; + Source: TBaseVirtualTree; begin - InterruptValidation; - if tsEditPending in FStates then - begin - StopTimer(EditTimer); - DoStateChange([], [tsEditPending]); - end; - - if not (tsEditing in FStates) or DoEndEdit then + Result := False; + if Mode = amNoWhere then + Effect := DROPEFFECT_NONE + else begin - if Node = nil then - Node := FRoot; - if vsHasChildren in Node.States then - begin - if (Node.ChildCount = 0) and DoInit then - InitChildren(Node); - // Make sure the children are valid, so they can be sorted at all. - if DoInit and (Node.ChildCount > 0) then - ValidateChildren(Node, False); - // Child count might have changed. - if Node.ChildCount > 1 then - begin - StartOperation(okSortNode); - try - // Sort the linked list, check direction flag only once. - if Direction = sdAscending then - Node.FirstChild := MergeSortAscending(Node.FirstChild, Node.ChildCount) + BeginUpdate; + // try to get the source tree of the operation + Source := GetTreeFromDataObject(DataObject); + if Assigned(Source) then + Source.BeginUpdate; + try + try + // Before adding the new nodes try to optimize the operation if source and target tree reside in + // the same application and operation is a move. + if ((Effect and DROPEFFECT_MOVE) <> 0) and Assigned(Source) then + begin + // If both copy and move are specified then prefer a copy because this is not destructing. + Result := ProcessOLEData(Source, DataObject, TargetNode, Mode, (Effect and DROPEFFECT_COPY) = 0); + // Since we made an optimized move or a copy there's no reason to act further after DoDragging returns. + Effect := DROPEFFECT_NONE; + end + else + // Act only if move or copy operation is requested. + if (Effect and (DROPEFFECT_MOVE or DROPEFFECT_COPY)) <> 0 then + Result := ProcessOLEData(Source, DataObject, TargetNode, Mode, False) else - Node.FirstChild := MergeSortDescending(Node.FirstChild, Node.ChildCount); - finally - EndOperation(okSortNode); - end; - // Consolidate the child list finally. - Run := Node.FirstChild; - Run.PrevSibling := nil; - Index := 0; - repeat - Run.Index := Index; - Inc(Index); - if Run.NextSibling = nil then - Break; - Run.NextSibling.PrevSibling := Run; - Run := Run.NextSibling; - until False; - Node.LastChild := Run; - - InvalidateCache; - end; - if FUpdateCount = 0 then - begin - ValidateCache; - Invalidate; + Result := False; + except + Effect := DROPEFFECT_NONE; end; + finally + if Assigned(Source) then + Source.EndUpdate; + EndUpdate; end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.SortTree(Column: TColumnIndex; Direction: TSortDirection; DoInit: Boolean = True); +type + // needed to handle OLE global memory objects + TOLEMemoryStream = class(TCustomMemoryStream) + public + function Write(const Buffer; Count: Integer): Integer; override; + end; - //--------------- local function -------------------------------------------- +//---------------------------------------------------------------------------------------------------------------------- - procedure DoSort(Node: PVirtualNode); +function TOLEMemoryStream.Write(const Buffer; Count: Integer): Integer; - // Recursively sorts Node and its child nodes. +begin + raise EStreamError.CreateRes(PResStringRec(@SCantWriteResourceStreamError)); +end; - var - Run: PVirtualNode; +//----------------- TBaseVirtualTree ----------------------------------------------------------------------------- - begin - Sort(Node, Column, Direction, DoInit); - // Recurse to next level - Run := Node.FirstChild; - while Assigned(Run) and not FOperationCanceled do - begin - if DoInit and not (vsInitialized in Run.States) then - InitNode(Run); - if (vsInitialized in Run.States) and (not (toAutoSort in TreeOptions.AutoOptions) or Expanded[Run]) then // There is no need to sort collapsed branches - DoSort(Run); - Run := Run.NextSibling; - end; - end; +procedure TBaseVirtualTree.DoDrawHint(Canvas: TCanvas; Node: PVirtualNode; R: + TRect; Column: TColumnIndex); - //--------------- end local function ---------------------------------------- +begin + if Assigned(FOnDrawHint) then + FOnDrawHint(Self, Canvas, Node, R, Column); +end; + +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.DoGetHintSize(Node: PVirtualNode; Column: + TColumnIndex; var R: TRect); begin - if RootNode.TotalCount <= 2 then - Exit;//Nothing to do if there are one or zero nodes. RootNode.TotalCount is 1 if there are no nodes in the treee as the root node counts too here. - // Instead of wrapping the sort using BeginUpdate/EndUpdate simply the update counter - // is modified. Otherwise the EndUpdate call will recurse here. - Inc(FUpdateCount); - try - if Column > InvalidColumn then - begin - StartOperation(okSortTree); - try - DoSort(FRoot); - finally - EndOperation(okSortTree); - end; - end; - InvalidateCache; - finally - if FUpdateCount > 0 then - Dec(FUpdateCount); - if FUpdateCount = 0 then - begin - ValidateCache; - Invalidate; - end; - end; + if Assigned(FOnGetHintSize) then + FOnGetHintSize(Self, Node, Column, R); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); +procedure TBaseVirtualTree.DoGetHintKind(Node: PVirtualNode; Column: + TColumnIndex; var Kind: TVTHintKind); -// Changes a node's expand state to the opposite state. +begin + if Assigned(FOnGetHintKind) then + FOnGetHintKind(Self, Node, Column, Kind) + else + Kind := DefaultHintKind; +end; -var - Child, - FirstVisible: PVirtualNode; - HeightDelta, - StepsR1, - StepsR2, - Steps: Integer; - TogglingTree, - ChildrenInView, - NeedFullInvalidate, - NeedUpdate, - NodeInView, - PosHoldable, - TotalFit: Boolean; - ToggleData: TToggleAnimationData; +function TBaseVirtualTree.GetDefaultHintKind: TVTHintKind; - //--------------- local function -------------------------------------------- +begin + Result := vhkText; +end; - procedure PrepareAnimation; +//---------------------------------------------------------------------------------------------------------------------- - // Prepares ToggleData. +function TBaseVirtualTree.ProcessOLEData(Source: TBaseVirtualTree; const DataObject: IDataObject; TargetNode: PVirtualNode; + Mode: TVTNodeAttachMode; Optimized: Boolean): Boolean; - var - R: TRect; - S: Integer; - M: TToggleAnimationMode; +// Recreates the (sub) tree structure serialized into memory and provided by DataObject. The new nodes are attached to +// the passed node or FRoot if TargetNode is nil according to Mode. Optimized can be set to True if the entire operation +// happens within the same process (i.e. sender and receiver of the OLE operation are located in the same process). +// Optimize = True makes only sense if the operation to carry out is a move hence it is also the indication of the +// operation to be done here. Source is the source of the OLE data and only of use (and usually assigned) when +// an OLE operation takes place in the same application. +// Returns True on success, i.e. the CF_VIRTUALTREE format is supported by the data object and the structure could be +// recreated, otherwise False. + +var + Medium: TStgMedium; + Stream: TStream; + Data: Pointer; + Node: PVirtualNode; + Nodes: TNodeArray; + I: Integer; + Res: HRESULT; + ChangeReason: TChangeReason; +begin + Nodes := nil; + // Check the data format available by the data object. + with StandardOLEFormat do begin - with ToggleData do - begin - Window := Handle; - DC := GetDC(Handle); - - if (toShowBackground in FOptions.PaintOptions) and Assigned(FBackground.Graphic) then - Self.Brush.Style := bsClear + // Read best format. + cfFormat := CF_VIRTUALTREE; + end; + Result := DataObject.QueryGetData(StandardOLEFormat) = S_OK; + if Result and not (toReadOnly in FOptions.MiscOptions) then + begin + BeginUpdate; + Result := False; + try + if TargetNode = nil then + TargetNode := FRoot; + if TargetNode = FRoot then + begin + case Mode of + amInsertBefore: + Mode := amAddChildFirst; + amInsertAfter: + Mode := amAddChildLast; + end; + end; + + // Optimized means source is known and in the same process so we can access its pointers, which avoids duplicating + // the data while doing a serialization. Can only be used with cut'n paste and drag'n drop with move effect. + if Optimized then + begin + if tsOLEDragging in Source.FStates then + Nodes := Source.FDragSelection + else + Nodes := Source.GetSortedCutCopySet(True); + + if Mode in [amInsertBefore,amAddChildLast] then + begin + for I := 0 to High(Nodes) do + if not HasAsParent(TargetNode, Nodes[I]) then + Source.MoveTo(Nodes[I], TargetNode, Mode, False); + end + else + begin + for I := High(Nodes) downto 0 do + if not HasAsParent(TargetNode, Nodes[I]) then + Source.MoveTo(Nodes[I], TargetNode, Mode, False); + end; + Result := True; + end else begin - Self.Brush.Style := bsSolid; - Self.Brush.Color := FColors.BackGroundColor; - end; + if Source = Self then + ChangeReason := crNodeCopied + else + ChangeReason := crNodeAdded; + Res := DataObject.GetData(StandardOLEFormat, Medium); + if Res = S_OK then + begin + case Medium.tymed of + TYMED_ISTREAM, // IStream interface + TYMED_HGLOBAL: // global memory block + begin + Stream := nil; + if Medium.tymed = TYMED_ISTREAM then + Stream := TOLEStream.Create(IUnknown(Medium.stm) as IStream) + else + begin + Data := GlobalLock(Medium.hGlobal); + if Assigned(Data) then + begin + // Get the total size of data to retrieve. + I := PCardinal(Data)^; + Inc(PCardinal(Data)); + Stream := TOLEMemoryStream.Create; + TOLEMemoryStream(Stream).SetPointer(Data, I); + end; + end; + + if Assigned(Stream) then + try + while Stream.Position < Stream.Size do + begin + Node := MakeNewNode; + InternalConnectNode(Node, TargetNode, Self, Mode); + InternalAddFromStream(Stream, VTTreeStreamVersion, Node); + // This seems a bit strange because of the callback for granting to add the node + // which actually comes after the node has been added. The reason is that the node must + // contain valid data otherwise I don't see how the application can make a funded decision. + if not DoNodeCopying(Node, TargetNode) then + begin + DeleteNode(Node); + end + else + begin + DoNodeCopied(Node); + StructureChange(Node, ChangeReason); + // In order to maintain the same node order when restoring nodes in the case of amInsertAfter + // we have to move the reference node continously. Othwise we would end up with reversed node order. + if Mode = amInsertAfter then + TargetNode := Node; + end; + end; + Result := True; + finally + Stream.Free; + if Medium.tymed = TYMED_HGLOBAL then + GlobalUnlock(Medium.hGlobal); + end; + end; + end; + ReleaseStgMedium(Medium); + end; + end; + finally + EndUpdate; + end; + end; +end; - Brush := Self.Brush.Handle; +//---------------------------------------------------------------------------------------------------------------------- - if (Mode1 <> tamNoScroll) and (Mode2 <> tamNoScroll) then - begin - if StepsR1 < StepsR2 then - begin - // As the primary rectangle is always R1 we will get a much smoother - // animation if R1 is the one that will be scrolled more. - R := R2; - R2 := R1; - R1 := R; +procedure TBaseVirtualTree.ReinitChildren(Node: PVirtualNode; Recursive: Boolean); - M := Mode2; - Mode2 := Mode1; - Mode1 := M; +// Forces all child nodes of Node to be reinitialized. +// If Recursive is True then also the grandchildren are reinitialized. - S := StepsR2; - StepsR2 := StepsR1; - StepsR1 := S; - end; - ScaleFactor := StepsR2 / StepsR1; - MissedSteps := 0; - end; +var + Run: PVirtualNode; - if Mode1 <> tamNoScroll then - Steps := StepsR1 - else - Steps := StepsR2; - end; +begin + if Assigned(Node) then + begin + InitChildren(Node); + Run := Node.FirstChild; + end + else + begin + InitChildren(FRoot); + Run := FRoot.FirstChild; end; - //--------------- end local function ---------------------------------------- + while Assigned(Run) do + begin + ReinitNode(Run, Recursive); + Run := Run.NextSibling; + end; +end; -begin - Assert(Assigned(Node), 'Node must not be nil.'); +//---------------------------------------------------------------------------------------------------------------------- - TogglingTree := tsToggling in FStates; - ChildrenInView := False; - HeightDelta := 0; - NeedFullInvalidate := False; - NeedUpdate := False; - NodeInView := False; - PosHoldable := False; - TotalFit := False; +procedure TBaseVirtualTree.ReinitNode(Node: PVirtualNode; Recursive: Boolean); - // We don't need to switch the expand state if the node is being deleted otherwise some - // updates (e.g. visible node count) are done twice with disasterous results). - if [vsDeleting, vsToggling] * Node.States = [] then +// Forces the given node and all its children (if recursive is True) to be initialized again without +// modifying any data in the nodes nor deleting children (unless the application requests a different amount). + +begin + if Assigned(Node) and (Node <> FRoot) then begin - try - DoStateChange([tsToggling]); - Include(Node.States, vsToggling); + // Remove dynamic styles. + Node.States := Node.States - [vsChecking, vsCutOrCopy, vsDeleting, vsHeightMeasured]; + if vsInitialized in Node.States then + InitNode(Node); + end; - if vsExpanded in Node.States then - begin - if DoCollapsing(Node) then - begin - NeedUpdate := True; + if Recursive then + ReinitChildren(Node, True); +end; - // Calculate the height delta right now as we need it for toChildrenAbove anyway. - HeightDelta := -Integer(Node.TotalHeight) + Integer(NodeHeight[Node]); - if (FUpdateCount = 0) and (toAnimatedToggle in FOptions.AnimationOptions) and not - (tsCollapsing in FStates) then - begin - if tsHint in Self.FStates then - Application.CancelHint; - UpdateWindow(Handle); +//---------------------------------------------------------------------------------------------------------------------- - // animated collapsing - with ToggleData do - begin - // Determine the animation behaviour and rectangle. If toChildrenAbove is set, the behaviour is depending - // on the position of the node to be collapsed. - R1 := GetDisplayRect(Node, NoColumn, False); - Mode2 := tamNoScroll; - if toChildrenAbove in FOptions.PaintOptions then - begin - PosHoldable := (FOffsetY + (Integer(Node.TotalHeight) - Integer(NodeHeight[Node]))) <= 0; - NodeInView := R1.Top < ClientHeight; +procedure TBaseVirtualTree.RepaintNode(Node: PVirtualNode); - StepsR1 := 0; - if NodeInView then - begin - if PosHoldable or not (toAdvancedAnimatedToggle in FOptions.AnimationOptions) then - begin - // Scroll the child nodes down. - Mode1 := tamScrollDown; - R1.Bottom := R1.Top; - R1.Top := 0; - StepsR1 := Min(R1.Bottom - R1.Top + 1, Integer(Node.TotalHeight) - Integer(NodeHeight[Node])); - end - else - begin - // The position cannot be kept. So scroll the node up to its future position. - Mode1 := tamScrollUp; - R1.Top := Max(0, R1.Top + HeightDelta); - R1.Bottom := ClientHeight; - StepsR1 := FOffsetY - HeightDelta; - end; - end; - end - else - begin - if (Integer(FRangeY) + FOffsetY - R1.Bottom + HeightDelta >= ClientHeight - R1.Bottom) or - (Integer(FRangeY) <= ClientHeight) or (FOffsetY = 0) or not - (toAdvancedAnimatedToggle in FOptions.AnimationOptions) then - begin - // Do a simple scroll up over the child nodes. - Mode1 := tamScrollUp; - Inc(R1.Top, NodeHeight[Node]); - R1.Bottom := ClientHeight; - StepsR1 := Min(R1.Bottom - R1.Top + 1, -HeightDelta); - end - else - begin - // Scroll the node down to its future position. As FOffsetY will change we need to invalidate the - // whole tree. - Mode1 := tamScrollDown; - StepsR1 := Min(-FOffsetY, ClientHeight - Integer(FRangeY) -FOffsetY - HeightDelta); - R1.Top := 0; - R1.Bottom := Min(ClientHeight, R1.Bottom + Steps); - NeedFullInvalidate := True; - end; - end; +// Causes an immediate repaint of the given node. - // No animation necessary if the node is below the current client height. - if R1.Top < ClientHeight then - begin - PrepareAnimation; - try - Animate(Steps, FAnimationDuration, ToggleCallback, @ToggleData); - finally - ReleaseDC(Window, DC); - end; - end; - end; - end; +var + R: Trect; - // collapse the node - AdjustTotalHeight(Node, IfThen(IsEffectivelyFiltered[Node], 0, NodeHeight[Node])); - if FullyVisible[Node] then - Dec(FVisibleCount, CountVisibleChildren(Node)); - Exclude(Node.States, vsExpanded); - DoCollapsed(Node); +begin + if Assigned(Node) and (Node <> FRoot) then + begin + R := GetDisplayRect(Node, NoColumn, False); + RedrawWindow(Handle, @R, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_NOERASE or RDW_VALIDATE or RDW_NOCHILDREN); + end; +end; - // Remove child nodes now, if enabled. - if (toAutoFreeOnCollapse in FOptions.AutoOptions) and (Node.ChildCount > 0) then - begin - DeleteChildren(Node); - Include(Node.States, vsHasChildren); - end; - end; - end - else - if DoExpanding(Node) then - begin - NeedUpdate := True; - // expand the node, need to adjust the height - if not (vsInitialized in Node.States) then - InitNode(Node); - if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then - InitChildren(Node); +//---------------------------------------------------------------------------------------------------------------------- - // Avoid setting the vsExpanded style if there are no child nodes. - if Node.ChildCount > 0 then - begin - // Iterate through the child nodes without initializing them. We have to determine the entire height. - Child := Node.FirstChild; - repeat - if vsVisible in Child.States then - begin - // Ensure the item height is measured - MeasureItemHeight(Canvas, Child); +procedure TBaseVirtualTree.ResetNode(Node: PVirtualNode); - Inc(HeightDelta, Child.TotalHeight); - end; - Child := Child.NextSibling; - until Child = nil; +// Deletes all children of the given node and marks it as being uninitialized. - // Getting the display rectangle is already done here as it is needed for toChildrenAbove in any case. - if (toChildrenAbove in FOptions.PaintOptions) or (FUpdateCount = 0) then - begin - with ToggleData do - begin - R1 := GetDisplayRect(Node, NoColumn, False); - Mode2 := tamNoScroll; - TotalFit := HeightDelta + Integer(NodeHeight[Node]) <= ClientHeight; +begin + DoCancelEdit; + if (Node = nil) or (Node = FRoot) then + Clear + else + begin + DoReset(Node); + DeleteChildren(Node); + // Remove initialized and other dynamic styles, keep persistent styles. + Node.States := Node.States - [vsInitialized, vsChecking, vsCutOrCopy, vsDeleting, vsHasChildren, vsExpanded, + vsHeightMeasured]; + InvalidateNode(Node); + end; +end; - if toChildrenAbove in FOptions.PaintOptions then - begin - // The main goal with toChildrenAbove being set is to keep the nodes visual position so the user does - // not get confused. Therefore we need to scroll the view when the expanding is done. - PosHoldable := TotalFit and (Integer(FRangeY) - ClientHeight >= 0) ; - ChildrenInView := (R1.Top - HeightDelta) >= 0; - NodeInView := R1.Bottom <= ClientHeight; - end - else - begin - PosHoldable := TotalFit; - ChildrenInView := R1.Bottom + HeightDelta <= ClientHeight; - end; +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.SaveToFile(const FileName: TFileName); + +// Saves the entire content of the tree into a file (see further notes in SaveToStream). + +var + FileStream: TFileStream; + +begin + FileStream := TFileStream.Create(FileName, fmCreate); + try + SaveToStream(FileStream); + finally + FileStream.Free; + end; +end; - R1.Bottom := ClientHeight; - end; - end; +//---------------------------------------------------------------------------------------------------------------------- - if FUpdateCount = 0 then - begin - // Do animated expanding if enabled. - if (ToggleData.R1.Top < ClientHeight) and ([tsPainting, tsExpanding] * FStates = []) and - (toAnimatedToggle in FOptions.AnimationOptions)then - begin - if tsHint in Self.FStates then - Application.CancelHint; - UpdateWindow(Handle); - // animated expanding - with ToggleData do - begin - if toChildrenAbove in FOptions.PaintOptions then - begin - // At first check if we hold the position, which is the most common case. - if not (toAdvancedAnimatedToggle in FOptions.AnimationOptions) or - (PosHoldable and ( (NodeInView and ChildrenInView) or not - (toAutoScrollOnExpand in FOptions.AutoOptions) )) then - begin - Mode1 := tamScrollUp; - R1 := Rect(R1.Left, 0, R1.Right, R1.Top); - StepsR1 := Min(HeightDelta, R1.Bottom); - end - else - begin - // If we will not hold the node's visual position we mostly scroll in both directions. - Mode1 := tamScrollDown; - Mode2 := tamScrollUp; - R2 := Rect(R1.Left, 0, R1.Right, R1.Top); - if not (toAutoScrollOnExpand in FOptions.AutoOptions) then - begin - // If we shall not or cannot scroll to the desired extent we calculate the new position (with - // max FOffsetY applied) and animate it that way. - StepsR1 := -FOffsetY - Max(Integer(FRangeY) + HeightDelta - ClientHeight, 0) + HeightDelta; - if (Integer(FRangeY) + HeightDelta - ClientHeight) <= 0 then - Mode2 := tamNoScroll - else - StepsR2 := Min(Integer(FRangeY) + HeightDelta - ClientHeight, R2.Bottom); - end - else - begin - if TotalFit and NodeInView and (Integer(FRangeY) + HeightDelta > ClientHeight) then - begin - // If the whole subtree will fit into the client area and the node is currently fully visible, - // the first child will be made the top node if possible. - if HeightDelta >= R1.Top then - StepsR1 := Abs(R1.Top - HeightDelta) - else - StepsR1 := ClientHeight - Integer(FRangeY); - end - else - if Integer(FRangeY) + HeightDelta <= ClientHeight then - begin - // We cannot make the first child the top node as we cannot scroll to that extent, - // so we do a simple scroll down. - Mode2 := tamNoScroll; - StepsR1 := HeightDelta; - end - else - // If the subtree does not fit into the client area at once, the expanded node will - // be made the bottom node. - StepsR1 := ClientHeight - R1.Top - Integer(NodeHeight[Node]); +procedure TBaseVirtualTree.SaveToStream(Stream: TStream; Node: PVirtualNode = nil); - if Mode2 <> tamNoScroll then - begin - if StepsR1 > 0 then - StepsR2 := Min(R1.Top, HeightDelta - StepsR1) - else - begin - // If the node is already at the bottom scrolling is needed. - Mode1 := tamNoScroll; - StepsR2 := Min(HeightDelta, R1.Bottom); - end; - end; - end; - end; - end - else - begin - // toChildrenAbove is not set. - if (PosHoldable and ChildrenInView) or not (toAutoScrollOnExpand in FOptions.AutoOptions) or not - (toAdvancedAnimatedToggle in FOptions.AnimationOptions) or (R1.Top <= 0) then - begin - // If the node will stay at its visual position, do a simple down-scroll. - Mode1 := tamScrollDown; - Inc(R1.Top, NodeHeight[Node]); - StepsR1 := Min(R1.Bottom - R1.Top, HeightDelta); - end - else - begin - // We will not hold the nodes visual position so perform a double scroll. - Mode1 := tamScrollUp; - Mode2 := tamScrollDown; +// Saves Node and all its children to Stream. If Node is nil then all top level nodes will be stored. +// Note: You should be careful about assuming what is actually saved. The problem here is that we are dealing with +// virtual data. The tree can so not know what it has to save. The only fact we reliably know is the tree's +// structure. To be flexible for future enhancements as well as unknown content (unknown to the tree class which +// is saving/loading the stream) a chunk based approach is used here. Every tree class handles only those +// chunks which are not handled by an anchestor class and are known by the class. +// +// The base tree class saves only the structure of the tree along with application provided data. descendants may +// optionally add their own chunks to store additional information. See: WriteChunks. - R1.Bottom := R1.Top + Integer(NodeHeight[Node]) + 1; - R1.Top := 0; - R2 := Rect(R1.Left, R1.Bottom, R1.Right, ClientHeight); +var + Count: Cardinal; - StepsR1 := Min(HeightDelta - (ClientHeight - R2.Top), R1.Bottom - Integer(NodeHeight[Node])); - StepsR2 := ClientHeight - R2.Top; - end; - end; +begin + Stream.Write(MagicID, SizeOf(MagicID)); + if Node = nil then + begin + // Keep number of top level nodes for easy restauration. + Count := FRoot.ChildCount; + Stream.WriteBuffer(Count, SizeOf(Count)); - if ClientHeight >= R1.Top then - begin - PrepareAnimation; - try - Animate(Steps, FAnimationDuration, ToggleCallback, @ToggleData); - finally - ReleaseDC(Window, DC); - end; - end; - end; - end; - if toAutoSort in FOptions.AutoOptions then - Sort(Node, FHeader.SortColumn, FHeader.SortDirection, False); - end;// if UpdateCount = 0 + // Save entire tree here. + Node := FRoot.FirstChild; + while Assigned(Node) do + begin + WriteNode(Stream, Node); + Node := Node.NextSibling; + end; + end + else + begin + Count := 1; + Stream.WriteBuffer(Count, SizeOf(Count)); + WriteNode(Stream, Node); + end; + if Assigned(FOnSaveTree) then + FOnSaveTree(Self, Stream); +end; - Include(Node.States, vsExpanded); - AdjustTotalHeight(Node, HeightDelta, True); - if FullyVisible[Node] then - Inc(FVisibleCount, CountVisibleChildren(Node)); +//---------------------------------------------------------------------------------------------------------------------- - DoExpanded(Node); - end; - end; +function TBaseVirtualTree.ScrollIntoView(Node: PVirtualNode; Center: Boolean; Horizontally: Boolean = False): Boolean; - if NeedUpdate then - begin - InvalidateCache; - if FUpdateCount = 0 then - begin - ValidateCache; - if Node.ChildCount > 0 then - begin - UpdateRanges; - UpdateScrollBars(True); - if [tsPainting, tsExpanding] * FStates = [] then - begin - if (vsExpanded in Node.States) and ((toAutoScrollOnExpand in FOptions.AutoOptions) or - (toChildrenAbove in FOptions.PaintOptions)) then - begin - if toChildrenAbove in FOptions.PaintOptions then - begin - NeedFullInvalidate := True; - if (PosHoldable and ChildrenInView and NodeInView) or not - (toAutoScrollOnExpand in FOptions.AutoOptions) then - SetOffsetY(FOffsetY - Integer(HeightDelta)) - else - if TotalFit and NodeInView then - begin - FirstVisible := GetFirstVisible(Node, True); - if Assigned(FirstVisible) then // otherwise there is no visible child at all - SetOffsetY(FOffsetY - GetDisplayRect(FirstVisible, NoColumn, False).Top); - end - else - BottomNode := Node; - end - else - begin - // Scroll as much child nodes into view as possible if the node has been expanded. - if PosHoldable then - NeedFullInvalidate := ScrollIntoView(GetLastVisible(Node, True), False) - else - begin - TopNode := Node; - NeedFullInvalidate := True; - end; - end; - end - else - begin - // If we have collapsed the node or toAutoScrollOnExpand is not set, we try to keep the nodes - // visual position. - if toChildrenAbove in FOptions.PaintOptions then - SetOffsetY(FOffsetY - Integer(HeightDelta)); - NeedFullInvalidate := True; - end; - end; +// Scrolls the tree so that the given node is in the client area and returns True if the tree really has been +// scrolled (e.g. to avoid further updates) else returns False. If extened focus is enabled then the tree will also +// be horizontally scrolled if needed. +// Note: All collapsed parents of the node are expanded. - //UpdateScrollBars(True); Moved up +var + R: TRect; + Run: PVirtualNode; + UseColumns, + HScrollBarVisible: Boolean; + ScrolledVertically, + ScrolledHorizontally: Boolean; - // Check for automatically scrolled tree. - if NeedFullInvalidate then - Invalidate - else - InvalidateToBottom(Node); - end - else - InvalidateNode(Node); - end +begin + ScrolledVertically := False; + ScrolledHorizontally := False; + + if Assigned(Node) and (Node <> FRoot) and HandleAllocated then // We don't want to create the handle if it has not yet been created, see issue #897 + begin + // Make sure all parents of the node are expanded. + Run := Node.Parent; + while Run <> FRoot do + begin + if not (vsExpanded in Run.States) then + ToggleNode(Run); + Run := Run.Parent; + end; + UseColumns := FHeader.UseColumns; + if UseColumns and FHeader.Columns.IsValidColumn(FFocusedColumn) then + R := GetDisplayRect(Node, FFocusedColumn, not (toGridExtensions in FOptions.MiscOptions)) + else + R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.MiscOptions)); + + // The returned rectangle can never be empty after the expand code above. + // 1) scroll vertically + if R.Top < 0 then + begin + if Center then + SetOffsetY(FOffsetY - R.Top + ClientHeight div 2) + else + SetOffsetY(FOffsetY - R.Top); + ScrolledVertically := True; + end + else + if (R.Bottom > ClientHeight) or Center then + begin + HScrollBarVisible := (ScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssBoth, System.UITypes.TScrollStyle.ssHorizontal]) and + (ScrollBarOptions.AlwaysVisible or (Integer(FRangeX) > ClientWidth)); + if Center then + SetOffsetY(FOffsetY - R.Bottom + ClientHeight div 2) else - begin - UpdateRanges; - UpdateScrollBars(True); - end; + SetOffsetY(FOffsetY - R.Bottom + ClientHeight); + // When scrolling up and the horizontal scroll appears because of the operation + // then we have to move up the node the horizontal scrollbar's height too + // in order to avoid that the scroll bar hides the node which we wanted to have in view. + if not UseColumns and not HScrollBarVisible and (Integer(FRangeX) > ClientWidth) then + SetOffsetY(FOffsetY - GetSystemMetrics(SM_CYHSCROLL)); + ScrolledVertically := True; end; - finally - Exclude(Node.States, vsToggling); - if not TogglingTree then - DoStateChange([], [tsToggling]); - end; + if Horizontally then + // 2) scroll horizontally + // Center only if there is enough space for the focused column, otherwise left align, see issue #397. + ScrolledHorizontally := ScrollIntoView(FFocusedColumn, Center and (R.Width <= (ClientWidth - Header.Columns.GetVisibleFixedWidth)), Node); end; + + Result := ScrolledVertically or ScrolledHorizontally; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.UpdateHorizontalRange; +function TBaseVirtualTree.ScaledPixels(pPixels: Integer): Integer; + /// Returns the given pixels scaled to the current dpi assuming that we designed at 96dpi (100%) begin - if FHeader.UseColumns then - SetRangeX(FHeader.Columns.TotalWidth) - else - SetRangeX(GetMaxRightExtend); + Result := MulDiv(pPixels, {$if CompilerVersion > 31}Self.FCurrentPPI{$else}Screen.PixelsPerInch{$ifend}, 96); end; -//---------------------------------------------------------------------------------------------------------------------- +function TBaseVirtualTree.ScrollIntoView(Column: TColumnIndex; Center: Boolean; Node: PVirtualNode = nil): Boolean; -procedure TBaseVirtualTree.UpdateHorizontalScrollBar(DoRepaint: Boolean); +// Scrolls the columns so that the given column is in the client area and returns True if the columns really have been +// scrolled (e.g. to avoid further updates) else returns False. var - ScrollInfo: TScrollInfo; + ColumnLeft, + ColumnRight: Integer; + NewOffset: Integer; + R: TRect; begin - UpdateHorizontalRange; + Result := False; - if IsUpdating or not HandleAllocated then + if FHeader.UseColumns and FHeader.Columns.IsValidColumn(Column) then begin + ColumnLeft := Header.Columns.Items[Column].Left; + ColumnRight := ColumnLeft + Header.Columns.Items[Column].Width; + end else if Assigned(Node) and (toCenterScrollIntoView in FOptions.SelectionOptions) then begin + Center := False; + R := GetDisplayRect(Node, NoColumn, not (toGridExtensions in FOptions.MiscOptions)); + ColumnLeft := R.Left; + ColumnRight := R.Right; + end else Exit; - // Adjust effect scroll offset depending on bidi mode. - if UseRightToLeftAlignment then - FEffectiveOffsetX := Integer(FRangeX) - ClientWidth + FOffsetX - else - FEffectiveOffsetX := -FOffsetX; - - if FScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssHorizontal, System.UITypes.TScrollStyle.ssBoth] then + NewOffset := FEffectiveOffsetX; + if not (FHeader.UseColumns and (coFixed in Header.Columns[Column].Options)) and (not Center) then begin - ZeroMemory (@ScrollInfo, SizeOf(ScrollInfo)); - ScrollInfo.cbSize := SizeOf(ScrollInfo); - ScrollInfo.fMask := SIF_ALL; - GetScrollInfo(Handle, SB_HORZ, ScrollInfo); - - if (Integer(FRangeX) > ClientWidth) or FScrollBarOptions.AlwaysVisible then + if ColumnRight > ClientWidth then + NewOffset := FEffectiveOffsetX + Min(ColumnRight - ClientWidth, + - (Header.Columns.GetVisibleFixedWidth - ColumnLeft)) + else if ColumnLeft < Header.Columns.GetVisibleFixedWidth then + NewOffset := FEffectiveOffsetX - (Header.Columns.GetVisibleFixedWidth - ColumnLeft); + if NewOffset <> FEffectiveOffsetX then begin - DoShowScrollBar(SB_HORZ, True); + if UseRightToLeftAlignment then + SetOffsetX(-Integer(FRangeX) + ClientWidth + NewOffset) + else + SetOffsetX(-NewOffset); + end; + Result := True; + end + else if Center then + begin + NewOffset := FEffectiveOffsetX + ColumnLeft - (Header.Columns.GetVisibleFixedWidth div 2) - (ClientWidth div 2) + ((ColumnRight - ColumnLeft) div 2); + if NewOffset <> FEffectiveOffsetX then + begin + if UseRightToLeftAlignment then + SetOffsetX(-Integer(FRangeX) + ClientWidth + NewOffset) + else + SetOffsetX(-NewOffset); + end; + Result := True; + end +end; - ScrollInfo.nMin := 0; - ScrollInfo.nMax := FRangeX; - ScrollInfo.nPos := FEffectiveOffsetX; - ScrollInfo.nPage := Max(0, ClientWidth + 1); +//---------------------------------------------------------------------------------------------------------------------- - ScrollInfo.fMask := SIF_ALL or ScrollMasks[FScrollBarOptions.AlwaysVisible]; - SetScrollInfo(Handle, SB_HORZ, ScrollInfo, DoRepaint); // 1 app freeze seen here in TreeSize 8.1.0 after ScaleForPpi() - if DoRepaint then - RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 +procedure TBaseVirtualTree.SelectAll(VisibleOnly: Boolean); + +// Select all nodes in the tree. +// If VisibleOnly is True then only visible nodes are selected. + +var + Run: PVirtualNode; + NextFunction: TGetNextNodeProc; +begin + if not FSelectionLocked and (toMultiSelect in FOptions.SelectionOptions) then + begin + ClearTempCache; + if VisibleOnly then + begin + Run := GetFirstVisible(nil, True); + NextFunction := GetNextVisible; end else begin - ScrollInfo.nMin := 0; - ScrollInfo.nMax := 0; - ScrollInfo.nPos := 0; - ScrollInfo.nPage := 0; - DoShowScrollBar(SB_HORZ, False); - SetScrollInfo(Handle, SB_HORZ, ScrollInfo, False); + Run := GetFirst; + NextFunction := GetNext; end; - - // Since the position is automatically changed if it doesn't meet the range - // we better read the current position back to stay synchronized. - FEffectiveOffsetX := GetScrollPos(Handle, SB_HORZ); - if UseRightToLeftAlignment then - SetOffsetX(-Integer(FRangeX) + ClientWidth + FEffectiveOffsetX) - else - SetOffsetX(-FEffectiveOffsetX); - end - else - begin - DoShowScrollBar(SB_HORZ, False); - - // Reset the current horizontal offset to account for window resize etc. - SetOffsetX(FOffsetX); + BeginUpdate(); // Improve performance, see issue #690 + try + while Assigned(Run) do + begin + if not(vsSelected in Run.States) then + InternalCacheNode(Run); + Run := NextFunction(Run); + end;//while + if FTempNodeCount > 0 then + AddToSelection(FTempNodeCache, FTempNodeCount); + ClearTempCache; + finally + EndUpdate(); + end;//try..finally + Invalidate; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.UpdateRanges; +procedure TBaseVirtualTree.Sort(Node: PVirtualNode; Column: TColumnIndex; Direction: TSortDirection; DoInit: Boolean = True); -begin - UpdateVerticalRange; - UpdateHorizontalRange; -end; +// Sorts the given node. The application is queried about how to sort via the OnCompareNodes event. +// Column is simply passed to the the compare function so the application can also sort in a particular column. +// In order to free the application from taking care about the sort direction the parameter Direction is used. +// This way the application can always sort in increasing order, while this method reorders nodes according to this flag. -//---------------------------------------------------------------------------------------------------------------------- + //--------------- local functions ------------------------------------------- -procedure TBaseVirtualTree.UpdateScrollBars(DoRepaint: Boolean); + function MergeAscending(A, B: PVirtualNode): PVirtualNode; -// adjusts scrollbars to reflect current size and paint offset of the tree + // Merges A and B (which both must be sorted via Compare) into one list. -begin - if HandleAllocated then + var + Dummy: TVirtualNode; + CompareResult: Integer; begin - UpdateVerticalScrollBar(DoRepaint); - UpdateHorizontalScrollBar(DoRepaint); - Perform(CM_UPDATE_VCLSTYLE_SCROLLBARS,0,0); + // This avoids checking for Result = nil in the loops. + Result := @Dummy; + while Assigned(A) and Assigned(B) do + begin + if OperationCanceled then + CompareResult := 0 + else + CompareResult := DoCompare(A, B, Column); + + if CompareResult <= 0 then + begin + Result.NextSibling := A; + Result := A; + A := A.NextSibling; + end + else + begin + Result.NextSibling := B; + Result := B; + B := B.NextSibling; + end; + end; + + // Just append the list which is not nil (or set end of result list to nil if both lists are nil). + if Assigned(A) then + Result.NextSibling := A + else + Result.NextSibling := B; + // return start of the new merged list + Result := Dummy.NextSibling; end; -end; -procedure TBaseVirtualTree.UpdateStyleElements; -begin - inherited; - UpdateHeaderRect; - FHeader.Columns.PaintHeader(Canvas, FHeaderRect, Point(0,0)); -end; + //--------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------------- + function MergeDescending(A, B: PVirtualNode): PVirtualNode; -procedure TBaseVirtualTree.UpdateVerticalRange; + // Merges A and B (which both must be sorted via Compare) into one list. + + var + Dummy: TVirtualNode; + CompareResult: Integer; + + begin + // this avoids checking for Result = nil in the loops + Result := @Dummy; + while Assigned(A) and Assigned(B) do + begin + if OperationCanceled then + CompareResult := 0 + else + CompareResult := DoCompare(A, B, Column); -begin - // Total node height includes the height of the invisible root node. - FRangeY := Cardinal(Int64(FRoot.TotalHeight) - FRoot.NodeHeight + FBottomSpace); -end; + if CompareResult >= 0 then + begin + Result.NextSibling := A; + Result := A; + A := A.NextSibling; + end + else + begin + Result.NextSibling := B; + Result := B; + B := B.NextSibling; + end; + end; -//---------------------------------------------------------------------------------------------------------------------- + // Just append the list which is not nil (or set end of result list to nil if both lists are nil). + if Assigned(A) then + Result.NextSibling := A + else + Result.NextSibling := B; + // Return start of the newly merged list. + Result := Dummy.NextSibling; + end; -procedure TBaseVirtualTree.UpdateVerticalScrollBar(DoRepaint: Boolean); + //--------------------------------------------------------------------------- -var - ScrollInfo: TScrollInfo; + function MergeSortAscending(var Node: PVirtualNode; N: Cardinal): PVirtualNode; -begin - UpdateVerticalRange; + // Sorts the list of nodes given by Node (which must not be nil). - if (IsUpdating) then - Exit; - Assert(GetCurrentThreadId = MainThreadId, 'UI controls like ' + Classname + ' and its scrollbars should only be manipulated through the main thread.'); + var + A, B: PVirtualNode; - if FScrollBarOptions.ScrollBars in [ssVertical, ssBoth] then begin - ScrollInfo.cbSize := SizeOf(ScrollInfo); - ScrollInfo.fMask := SIF_ALL; - GetScrollInfo(Handle, SB_VERT, ScrollInfo); - - if (Integer(FRangeY) > ClientHeight) or FScrollBarOptions.AlwaysVisible then + if N > 1 then begin - DoShowScrollBar(SB_VERT, True); - - ScrollInfo.nMin := 0; - ScrollInfo.nMax := FRangeY; - ScrollInfo.nPos := -FOffsetY; - ScrollInfo.nPage := Max(0, ClientHeight + 1); - - ScrollInfo.fMask := SIF_ALL or ScrollMasks[FScrollBarOptions.AlwaysVisible]; - SetScrollInfo(Handle, SB_VERT, ScrollInfo, DoRepaint); + A := MergeSortAscending(Node, N div 2); + B := MergeSortAscending(Node, (N + 1) div 2); + Result := MergeAscending(A, B); end else begin - ScrollInfo.nMin := 0; - ScrollInfo.nMax := 0; - ScrollInfo.nPos := 0; - ScrollInfo.nPage := 0; - DoShowScrollBar(SB_VERT, False); - SetScrollInfo(Handle, SB_VERT, ScrollInfo, False); + Result := Node; + Node := Node.NextSibling; + Result.NextSibling := nil; end; - - // Since the position is automatically changed if it doesn't meet the range - // we better read the current position back to stay synchronized. - SetOffsetY(-GetScrollPos(Handle, SB_VERT)); - end - else - begin - DoShowScrollBar(SB_VERT, False); - - // Reset the current vertical offset to account for window resize etc. - SetOffsetY(FOffsetY); end; -end; - -//---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.UseRightToLeftReading: Boolean; + //--------------------------------------------------------------------------- -// The tree can handle right-to-left reading also on non-middle-east systems, so we cannot use the same function as -// it is implemented in TControl. + function MergeSortDescending(var Node: PVirtualNode; N: Cardinal): PVirtualNode; -begin - Result := BiDiMode <> bdLeftToRight; -end; + // Sorts the list of nodes given by Node (which must not be nil). -//---------------------------------------------------------------------------------------------------------------------- + var + A, B: PVirtualNode; -procedure TBaseVirtualTree.ValidateChildren(Node: PVirtualNode; Recursive: Boolean); + begin + if N > 1 then + begin + A := MergeSortDescending(Node, N div 2); + B := MergeSortDescending(Node, (N + 1) div 2); + Result := MergeDescending(A, B); + end + else + begin + Result := Node; + Node := Node.NextSibling; + Result.NextSibling := nil; + end; + end; -// Ensures that the children of the given node (and all their children, if Recursive is True) are initialized. -// Node must already be initialized + //--------------- end local functions --------------------------------------- var - Child: PVirtualNode; + Run: PVirtualNode; + Index: Cardinal; begin - if Node = nil then - Node := FRoot; + InterruptValidation; + if tsEditPending in FStates then + begin + StopTimer(EditTimer); + DoStateChange([], [tsEditPending]); + end; - if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then - InitChildren(Node); - Child := Node.FirstChild; - while Assigned(Child) do + if not (tsEditing in FStates) or DoEndEdit then begin - ValidateNode(Child, Recursive); - Child := Child.NextSibling; + if Node = nil then + Node := FRoot; + if vsHasChildren in Node.States then + begin + if (Node.ChildCount = 0) and DoInit then + InitChildren(Node); + // Make sure the children are valid, so they can be sorted at all. + if DoInit and (Node.ChildCount > 0) then + ValidateChildren(Node, False); + // Child count might have changed. + if Node.ChildCount > 1 then + begin + StartOperation(okSortNode); + try + // Sort the linked list, check direction flag only once. + if Direction = sdAscending then + Node.FirstChild := MergeSortAscending(Node.FirstChild, Node.ChildCount) + else + Node.FirstChild := MergeSortDescending(Node.FirstChild, Node.ChildCount); + finally + EndOperation(okSortNode); + end; + // Consolidate the child list finally. + Run := Node.FirstChild; + Run.PrevSibling := nil; + Index := 0; + repeat + Run.Index := Index; + Inc(Index); + if Run.NextSibling = nil then + Break; + Run.NextSibling.PrevSibling := Run; + Run := Run.NextSibling; + until False; + Node.LastChild := Run; + + InvalidateCache; + end; + if FUpdateCount = 0 then + begin + ValidateCache; + Invalidate; + end; + end; end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TBaseVirtualTree.ValidateNode(Node: PVirtualNode; Recursive: Boolean); +procedure TBaseVirtualTree.SortTree(Column: TColumnIndex; Direction: TSortDirection; DoInit: Boolean = True); -// Ensures that the given node (and all its children, if Recursive is True) are initialized. + //--------------- local function -------------------------------------------- -var - Child: PVirtualNode; + procedure DoSort(Node: PVirtualNode); -begin - if Node = nil then - Node := FRoot - else - if not (vsInitialized in Node.States) then - InitNode(Node); + // Recursively sorts Node and its child nodes. + + var + Run: PVirtualNode; - if Recursive then begin - if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then - InitChildren(Node); - Child := Node.FirstChild; - while Assigned(Child) do + Sort(Node, Column, Direction, DoInit); + // Recurse to next level + Run := Node.FirstChild; + while Assigned(Run) and not FOperationCanceled do begin - ValidateNode(Child, Recursive); - Child := Child.NextSibling; + if DoInit and not (vsInitialized in Run.States) then + InitNode(Run); + if (vsInitialized in Run.States) and (not (toAutoSort in TreeOptions.AutoOptions) or Expanded[Run]) then // There is no need to sort collapsed branches + DoSort(Run); + Run := Run.NextSibling; end; end; -end; - -//----------------- TCustomStringTreeOptions --------------------------------------------------------------------------- -constructor TCustomStringTreeOptions.Create(AOwner: TBaseVirtualTree); + //--------------- end local function ---------------------------------------- begin - inherited; - - FStringOptions := DefaultStringOptions; + if RootNode.TotalCount <= 2 then + Exit;//Nothing to do if there are one or zero nodes. RootNode.TotalCount is 1 if there are no nodes in the treee as the root node counts too here. + // Instead of wrapping the sort using BeginUpdate/EndUpdate simply the update counter + // is modified. Otherwise the EndUpdate call will recurse here. + Inc(FUpdateCount); + try + if Column > InvalidColumn then + begin + StartOperation(okSortTree); + try + DoSort(FRoot); + finally + EndOperation(okSortTree); + end; + end; + InvalidateCache; + finally + if FUpdateCount > 0 then + Dec(FUpdateCount); + if FUpdateCount = 0 then + begin + ValidateCache; + Invalidate; + end; + end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TCustomStringTreeOptions.SetStringOptions(const Value: TVTStringOptions); +procedure TBaseVirtualTree.ToggleNode(Node: PVirtualNode); + +// Changes a node's expand state to the opposite state. + +var + Child, + FirstVisible: PVirtualNode; + HeightDelta, + StepsR1, + StepsR2, + Steps: Integer; + TogglingTree, + ChildrenInView, + NeedFullInvalidate, + NeedUpdate, + NodeInView, + PosHoldable, + TotalFit: Boolean; + ToggleData: TToggleAnimationData; -var - ChangedOptions: TVTStringOptions; + //--------------- local function -------------------------------------------- -begin - if FStringOptions <> Value then - begin - // Exclusive ORing to get all entries wich are in either set but not in both. - ChangedOptions := FStringOptions + Value - (FStringOptions * Value); - FStringOptions := Value; - with FOwner do - if (toShowStaticText in ChangedOptions) and not (csLoading in ComponentState) and HandleAllocated then - Invalidate; - end; -end; + procedure PrepareAnimation; -//---------------------------------------------------------------------------------------------------------------------- + // Prepares ToggleData. -procedure TCustomStringTreeOptions.AssignTo(Dest: TPersistent); + var + R: TRect; + S: Integer; + M: TToggleAnimationMode; -begin - if Dest is TCustomStringTreeOptions then begin - with Dest as TCustomStringTreeOptions do + with ToggleData do begin - StringOptions := Self.StringOptions; - EditOptions := Self.EditOptions; - end; - end; + Window := Handle; + DC := GetDC(Handle); + + if (toShowBackground in FOptions.PaintOptions) and Assigned(FBackground.Graphic) then + Self.Brush.Style := bsClear + else + begin + Self.Brush.Style := bsSolid; + Self.Brush.Color := FColors.BackGroundColor; + end; - // Let ancestors assign their options to the destination class. - inherited; -end; + Brush := Self.Brush.Handle; -//----------------- TVTEdit -------------------------------------------------------------------------------------------- + if (Mode1 <> tamNoScroll) and (Mode2 <> tamNoScroll) then + begin + if StepsR1 < StepsR2 then + begin + // As the primary rectangle is always R1 we will get a much smoother + // animation if R1 is the one that will be scrolled more. + R := R2; + R2 := R1; + R1 := R; -// Implementation of a generic node caption editor. + M := Mode2; + Mode2 := Mode1; + Mode1 := M; -constructor TVTEdit.Create(Link: TStringEditLink); + S := StepsR2; + StepsR2 := StepsR1; + StepsR1 := S; + end; + ScaleFactor := StepsR2 / StepsR1; + MissedSteps := 0; + end; -begin - inherited Create(nil); - if not Assigned(Link) then - raise EArgumentException.Create('Paramter Link must not be nil.'); - ShowHint := False; - ParentShowHint := False; - // This assignment increases the reference count for the interface. - FRefLink := Link; - // This reference is used to access the link. - FLink := Link; -end; + if Mode1 <> tamNoScroll then + Steps := StepsR1 + else + Steps := StepsR2; + end; + end; -//---------------------------------------------------------------------------------------------------------------------- + //--------------- end local function ---------------------------------------- -procedure TVTEdit.ClearLink; begin - FLink := nil -end; + Assert(Assigned(Node), 'Node must not be nil.'); -//---------------------------------------------------------------------------------------------------------------------- -procedure TVTEdit.ClearRefLink; -begin - FRefLink := nil -end; + TogglingTree := tsToggling in FStates; + ChildrenInView := False; + HeightDelta := 0; + NeedFullInvalidate := False; + NeedUpdate := False; + NodeInView := False; + PosHoldable := False; + TotalFit := False; -//---------------------------------------------------------------------------------------------------------------------- + // We don't need to switch the expand state if the node is being deleted otherwise some + // updates (e.g. visible node count) are done twice with disasterous results). + if [vsDeleting, vsToggling] * Node.States = [] then + begin + try + DoStateChange([tsToggling]); + Include(Node.States, vsToggling); -function TVTEdit.CalcMinHeight: Integer; -var - textHeight : Integer; -begin - // Get the actual text height. - textHeight := GetTextSize.cy; - // The minimal height is the actual text height in pixels plus the the non client area. - Result := textHeight + (Height - ClientHeight); - // Also, proportionally to the text size, additional pixel(s) needs to be added for the caret. - Result := Result + Trunc(textHeight * 0.05); -end; + if vsExpanded in Node.States then + begin + if DoCollapsing(Node) then + begin + NeedUpdate := True; -//---------------------------------------------------------------------------------------------------------------------- + // Calculate the height delta right now as we need it for toChildrenAbove anyway. + HeightDelta := -Integer(Node.TotalHeight) + Integer(NodeHeight[Node]); + if (FUpdateCount = 0) and (toAnimatedToggle in FOptions.AnimationOptions) and not + (tsCollapsing in FStates) then + begin + if tsHint in Self.FStates then + Application.CancelHint; + UpdateWindow(Handle); -procedure TVTEdit.CMAutoAdjust(var Message: TMessage); + // animated collapsing + with ToggleData do + begin + // Determine the animation behaviour and rectangle. If toChildrenAbove is set, the behaviour is depending + // on the position of the node to be collapsed. + R1 := GetDisplayRect(Node, NoColumn, False); + Mode2 := tamNoScroll; + if toChildrenAbove in FOptions.PaintOptions then + begin + PosHoldable := (FOffsetY + (Integer(Node.TotalHeight) - Integer(NodeHeight[Node]))) <= 0; + NodeInView := R1.Top < ClientHeight; -begin - AutoAdjustSize; -end; + StepsR1 := 0; + if NodeInView then + begin + if PosHoldable or not (toAdvancedAnimatedToggle in FOptions.AnimationOptions) then + begin + // Scroll the child nodes down. + Mode1 := tamScrollDown; + R1.Bottom := R1.Top; + R1.Top := 0; + StepsR1 := Min(R1.Bottom - R1.Top + 1, Integer(Node.TotalHeight) - Integer(NodeHeight[Node])); + end + else + begin + // The position cannot be kept. So scroll the node up to its future position. + Mode1 := tamScrollUp; + R1.Top := Max(0, R1.Top + HeightDelta); + R1.Bottom := ClientHeight; + StepsR1 := FOffsetY - HeightDelta; + end; + end; + end + else + begin + if (Integer(FRangeY) + FOffsetY - R1.Bottom + HeightDelta >= ClientHeight - R1.Bottom) or + (Integer(FRangeY) <= ClientHeight) or (FOffsetY = 0) or not + (toAdvancedAnimatedToggle in FOptions.AnimationOptions) then + begin + // Do a simple scroll up over the child nodes. + Mode1 := tamScrollUp; + Inc(R1.Top, NodeHeight[Node]); + R1.Bottom := ClientHeight; + StepsR1 := Min(R1.Bottom - R1.Top + 1, -HeightDelta); + end + else + begin + // Scroll the node down to its future position. As FOffsetY will change we need to invalidate the + // whole tree. + Mode1 := tamScrollDown; + StepsR1 := Min(-FOffsetY, ClientHeight - Integer(FRangeY) -FOffsetY - HeightDelta); + R1.Top := 0; + R1.Bottom := Min(ClientHeight, R1.Bottom + Steps); + NeedFullInvalidate := True; + end; + end; -//---------------------------------------------------------------------------------------------------------------------- + // No animation necessary if the node is below the current client height. + if R1.Top < ClientHeight then + begin + PrepareAnimation; + try + Animate(Steps, FAnimationDuration, ToggleCallback, @ToggleData); + finally + ReleaseDC(Window, DC); + end; + end; + end; + end; -procedure TVTEdit.CMExit(var Message: TMessage); + // collapse the node + AdjustTotalHeight(Node, IfThen(IsEffectivelyFiltered[Node], 0, NodeHeight[Node])); + if FullyVisible[Node] then + Dec(FVisibleCount, CountVisibleChildren(Node)); + Exclude(Node.States, vsExpanded); + DoCollapsed(Node); -begin - if Assigned(FLink) and not FLink.Stopping then - with FLink, FTree do - begin - if (toAutoAcceptEditChange in TreeOptions.StringOptions) then - DoEndEdit + // Remove child nodes now, if enabled. + if (toAutoFreeOnCollapse in FOptions.AutoOptions) and (Node.ChildCount > 0) then + begin + DeleteChildren(Node); + Include(Node.States, vsHasChildren); + end; + end; + end else - DoCancelEdit; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTEdit.CMRelease(var Message: TMessage); - -begin - Free; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTEdit.CNCommand(var Message: TWMCommand); - -begin - if Assigned(FLink) and Assigned(FLink.Tree) and (Message.NotifyCode = EN_UPDATE) and - not (vsMultiline in FLink.Node.States) then - // Instead directly calling AutoAdjustSize it is necessary on Win9x/Me to decouple this notification message - // and eventual resizing. Hence we use a message to accomplish that. - AutoAdjustSize() - else - inherited; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTEdit.WMChar(var Message: TWMChar); + if DoExpanding(Node) then + begin + NeedUpdate := True; + // expand the node, need to adjust the height + if not (vsInitialized in Node.States) then + InitNode(Node); + if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then + InitChildren(Node); -begin - if not (Message.CharCode in [VK_ESCAPE, VK_TAB]) then - inherited; -end; + // Avoid setting the vsExpanded style if there are no child nodes. + if Node.ChildCount > 0 then + begin + // Iterate through the child nodes without initializing them. We have to determine the entire height. + Child := Node.FirstChild; + repeat + if vsVisible in Child.States then + begin + // Ensure the item height is measured + MeasureItemHeight(Canvas, Child); -//---------------------------------------------------------------------------------------------------------------------- + Inc(HeightDelta, Child.TotalHeight); + end; + Child := Child.NextSibling; + until Child = nil; -procedure TVTEdit.WMDestroy(var Message: TWMDestroy); + // Getting the display rectangle is already done here as it is needed for toChildrenAbove in any case. + if (toChildrenAbove in FOptions.PaintOptions) or (FUpdateCount = 0) then + begin + with ToggleData do + begin + R1 := GetDisplayRect(Node, NoColumn, False); + Mode2 := tamNoScroll; + TotalFit := HeightDelta + Integer(NodeHeight[Node]) <= ClientHeight; -begin - // If editing stopped by other means than accept or cancel then we have to do default processing for - // pending changes. - if Assigned(FLink) and not FLink.Stopping and not (csRecreating in Self.ControlState) then - begin - with FLink, FTree do - begin - if (toAutoAcceptEditChange in TreeOptions.StringOptions) and Modified then - Text[FNode, FColumn] := FEdit.Text; - end; - FLink := nil; - FRefLink := nil; - end; + if toChildrenAbove in FOptions.PaintOptions then + begin + // The main goal with toChildrenAbove being set is to keep the nodes visual position so the user does + // not get confused. Therefore we need to scroll the view when the expanding is done. + PosHoldable := TotalFit and (Integer(FRangeY) - ClientHeight >= 0) ; + ChildrenInView := (R1.Top - HeightDelta) >= 0; + NodeInView := R1.Bottom <= ClientHeight; + end + else + begin + PosHoldable := TotalFit; + ChildrenInView := R1.Bottom + HeightDelta <= ClientHeight; + end; - inherited; -end; + R1.Bottom := ClientHeight; + end; + end; -//---------------------------------------------------------------------------------------------------------------------- + if FUpdateCount = 0 then + begin + // Do animated expanding if enabled. + if (ToggleData.R1.Top < ClientHeight) and ([tsPainting, tsExpanding] * FStates = []) and + (toAnimatedToggle in FOptions.AnimationOptions)then + begin + if tsHint in Self.FStates then + Application.CancelHint; + UpdateWindow(Handle); + // animated expanding + with ToggleData do + begin + if toChildrenAbove in FOptions.PaintOptions then + begin + // At first check if we hold the position, which is the most common case. + if not (toAdvancedAnimatedToggle in FOptions.AnimationOptions) or + (PosHoldable and ( (NodeInView and ChildrenInView) or not + (toAutoScrollOnExpand in FOptions.AutoOptions) )) then + begin + Mode1 := tamScrollUp; + R1 := Rect(R1.Left, 0, R1.Right, R1.Top); + StepsR1 := Min(HeightDelta, R1.Bottom); + end + else + begin + // If we will not hold the node's visual position we mostly scroll in both directions. + Mode1 := tamScrollDown; + Mode2 := tamScrollUp; + R2 := Rect(R1.Left, 0, R1.Right, R1.Top); + if not (toAutoScrollOnExpand in FOptions.AutoOptions) then + begin + // If we shall not or cannot scroll to the desired extent we calculate the new position (with + // max FOffsetY applied) and animate it that way. + StepsR1 := -FOffsetY - Max(Integer(FRangeY) + HeightDelta - ClientHeight, 0) + HeightDelta; + if (Integer(FRangeY) + HeightDelta - ClientHeight) <= 0 then + Mode2 := tamNoScroll + else + StepsR2 := Min(Integer(FRangeY) + HeightDelta - ClientHeight, R2.Bottom); + end + else + begin + if TotalFit and NodeInView and (Integer(FRangeY) + HeightDelta > ClientHeight) then + begin + // If the whole subtree will fit into the client area and the node is currently fully visible, + // the first child will be made the top node if possible. + if HeightDelta >= R1.Top then + StepsR1 := Abs(R1.Top - HeightDelta) + else + StepsR1 := ClientHeight - Integer(FRangeY); + end + else + if Integer(FRangeY) + HeightDelta <= ClientHeight then + begin + // We cannot make the first child the top node as we cannot scroll to that extent, + // so we do a simple scroll down. + Mode2 := tamNoScroll; + StepsR1 := HeightDelta; + end + else + // If the subtree does not fit into the client area at once, the expanded node will + // be made the bottom node. + StepsR1 := ClientHeight - R1.Top - Integer(NodeHeight[Node]); -procedure TVTEdit.WMGetDlgCode(var Message: TWMGetDlgCode); + if Mode2 <> tamNoScroll then + begin + if StepsR1 > 0 then + StepsR2 := Min(R1.Top, HeightDelta - StepsR1) + else + begin + // If the node is already at the bottom scrolling is needed. + Mode1 := tamNoScroll; + StepsR2 := Min(HeightDelta, R1.Bottom); + end; + end; + end; + end; + end + else + begin + // toChildrenAbove is not set. + if (PosHoldable and ChildrenInView) or not (toAutoScrollOnExpand in FOptions.AutoOptions) or not + (toAdvancedAnimatedToggle in FOptions.AnimationOptions) or (R1.Top <= 0) then + begin + // If the node will stay at its visual position, do a simple down-scroll. + Mode1 := tamScrollDown; + Inc(R1.Top, NodeHeight[Node]); + StepsR1 := Min(R1.Bottom - R1.Top, HeightDelta); + end + else + begin + // We will not hold the nodes visual position so perform a double scroll. + Mode1 := tamScrollUp; + Mode2 := tamScrollDown; -begin - inherited; + R1.Bottom := R1.Top + Integer(NodeHeight[Node]) + 1; + R1.Top := 0; + R2 := Rect(R1.Left, R1.Bottom, R1.Right, ClientHeight); - Message.Result := Message.Result or DLGC_WANTALLKEYS or DLGC_WANTTAB or DLGC_WANTARROWS; -end; + StepsR1 := Min(HeightDelta - (ClientHeight - R2.Top), R1.Bottom - Integer(NodeHeight[Node])); + StepsR2 := ClientHeight - R2.Top; + end; + end; -//---------------------------------------------------------------------------------------------------------------------- + if ClientHeight >= R1.Top then + begin + PrepareAnimation; + try + Animate(Steps, FAnimationDuration, ToggleCallback, @ToggleData); + finally + ReleaseDC(Window, DC); + end; + end; + end; + end; + if toAutoSort in FOptions.AutoOptions then + Sort(Node, FHeader.SortColumn, FHeader.SortDirection, False); + end;// if UpdateCount = 0 -procedure TVTEdit.WMKeyDown(var Message: TWMKeyDown); + Include(Node.States, vsExpanded); + AdjustTotalHeight(Node, HeightDelta, True); + if FullyVisible[Node] then + Inc(FVisibleCount, CountVisibleChildren(Node)); -// Handles some control keys. + DoExpanded(Node); + end; + end; -var - Shift: TShiftState; - EndEdit: Boolean; - Tree: TBaseVirtualTree; - NextNode: PVirtualNode; - ColumnCandidate: Integer; - EditOptions: TVTEditOptions; - Column: TVirtualTreeColumn; -begin - Tree := FLink.Tree; - case Message.CharCode of - VK_ESCAPE: - begin - Tree.DoCancelEdit; - end; - VK_RETURN: + if NeedUpdate then begin - EndEdit := not (vsMultiline in FLink.Node.States); - if not EndEdit then - begin - // If a multiline node is being edited the finish editing only if Ctrl+Enter was pressed, - // otherwise allow to insert line breaks into the text. - Shift := KeyDataToShiftState(Message.KeyData); - EndEdit := ssCtrl in Shift; - end; - if EndEdit then + InvalidateCache; + if FUpdateCount = 0 then begin - Tree := FLink.Tree; - FLink.Tree.InvalidateNode(FLink.Node); - NextNode := Tree.GetNextVisible(FLink.Node, True); - FLink.Tree.DoEndEdit; - - // get edit options for column as priority. If column has toDefaultEdit - // use global edit options for tree - EditOptions := Tree.TreeOptions.EditOptions; // default - ColumnCandidate := -1; - if Tree.Header.Columns.Count > 0 then // are there any columns? + ValidateCache; + if Node.ChildCount > 0 then begin - Column := Tree.Header.Columns[Tree.FocusedColumn]; - if Column.EditOptions <> toDefaultEdit then - EditOptions := Column.EditOptions; - - // next column candidate for toVerticalEdit and toHorizontalEdit - if Column.EditNextColumn <> -1 then - ColumnCandidate := Column.EditNextColumn; - end; - - case EditOptions of - toDefaultEdit: Tree.TrySetFocus; - toVerticalEdit: - if NextNode <> nil then - begin - Tree.FocusedNode := NextNode; - - // for toVerticalEdit ColumnCandidate is also proper, - // select ColumnCandidate column in row below - if ColumnCandidate <> -1 then - begin - Tree.FocusedColumn := ColumnCandidate; - Tree.EditColumn := ColumnCandidate; - end; - - if Tree.CanEdit(Tree.FocusedNode, Tree.FocusedColumn) then - Tree.DoEdit; - end; - toHorizontalEdit: + UpdateRanges; + UpdateScrollBars(True); + if [tsPainting, tsExpanding] * FStates = [] then + begin + if (vsExpanded in Node.States) and ((toAutoScrollOnExpand in FOptions.AutoOptions) or + (toChildrenAbove in FOptions.PaintOptions)) then begin - if ColumnCandidate = -1 then + if toChildrenAbove in FOptions.PaintOptions then begin - // for toHorizontalEdit if property EditNextColumn is not used - // try to use just next column - ColumnCandidate := Tree.FocusedColumn+1; - while (ColumnCandidate < Tree.Header.Columns.Count) - and not Tree.CanEdit(Tree.FocusedNode, ColumnCandidate) - do - Inc(ColumnCandidate); + NeedFullInvalidate := True; + if (PosHoldable and ChildrenInView and NodeInView) or not + (toAutoScrollOnExpand in FOptions.AutoOptions) then + SetOffsetY(FOffsetY - Integer(HeightDelta)) + else + if TotalFit and NodeInView then + begin + FirstVisible := GetFirstVisible(Node, True); + if Assigned(FirstVisible) then // otherwise there is no visible child at all + SetOffsetY(FOffsetY - GetDisplayRect(FirstVisible, NoColumn, False).Top); + end + else + BottomNode := Node; end else - if not Tree.CanEdit(Tree.FocusedNode, ColumnCandidate) then - ColumnCandidate := Tree.Header.Columns.Count; // omit "focus/edit column" (see below) - - if ColumnCandidate < Tree.Header.Columns.Count then begin - Tree.FocusedColumn := ColumnCandidate; - Tree.EditColumn := ColumnCandidate; - Tree.DoEdit; + // Scroll as much child nodes into view as possible if the node has been expanded. + if PosHoldable then + NeedFullInvalidate := ScrollIntoView(GetLastVisible(Node, True), False) + else + begin + TopNode := Node; + NeedFullInvalidate := True; + end; end; + end + else + begin + // If we have collapsed the node or toAutoScrollOnExpand is not set, we try to keep the nodes + // visual position. + if toChildrenAbove in FOptions.PaintOptions then + SetOffsetY(FOffsetY - Integer(HeightDelta)); + NeedFullInvalidate := True; end; - end; - end; - end; - VK_UP: - begin - if not (vsMultiline in FLink.Node.States) then - Message.CharCode := VK_LEFT; - inherited; - end; - VK_DOWN: - begin - if not (vsMultiline in FLink.Node.States) then - Message.CharCode := VK_RIGHT; - inherited; - end; - VK_TAB: - begin - if Tree.IsEditing then - begin - Tree.InvalidateNode(FLink.Node); - if ssShift in KeyDataToShiftState(Message.KeyData) then - NextNode := Tree.GetPreviousVisible(FLink.Node, True) // Shift+Tab goes to previous mode + end; + + //UpdateScrollBars(True); Moved up + + // Check for automatically scrolled tree. + if NeedFullInvalidate then + Invalidate + else + InvalidateToBottom(Node); + end else - NextNode := Tree.GetNextVisible(FLink.Node, True); - Tree.EndEditNode; - // check NextNode, otherwise we got AV - if NextNode <> nil then - begin - // Continue editing next node - Tree.ClearSelection(); - Tree.Selected[NextNode] := True; - if Tree.CanEdit(Tree.FocusedNode, Tree.FocusedColumn) then - Tree.DoEdit; - end; - end; - end; - Ord('A'): - begin - if Tree.IsEditing and ([ssCtrl] = KeyboardStateToShiftState) then + InvalidateNode(Node); + end + else begin - Self.SelectAll(); - Message.CharCode := 0; + UpdateRanges; + UpdateScrollBars(True); end; end; - else - inherited; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTEdit.AutoAdjustSize; - -// Changes the size of the edit to accomodate as much as possible of its text within its container window. -// NewChar describes the next character which will be added to the edit's text. - -var - Size: TSize; -begin - if not (vsMultiline in FLink.Node.States) and not (toGridExtensions in FLink.Tree.TreeOptions.MiscOptions{see issue #252}) then - begin - // avoid flicker - SendMessage(Handle, WM_SETREDRAW, 0, 0); - try - Size := GetTextSize; - Inc(Size.cx, 2 * FLink.Tree.FTextMargin); - // Repaint associated node if the edit becomes smaller. - if Size.cx < Width then - FLink.Tree.Invalidate(); - - if FLink.Alignment = taRightJustify then - FLink.SetBounds(Rect(Left + Width - Size.cx, Top, Left + Width, Top + Max(Size.cy, Height))) - else - FLink.SetBounds(Rect(Left, Top, Left + Size.cx, Top + Max(Size.cy, Height))); - finally - SendMessage(Handle, WM_SETREDRAW, 1, 0); - end; - end; -end; - -//---------------------------------------------------------------------------------------------------------------------- - -procedure TVTEdit.CreateParams(var Params: TCreateParams); - -begin - inherited; - if not Assigned(FLink.Node) then - exit; // Prevent AV exceptions occasionally seen in code below - - // Only with multiline style we can use the text formatting rectangle. - // This does not harm formatting as single line control, if we don't use word wrapping. - with Params do - begin - Style := Style or ES_MULTILINE; - if vsMultiline in FLink.Node.States then - Style := Style and not (ES_AUTOHSCROLL or WS_HSCROLL) or WS_VSCROLL or ES_AUTOVSCROLL; - if tsUseThemes in FLink.Tree.FStates then - begin - Style := Style and not WS_BORDER; - ExStyle := ExStyle or WS_EX_CLIENTEDGE; - end - else - begin - Style := Style or WS_BORDER; - ExStyle := ExStyle and not WS_EX_CLIENTEDGE; + + finally + Exclude(Node.States, vsToggling); + if not TogglingTree then + DoStateChange([], [tsToggling]); end; end; end; //---------------------------------------------------------------------------------------------------------------------- -function TVTEdit.GetTextSize: TSize; -var - DC: HDC; - LastFont: THandle; -begin - DC := GetDC(Handle); - LastFont := SelectObject(DC, Font.Handle); - try - // Read needed space for the current text. - GetTextExtentPoint32(DC, PChar(Text+'yG'), Length(Text)+2, Result); - finally - SelectObject(DC, LastFont); - ReleaseDC(Handle, DC); - end; -end; +procedure TBaseVirtualTree.UpdateHorizontalRange; -procedure TVTEdit.KeyPress(var Key: Char); begin - if (Key = #13) and Assigned(FLink) and not (vsMultiline in FLink.Node.States) then - Key := #0; // Filter out return keys as they will be added to the text, avoids #895 - inherited; + if FHeader.UseColumns then + SetRangeX(FHeader.Columns.TotalWidth) + else + SetRangeX(GetMaxRightExtend); end; //---------------------------------------------------------------------------------------------------------------------- -procedure TVTEdit.Release; - -begin - if HandleAllocated then - PostMessage(Handle, CM_RELEASE, 0, 0); -end; - -//----------------- TStringEditLink ------------------------------------------------------------------------------------ +procedure TBaseVirtualTree.UpdateHorizontalScrollBar(DoRepaint: Boolean); -constructor TStringEditLink.Create; +var + ScrollInfo: TScrollInfo; begin - inherited; - FEdit := TVTEdit.Create(Self); - with FEdit do - begin - Visible := False; - BorderStyle := bsSingle; - AutoSize := False; - end; -end; + UpdateHorizontalRange; -//---------------------------------------------------------------------------------------------------------------------- + if IsUpdating or not HandleAllocated then + Exit; -destructor TStringEditLink.Destroy; + // Adjust effect scroll offset depending on bidi mode. + if UseRightToLeftAlignment then + FEffectiveOffsetX := Integer(FRangeX) - ClientWidth + FOffsetX + else + FEffectiveOffsetX := -FOffsetX; -begin - if Assigned(FEdit) then - FEdit.Release; - inherited; -end; + if FScrollBarOptions.ScrollBars in [System.UITypes.TScrollStyle.ssHorizontal, System.UITypes.TScrollStyle.ssBoth] then + begin + ZeroMemory (@ScrollInfo, SizeOf(ScrollInfo)); + ScrollInfo.cbSize := SizeOf(ScrollInfo); + ScrollInfo.fMask := SIF_ALL; + GetScrollInfo(Handle, SB_HORZ, ScrollInfo); -//---------------------------------------------------------------------------------------------------------------------- + if (Integer(FRangeX) > ClientWidth) or FScrollBarOptions.AlwaysVisible then + begin + DoShowScrollBar(SB_HORZ, True); -function TStringEditLink.BeginEdit: Boolean; + ScrollInfo.nMin := 0; + ScrollInfo.nMax := FRangeX; + ScrollInfo.nPos := FEffectiveOffsetX; + ScrollInfo.nPage := Max(0, ClientWidth + 1); -// Notifies the edit link that editing can start now. descendants may cancel node edit -// by returning False. + ScrollInfo.fMask := SIF_ALL or ScrollMasks[FScrollBarOptions.AlwaysVisible]; + SetScrollInfo(Handle, SB_HORZ, ScrollInfo, DoRepaint); // 1 app freeze seen here in TreeSize 8.1.0 after ScaleForPpi() + if DoRepaint then + RedrawWindow(Handle, nil, 0, RDW_FRAME or RDW_INVALIDATE); // Fixes issue #698 + end + else + begin + ScrollInfo.nMin := 0; + ScrollInfo.nMax := 0; + ScrollInfo.nPos := 0; + ScrollInfo.nPage := 0; + DoShowScrollBar(SB_HORZ, False); + SetScrollInfo(Handle, SB_HORZ, ScrollInfo, False); + end; -begin - Result := not FStopping; - if Result then + // Since the position is automatically changed if it doesn't meet the range + // we better read the current position back to stay synchronized. + FEffectiveOffsetX := GetScrollPos(Handle, SB_HORZ); + if UseRightToLeftAlignment then + SetOffsetX(-Integer(FRangeX) + ClientWidth + FEffectiveOffsetX) + else + SetOffsetX(-FEffectiveOffsetX); + end + else begin - FEdit.Show; - FEdit.SelectAll; - FEdit.SetFocus; - FEdit.AutoAdjustSize; + DoShowScrollBar(SB_HORZ, False); + + // Reset the current horizontal offset to account for window resize etc. + SetOffsetX(FOffsetX); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TStringEditLink.SetEdit(const Value: TVTEdit); +procedure TBaseVirtualTree.UpdateRanges; begin - if Assigned(FEdit) then - FEdit.Free; - FEdit := Value; + UpdateVerticalRange; + UpdateHorizontalRange; end; //---------------------------------------------------------------------------------------------------------------------- -function TStringEditLink.CancelEdit: Boolean; +procedure TBaseVirtualTree.UpdateScrollBars(DoRepaint: Boolean); + +// adjusts scrollbars to reflect current size and paint offset of the tree begin - Result := not FStopping; - if Result then + if HandleAllocated then begin - FStopping := True; - FEdit.Hide; - FTree.CancelEditNode; - FEdit.ClearLink; - FEdit.ClearRefLink; + UpdateVerticalScrollBar(DoRepaint); + UpdateHorizontalScrollBar(DoRepaint); + Perform(CM_UPDATE_VCLSTYLE_SCROLLBARS,0,0); end; end; -//---------------------------------------------------------------------------------------------------------------------- - -function TStringEditLink.EndEdit: Boolean; - +procedure TBaseVirtualTree.UpdateStyleElements; begin - Result := not FStopping; - if Result then - try - FStopping := True; - if FEdit.Modified then - FTree.Text[FNode, FColumn] := FEdit.Text; - FEdit.Hide; - FEdit.ClearLink; - FEdit.ClearRefLink; - except - FStopping := False; - raise; - end; + inherited; + UpdateHeaderRect; + FHeader.Columns.PaintHeader(Canvas, FHeaderRect, Point(0,0)); end; //---------------------------------------------------------------------------------------------------------------------- -function TStringEditLink.GetBounds: TRect; +procedure TBaseVirtualTree.UpdateVerticalRange; begin - Result := FEdit.BoundsRect; + // Total node height includes the height of the invisible root node. + FRangeY := Cardinal(Int64(FRoot.TotalHeight) - FRoot.NodeHeight + FBottomSpace); end; //---------------------------------------------------------------------------------------------------------------------- -function TStringEditLink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; - -// Retrieves the true text bounds from the owner tree. +procedure TBaseVirtualTree.UpdateVerticalScrollBar(DoRepaint: Boolean); var - Text: string; + ScrollInfo: TScrollInfo; begin - Result := Tree is TCustomVirtualStringTree; - if Result then + UpdateVerticalRange; + + if (IsUpdating) then + Exit; + Assert(GetCurrentThreadId = MainThreadId, 'UI controls like ' + Classname + ' and its scrollbars should only be manipulated through the main thread.'); + + if FScrollBarOptions.ScrollBars in [ssVertical, ssBoth] then begin - if not Assigned(FEdit) then - begin - FEdit := TVTEdit.Create(Self); - FEdit.Visible := False; - FEdit.BorderStyle := bsSingle; - end; - FEdit.AutoSize := True; - FTree := Tree as TCustomVirtualStringTree; - FNode := Node; - FColumn := Column; - FEdit.Parent := Tree; - // Initial size, font and text of the node. - FTree.GetTextInfo(Node, Column, FEdit.Font, FTextBounds, Text); - FEdit.Font.Color := clWindowText; - FEdit.RecreateWnd; - FEdit.AutoSize := False; - FEdit.Text := Text; + ScrollInfo.cbSize := SizeOf(ScrollInfo); + ScrollInfo.fMask := SIF_ALL; + GetScrollInfo(Handle, SB_VERT, ScrollInfo); - if Column <= NoColumn then + if (Integer(FRangeY) > ClientHeight) or FScrollBarOptions.AlwaysVisible then begin - FEdit.BidiMode := FTree.BidiMode; - FAlignment := FTree.Alignment; + DoShowScrollBar(SB_VERT, True); + + ScrollInfo.nMin := 0; + ScrollInfo.nMax := FRangeY; + ScrollInfo.nPos := -FOffsetY; + ScrollInfo.nPage := Max(0, ClientHeight + 1); + + ScrollInfo.fMask := SIF_ALL or ScrollMasks[FScrollBarOptions.AlwaysVisible]; + SetScrollInfo(Handle, SB_VERT, ScrollInfo, DoRepaint); end else begin - FEdit.BidiMode := FTree.Header.Columns[Column].BidiMode; - FAlignment := FTree.Header.Columns[Column].Alignment; + ScrollInfo.nMin := 0; + ScrollInfo.nMax := 0; + ScrollInfo.nPos := 0; + ScrollInfo.nPage := 0; + DoShowScrollBar(SB_VERT, False); + SetScrollInfo(Handle, SB_VERT, ScrollInfo, False); end; - if FEdit.BidiMode <> bdLeftToRight then - ChangeBidiModeAlignment(FAlignment); + // Since the position is automatically changed if it doesn't meet the range + // we better read the current position back to stay synchronized. + SetOffsetY(-GetScrollPos(Handle, SB_VERT)); + end + else + begin + DoShowScrollBar(SB_VERT, False); + + // Reset the current vertical offset to account for window resize etc. + SetOffsetY(FOffsetY); end; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TStringEditLink.ProcessMessage(var Message: TMessage); +function TBaseVirtualTree.UseRightToLeftReading: Boolean; + +// The tree can handle right-to-left reading also on non-middle-east systems, so we cannot use the same function as +// it is implemented in TControl. begin - FEdit.WindowProc(Message); + Result := BiDiMode <> bdLeftToRight; end; //---------------------------------------------------------------------------------------------------------------------- -procedure TStringEditLink.SetBounds(R: TRect); +procedure TBaseVirtualTree.ValidateChildren(Node: PVirtualNode; Recursive: Boolean); -// Sets the outer bounds of the edit control and the actual edit area in the control. +// Ensures that the children of the given node (and all their children, if Recursive is True) are initialized. +// Node must already be initialized var - lOffset, tOffset, height: Integer; - offsets : TVTOffsets; + Child: PVirtualNode; + begin - if not FStopping then + if Node = nil then + Node := FRoot; + + if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then + InitChildren(Node); + Child := Node.FirstChild; + while Assigned(Child) do begin - // Check if the provided rect height is smaller than the edit control height. - height := R.Bottom - R.Top; - if height < FEdit.ClientHeight then - begin - // If the height is smaller than the minimal height we must correct it, otherwise the caret will be invisible. - tOffset := FEdit.CalcMinHeight - height; - if tOffset > 0 then - Inc(R.Bottom, tOffset); - end; + ValidateNode(Child, Recursive); + Child := Child.NextSibling; + end; +end; - // Set the edit's bounds but make sure there's a minimum width and the right border does not - // extend beyond the parent's left/right border. - if R.Left < 0 then - R.Left := 0; - if R.Right - R.Left < 30 then - begin - if FAlignment = taRightJustify then - R.Left := R.Right - 30 - else - R.Right := R.Left + 30; - end; - if R.Right > FTree.ClientWidth then - R.Right := FTree.ClientWidth; - FEdit.BoundsRect := R; - - // The selected text shall exclude the text margins and be centered vertically. - // We have to take out the two pixel border of the edit control as well as a one pixel "edit border" the - // control leaves around the (selected) text. - R := FEdit.ClientRect; - - // If toGridExtensions are turned on, we can fine tune the left margin (or the right margin if RTL is on) - // of the text to exactly match the text in the tree cell. - if (toGridExtensions in FTree.TreeOptions.MiscOptions) and - ((FAlignment = taLeftJustify) and (FEdit.BidiMode = bdLeftToRight) or - (FAlignment = taRightJustify) and (FEdit.BidiMode <> bdLeftToRight)) then - begin - // Calculate needed text area offset. - FTree.GetOffsets(FNode, offsets, ofsText, FColumn); - if FColumn = FTree.Header.MainColumn then - begin - if offsets[ofsToggleButton] < 0 then - lOffset := -(offsets[ofsToggleButton] + 2) - else - lOffset := 0; - end - else - lOffset := offsets[ofsText] - offsets[ofsMargin] + 1; - // Apply the offset. - if FEdit.BidiMode = bdLeftToRight then - Inc(R.Left, lOffset) - else - Dec(R.Right, lOffset); - end; +//---------------------------------------------------------------------------------------------------------------------- + +procedure TBaseVirtualTree.ValidateNode(Node: PVirtualNode; Recursive: Boolean); + +// Ensures that the given node (and all its children, if Recursive is True) are initialized. + +var + Child: PVirtualNode; + +begin + if Node = nil then + Node := FRoot + else + if not (vsInitialized in Node.States) then + InitNode(Node); - lOffset := IfThen(vsMultiline in FNode.States, 0, 2); - if tsUseThemes in FTree.TreeStates then - Inc(lOffset); - InflateRect(R, -FTree.TextMargin + lOffset, lOffset); - if not (vsMultiline in FNode.States) then + if Recursive then + begin + if (vsHasChildren in Node.States) and (Node.ChildCount = 0) then + InitChildren(Node); + Child := Node.FirstChild; + while Assigned(Child) do begin - tOffset := FTextBounds.Top - FEdit.Top; - // Do not apply a negative offset, the cursor will disappear. - if tOffset > 0 then - OffsetRect(R, 0, tOffset); + ValidateNode(Child, Recursive); + Child := Child.NextSibling; end; - R.Top := Max(-1, R.Top); // A value smaller than -1 will prevent the edit cursor from being shown by Windows, see issue #159 - R.Left := Max(-1, R.Left); - SendMessage(FEdit.Handle, EM_SETRECTNP, 0, LPARAM(@R)); end; end; + //----------------- TCustomVirtualString ------------------------------------------------------------------------------- constructor TCustomVirtualStringTree.Create(AOwner: TComponent); @@ -35096,56 +26157,6 @@ constructor TVSTGetCellTextEventArgs.Create(pNode: PVirtualNode; pColumn: TColum Self.ExportType := pExportType; end; -{ TCheckStateHelper } - -function TCheckStateHelper.IsDisabled: Boolean; -begin - Result := Self >= TCheckState.csUncheckedDisabled; -end; - -function TCheckStateHelper.IsChecked: Boolean; -begin - Result := Self in [csCheckedNormal, csCheckedPressed, csCheckedDisabled]; -end; - -function TCheckStateHelper.IsUnChecked: Boolean; -begin - Result := Self in [csUnCheckedNormal, csUnCheckedPressed, csUnCheckedDisabled]; -end; - -function TCheckStateHelper.IsMixed: Boolean; -begin - Result := Self in [csMixedNormal, csMixedPressed, csMixedDisabled]; -end; - -function TCheckStateHelper.GetEnabled: TCheckState; -begin - Result := cEnabledState[Self]; -end; - -function TCheckStateHelper.GetPressed(): TCheckState; -begin - Result := cPressedState[Self]; -end; - -function TCheckStateHelper.GetUnpressed(): TCheckState; -begin - Result := cUnpressedState[Self]; -end; - -function TCheckStateHelper.GetToggled(): TCheckState; -begin - Result := cToggledState[Self]; -end; - -{ TSortDirectionHelper } - -function TSortDirectionHelper.ToInt(): Integer; -begin - Result := cSortDirectionToInt[Self]; -end; - - { TVTPaintInfo } procedure TVTPaintInfo.AdjustImageCoordinates(); @@ -35175,33 +26186,6 @@ procedure TVTPaintInfo.AdjustImageCoordinates(); ImageInfo[iiCheck].YPos := CellRect.Top + VAlign - ImageInfo[iiCheck].Images.Height div 2; end; -{ THeaderPaintInfo } - -procedure THeaderPaintInfo.DrawDropMark(); -var - Y: Integer; - lArrowWidth: Integer; -begin - lArrowWidth := Self.Column.Owner.Header.Treeview.ScaledPixels(5); - Y := (PaintRectangle.Top + PaintRectangle.Bottom - 3 * lArrowWidth) div 2; - if DropMark = dmmLeft then - DrawArrow(TargetCanvas, TScrollDirection.sdLeft, Point(PaintRectangle.Left, Y), lArrowWidth) - else - DrawArrow(TargetCanvas, TScrollDirection.sdRight, Point(PaintRectangle.Right - lArrowWidth - (lArrowWidth div 2) {spacing}, Y), lArrowWidth); -end; - -procedure THeaderPaintInfo.DrawSortArrow(pDirection: TSortDirection); -const - cDirection: array[TSortDirection] of TScrollDirection = (TScrollDirection.sdUp, TScrollDirection.sdDown); -var - lOldColor: TColor; -begin - lOldColor := TargetCanvas.Pen.Color; - TargetCanvas.Pen.Color := clDkGray; - DrawArrow(TargetCanvas, cDirection[pDirection], Point(SortGlyphPos.X, SortGlyphPos.Y), SortGlyphSize.cy); - TargetCanvas.Pen.Color := lOldColor; -end; - initialization finalization From 5015a93d61a654d0db3c69a6da5b59531a816d39 Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Wed, 5 May 2021 10:46:01 +1000 Subject: [PATCH 214/681] CM_BASE is defined in Vcl.Themes and Vcl.Controls with different values ffs! --- Source/VirtualTrees.Constants.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/VirtualTrees.Constants.pas b/Source/VirtualTrees.Constants.pas index 89bdba8c0..a1a418265 100644 --- a/Source/VirtualTrees.Constants.pas +++ b/Source/VirtualTrees.Constants.pas @@ -4,7 +4,7 @@ interface uses System.UITypes, - Vcl.Themes; + Vcl.Controls; const VTVersion = '7.5.0' deprecated 'This const is going to be removed in a future version'; From d0c3df9bc27bdc7c9a556d418f86f6240581e153 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 5 May 2021 07:44:55 +0100 Subject: [PATCH 215/681] Improved fix for #1001: Call CheckSynchronize() as long as queued calls have been processed. --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 8fe360854..dcad7852a 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -12253,8 +12253,8 @@ destructor TBaseVirtualTree.Destroy; if WasValidating then begin // Make sure we dequeue the two synchronized calls from ChangeTreeStatesAsync(), fixes mem leak and AV reported in issue #1001, but is more a workaround. - CheckSynchronize(); - CheckSynchronize(); + while CheckSynchronize() do + Sleep(1); end;// if FOptions.InternalSetMiscOptions(FOptions.MiscOptions - [toReadOnly]); //SetMiscOptions has side effects // Make sure there is no reference remaining to the releasing tree. From f1adcc6c92d6496f4ff9a19136e28e7e35b35d00 Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Wed, 5 May 2021 17:50:07 +0100 Subject: [PATCH 216/681] Improved fix for #1001: Checking tsValidating in TBaseVirtualTree.Destroy() is not enough, the TWorkerThread may be stuck in the first call to ChangeTreeStatesAsync() where tsValidating is not set. --- Source/VirtualTrees.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index dcad7852a..774b1db1b 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -12234,7 +12234,7 @@ constructor TBaseVirtualTree.Create(AOwner: TComponent); //---------------------------------------------------------------------------------------------------------------------- -destructor TBaseVirtualTree.Destroy; +destructor TBaseVirtualTree.Destroy(); var WasValidating: Boolean; begin @@ -12248,7 +12248,7 @@ destructor TBaseVirtualTree.Destroy; fAccessible := nil; end; - WasValidating := (tsValidating in FStates); + WasValidating := (tsValidating in FStates) or (tsValidationNeeded in FStates); // Checking tsValidating is not enough, the TWorkerThread may be stuck in the first call to ChangeTreeStatesAsync() InterruptValidation(True); if WasValidating then begin From 60cce90b7db98aecb4f0e8ba60b3f4dfaacbd5fd Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Mon, 10 May 2021 12:55:46 +1000 Subject: [PATCH 217/681] Copied in recent changes from master. --- Source/VirtualTrees.pas | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index e93f8a07b..a2a4fa2f0 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -3920,7 +3920,7 @@ constructor TBaseVirtualTree.Create(AOwner: TComponent); //---------------------------------------------------------------------------------------------------------------------- -destructor TBaseVirtualTree.Destroy; +destructor TBaseVirtualTree.Destroy(); var WasValidating: Boolean; begin @@ -3934,13 +3934,13 @@ destructor TBaseVirtualTree.Destroy; fAccessible := nil; end; - WasValidating := (tsValidating in FStates); + WasValidating := (tsValidating in FStates) or (tsValidationNeeded in FStates); // Checking tsValidating is not enough, the TWorkerThread may be stuck in the first call to ChangeTreeStatesAsync() InterruptValidation(True); if WasValidating then begin // Make sure we dequeue the two synchronized calls from ChangeTreeStatesAsync(), fixes mem leak and AV reported in issue #1001, but is more a workaround. - CheckSynchronize(); - CheckSynchronize(); + while CheckSynchronize() do + Sleep(1); end;// if FOptions.InternalSetMiscOptions(FOptions.MiscOptions - [toReadOnly]); //SetMiscOptions has side effects // Make sure there is no reference remaining to the releasing tree. From e23c0fdfda7ae882a1a14f6732489a57048c51fc Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Mon, 10 May 2021 12:56:19 +1000 Subject: [PATCH 218/681] Added System.UITypes to uses to remove inlining hint for fonts. --- Source/VirtualTrees.Header.pas | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Source/VirtualTrees.Header.pas b/Source/VirtualTrees.Header.pas index 02fde6bd1..727d8f11e 100644 --- a/Source/VirtualTrees.Header.pas +++ b/Source/VirtualTrees.Header.pas @@ -145,7 +145,7 @@ TVTHeader = class(TPersistent) FDoingAutoFitColumns : Boolean; //Flag to avoid using the stored width for Main column procedure FontChanged(Sender : TObject); virtual; - procedure AutoScale(); virtual; + procedure AutoScale(isDpiChange: Boolean); virtual; function CanSplitterResize(P : TPoint) : Boolean; function CanWriteColumns : Boolean; virtual; procedure ChangeScale(M, D : TDimension; isDpiChange : Boolean); virtual; @@ -232,6 +232,7 @@ implementation uses System.Math, + System.UITypes, Vcl.Forms, VirtualTrees; @@ -383,15 +384,17 @@ destructor TVTHeader.Destroy; procedure TVTHeader.FontChanged(Sender : TObject); begin inherited; - AutoScale(); + {$IF CompilerVersion < 31} + AutoScale(false); + {$ENDIF} end; -procedure TVTHeader.AutoScale(); +procedure TVTHeader.AutoScale(isDpiChange: Boolean); var I : Integer; lMaxHeight : Integer; begin - if toAutoChangeScale in TBaseVirtualTreeCracker(Tree).TreeOptions.AutoOptions then + if (toAutoChangeScale in TBaseVirtualTreeCracker(Tree).TreeOptions.AutoOptions) and not isDpiChange then begin //Ensure a minimum header size based on the font, so that all text is visible. //First find the largest Columns[].Spacing @@ -696,7 +699,9 @@ procedure TVTHeader.SetStyle(Value : TVTHeaderStyle); procedure TVTHeader.StyleChanged(); begin - AutoScale(); //Elements may have chnaged in size + {$IF CompilerVersion < 31} + AutoScale(False); //Elements may have changed in size + {$ENDIF} end; //---------------------------------------------------------------------------------------------------------------------- @@ -725,7 +730,7 @@ procedure TVTHeader.ChangeScale(M, D : Integer; isDpiChange : Boolean); for I := 0 to FColumns.Count - 1 do TVirtualTreeColumnCracker(Self.FColumns[I]).ChangeScale(M, D, isDpiChange); if not isDpiChange then - AutoScale(); + AutoScale(isDpiChange); end; //---------------------------------------------------------------------------------------------------------------------- From 13eb225dea5ae2e6dea803b42dec5d4f65bfba9e Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Mon, 10 May 2021 13:07:13 +1000 Subject: [PATCH 219/681] Re-instated properties --- Source/VirtualTrees.Columns.pas | 2 +- Source/VirtualTrees.Header.pas | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.Columns.pas b/Source/VirtualTrees.Columns.pas index 7b47b2315..d4edcb606 100644 --- a/Source/VirtualTrees.Columns.pas +++ b/Source/VirtualTrees.Columns.pas @@ -381,7 +381,7 @@ TVirtualTreeColumns = class(TCollection) property DropBefore : Boolean read FDropBefore write FDropBefore; property DropTarget : TColumnIndex read FDropTarget write FDropTarget; property Items[Index : TColumnIndex] : TVirtualTreeColumn read GetItem write SetItem; default; - //property Header: TPersistent read FHeader; + property Header: TPersistent read FHeader; property TrackIndex : TColumnIndex read FTrackIndex write FTrackIndex; end; diff --git a/Source/VirtualTrees.Header.pas b/Source/VirtualTrees.Header.pas index 727d8f11e..bb1f5961c 100644 --- a/Source/VirtualTrees.Header.pas +++ b/Source/VirtualTrees.Header.pas @@ -201,7 +201,7 @@ TVTHeader = class(TPersistent) property DragImage : TVTDragImage read FDragImage; property RestoreSelectionColumnIndex : Integer read GetRestoreSelectionColumnIndex write FRestoreSelectionColumnIndex default NoColumn; property States : THeaderStates read FStates; - //property Treeview : TCustomControl read FOwner; + property Treeview : TCustomControl read FOwner; property UseColumns : Boolean read GetUseColumns; property doingAutoFitColumns : Boolean read FDoingAutoFitColumns; published From 733aa289cac6705d6344e52db066e1ac3c88d0bb Mon Sep 17 00:00:00 2001 From: Joachim Marder Date: Mon, 10 May 2021 20:06:51 +0100 Subject: [PATCH 220/681] Fixed issue #1043: Header height scaling on dynamically created trees not correct --- Source/VirtualTrees.pas | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/VirtualTrees.pas b/Source/VirtualTrees.pas index 774b1db1b..039c2c0bc 100644 --- a/Source/VirtualTrees.pas +++ b/Source/VirtualTrees.pas @@ -9691,7 +9691,9 @@ destructor TVTHeader.Destroy; procedure TVTHeader.FontChanged(Sender: TObject); begin inherited; - AutoScale(); + {$IF CompilerVersion < 31} // See issue #1043 + AutoScale(); + {$IFEND} end; procedure TVTHeader.AutoScale(); @@ -10017,7 +10019,9 @@ procedure TVTHeader.SetStyle(Value: TVTHeaderStyle); procedure TVTHeader.StyleChanged(); begin - AutoScale(); // Elements may have chnaged in size + {$IF CompilerVersion < 31} // See issue #1043 + AutoScale(False); //Elements may have changed in size + {$IFEND} end; //---------------------------------------------------------------------------------------------------------------------- From df21300584e058b095e322eb0393186915077694 Mon Sep 17 00:00:00 2001 From: Vincent Parrett Date: Wed, 12 May 2021 09:42:36 +1000 Subject: [PATCH 221/681] Moved constants to types unit, added more aliases in virtualtrees and re-instated columns.TreeView property --- Packages/RAD Studio 10.1/VirtualTreesR.dpk | 1 - Packages/RAD Studio 10.1/VirtualTreesR.dproj | 1 - Packages/RAD Studio 10.1/VirtualTreesR.lib | Bin 0 -> 1157632 bytes Packages/RAD Studio 10.2/VirtualTreesR.dpk | 1 - Packages/RAD Studio 10.2/VirtualTreesR.dproj | 1 - Packages/RAD Studio 10.2/VirtualTreesR.lib | Bin 0 -> 1165312 bytes Packages/RAD Studio 10.3/VirtualTreesR.dpk | 1 - Packages/RAD Studio 10.3/VirtualTreesR.dproj | 1 - Packages/RAD Studio 10.3/VirtualTreesR.lib | Bin 0 -> 1167872 bytes Packages/RAD Studio 10.4/VirtualTreesR.dpk | 1 - Packages/RAD Studio 10.4/VirtualTreesR.dproj | 1 - Packages/RAD Studio 10.4/VirtualTreesR.lib | Bin 0 -> 1172992 bytes Packages/RAD Studio 10.4/VirtualTreesR.res | Bin 96 -> 109668 bytes Packages/RAD Studio 10/VirtualTreesR.dpk | 1 - Packages/RAD Studio 10/VirtualTreesR.dproj | 1 - Packages/RAD Studio 10/VirtualTreesR.lib | Bin 0 -> 1153536 bytes Packages/RAD Studio XE3/VirtualTreesR.dpk | 4 +- Packages/RAD Studio XE3/VirtualTreesR.dproj | 1 - Packages/RAD Studio XE3/VirtualTreesR.lib | Bin 0 -> 1149440 bytes Packages/RAD Studio XE3/VirtualTreesR.res | Bin 96 -> 61540 bytes Packages/RAD Studio XE4/VirtualTreesR.dpk | 1 - Packages/RAD Studio XE4/VirtualTreesR.dproj | 1 - Packages/RAD Studio XE4/VirtualTreesR.res | Bin 96 -> 297436 bytes Packages/RAD Studio XE5/VirtualTreesR.dpk | 1 - Packages/RAD Studio XE5/VirtualTreesR.dproj | 1 - Packages/RAD Studio XE5/VirtualTreesR.res | Bin 96 -> 61540 bytes Packages/RAD Studio XE6/VirtualTreesR.dpk | 1 - Packages/RAD Studio XE6/VirtualTreesR.dproj | 1 - Packages/RAD Studio XE7/VirtualTreesR.a | Bin 0 -> 5124622 bytes Packages/RAD Studio XE7/VirtualTreesR.dpk | 1 - Packages/RAD Studio XE7/VirtualTreesR.dproj | 1 - Packages/RAD Studio XE8/VirtualTreesR.dpk | 1 - Packages/RAD Studio XE8/VirtualTreesR.dproj | 1 - Packages/RAD Studio XE8/VirtualTreesR.lib | Bin 0 -> 1152000 bytes Source/VirtualTrees.Actions.pas | 2 +- Source/VirtualTrees.Columns.pas | 331 +++++++------------ Source/VirtualTrees.Constants.pas | 110 ------ Source/VirtualTrees.EditLink.pas | 2 +- Source/VirtualTrees.Export.pas | 2 +- Source/VirtualTrees.Header.pas | 5 +- Source/VirtualTrees.HeaderPopup.pas | 1 - Source/VirtualTrees.Types.pas | 193 ++++++++++- Source/VirtualTrees.pas | 30 +- 43 files changed, 350 insertions(+), 351 deletions(-) create mode 100644 Packages/RAD Studio 10.1/VirtualTreesR.lib create mode 100644 Packages/RAD Studio 10.2/VirtualTreesR.lib create mode 100644 Packages/RAD Studio 10.3/VirtualTreesR.lib create mode 100644 Packages/RAD Studio 10.4/VirtualTreesR.lib create mode 100644 Packages/RAD Studio 10/VirtualTreesR.lib create mode 100644 Packages/RAD Studio XE3/VirtualTreesR.lib create mode 100644 Packages/RAD Studio XE7/VirtualTreesR.a create mode 100644 Packages/RAD Studio XE8/VirtualTreesR.lib delete mode 100644 Source/VirtualTrees.Constants.pas diff --git a/Packages/RAD Studio 10.1/VirtualTreesR.dpk b/Packages/RAD Studio 10.1/VirtualTreesR.dpk index d6f90edc6..c026bb2c0 100644 --- a/Packages/RAD Studio 10.1/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.1/VirtualTreesR.dpk @@ -46,7 +46,6 @@ contains VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', - VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', diff --git a/Packages/RAD Studio 10.1/VirtualTreesR.dproj b/Packages/RAD Studio 10.1/VirtualTreesR.dproj index a20f93ca3..70bc0d543 100644 --- a/Packages/RAD Studio 10.1/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.1/VirtualTreesR.dproj @@ -91,7 +91,6 @@ - diff --git a/Packages/RAD Studio 10.1/VirtualTreesR.lib b/Packages/RAD Studio 10.1/VirtualTreesR.lib new file mode 100644 index 0000000000000000000000000000000000000000..7449e09376dab962c6441b88a431cf9d487de8db GIT binary patch literal 1157632 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�Z2`HFVR{q)!{RM#AsiMVmGlx=>fs_Lro>z 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�QkkA+|1fDF5+Sp{H)nLH32V2(< z`M38DH4XWP#RihFBEoDq2@R;j${_jyJOmrnA!n=@)cJ4`6dTLkp7kB>=C+=W;1H-i z22dYl`yGNZSpz&lba@9dfTT;!2I3!USfy{T=emTiZ=*U@M%$?HTShAtQSon;dZ5S)?b+toZV! zw%BQCqr}~wwZaw@_*uy&cQUWTT2kK3u{PohE;si7b9-XBfYq|Gt7EOl?P&tA5S#30 z+Z+NTiv)w8WC68yj3hx54lAl)lbhK+&cW!kozvqM+D50s`{ZPf#gXkeL<{I$D?GO6 zBdDC>QSS|Gy~BEo_#Zlie5Iqd9|j3-VOIavt$VQMJZbif`^s!aS`tO8v z-`P7njBo`Aw>aV#R!cC`-mT0@qK$NcE+Qq04k*-0#1YQwN|_fv@kji_R%7|x=IcS@ z3?Y&xm$eA_Mo=M>b`AnpvnP z82EeP3Xl(eJq9P;k`sJosaES*87N)2_80lBM`3a?MP~3>gVXOUkH@<^(A0~-0TNLi zs@D03!n=ptM%=ptArX?P5UzY|xw5$3fpEy%qS`S>=V|@coLZo>nRmCbYW$;xhTgi6W;o6dSIYo)|xY_Irf}Rm= zMbHc;xj#|mH{*1*iZJOE$w+iyL$*>@4{z1zk;#NOL*r>|g%K5VAy$rc%d!u1W}vPaZa8^F*Yp-F&~y(>aKuMhSjxM&|@Dr3eS?Hwt^Bz@ozMz?2<`F+m7 zw}rgB-9ZSu7YY8-p1|lTX|!giIeGQc!uNAmWz`J5>v;`#|}sPiqfs z-1hBK6{eg7Th(19vyYR>ERm1$>l=8oRFR@%R$Z6GM!(} z^k(PzR+?%Nn&2Cj>72t*!pfiU&mi{s;hiBp$=5H%EN?l+pO09?s?G>`N8Na-Fqb*O zK30-)XbFfIA^%)*m&1HwndZar+$%?nJbeybka9@`?22QvDq;=Q`>ZGE^I=Xb??|H} zH@c|QQMlE}6uTPj^I?YV=pF9e;T5W*QMSQhMq+bP-BgHfeXLno9w|z8=wxfd*&Og{ zDn*1D6#X!2)u84q{F+=1R?+v#H%eL1Tlu zW!VN99%I>zmi*FDODIF$mK5FtO%vq8v1P__-Ru=tcx2(FVZ)}$L7!ctF~H}RST?Hi zP=+yuk6PW!Fde-?a7QI)vMklf1G2RYTKEQ*=f+nP3R*tU&rcY>l;DEPX459v$t0_(&4i!X~6NFI_y9ff+S4dyRn zD*WeeGUP7x>SqFmu*Fb2rQ>? zANE7lv#VYilAOK$8+A`HHsX+jfuB*FKE0Aw?e>ztR`#ZuQz(pWXgtu6jeS#(tglhT zF=DX*L=*C;Y5o3&?A_;d0aBjr4elc4o7MAHA$;!&`ZDrz>FtHv2_>8k7}$h^UecvYOy?Ef3lR-u&!uWG z#BkhLZ|GVehdJ84y^>G&kGA8%@#sf^f zj;4Y#RhbYJY|8qgvS+BQR^a|$X2>AsmOiUX}9#(dt z03#HI5Uehi?c|l@91I=-obV=idG112yeF#z2Wo;#U(a}y$m;U$2CwrEtzg~Mb6+=@{-w`-DnqkoSOyzLmylCGrtAppOi2^I&07TN zGj0 z+G4d*ONdu#wQO)~h3OPqX}Z&p#>a}~bqm3|iejIWh#&_c*}3?Jix~B+^`Tt2WwzALWpSU0t&u_8k4Kr0$els=y5A1K(4b45M0l zncQiae(O=h97)4HOp6YhgvB0-(F0t8QaUY5nc9XOPld1s^I1aa+#`+H&XL!W0wH#%K6(~YRVIL|q{jwSM zTNhS#e}T56Wbt!PiOLRwY9w@TVsT1U8dX{yAP)??d&5H^()!cXBl9_cB|GrKU=a}M zmmwqH2h^C(-o7bx*d})nn%g&4Gav5JiyiQbogt<=Wdvl298NZ?3e*E4gT)&_&Nu-i zG5mJJoWlt;B_BY69lc{+wEDzO2hM@ZZdNJzsUDbAN8xu}4*{b)*`w?Z;}|HXTL9T8 zqq<}eg=z_fOr&fkX+1raQmq_Mj~G+4N>rjiYwvLXkQW+5u>_{_!IV^|oMfiFNLDAI8rzIZ{JG{zmF95mpwW!Ll>_U{hN_7|&W~{;sp#|fi zy+kF5wvlEL?)-6pQ!JE)_ERF?M@y2d`9Wkq!Qk zZvg&o48tzauf<3!MNOZM5)n4dl2q*2tdasyRz zp}dVM#DZ)N6yYqLVe4ozv~|8_pBRzvb5b)BPiP@0x1`5%(JFBT{ikp2B2KcY#0XKk zxq72>kr+8cqFZb>_iZo{YEX)+`oVQ6*>?v0(!o+5K|&W@FgETgQEk+A2E7!Lh>YJ< zv;>(^`^swR}3a!yv2 ztUfx>y~`I+4jZK%FqFH&(56AJ7Y9s*BMqvUjSf_4O4S|m2drg{DyKh)yuULg`JXCA zSa%pEV8ucQGi{V{e%tne9H0R@$bN7V@^LR62T6H%1WSTc*HxT3*BN$ zEj&UnK#G+bGs&b7s>iou*f5w-@e7%UP717}om*X~be2o1%xEqqgs9K<7@HQbqs_E$ z8&%qR$&7M77Ml_o9!wC{G24jGI?nU&IAgh>+P2BFYhnq9Xa!~Zx1!NcdK^R91s%M~ z#|k`GI1x2eT7yl9aDqK8Xry(jvYE9Dk(*L35I~e1t{@1NrV7qNg~4@(HhdI=V0nq^ z+?MHi3gB%QI?BaJ-FDN#sOqE(bO&NuQZWVCtg9A=YlTYz_(u?YS3ij}s@&dMJ3=E~ z!Z&tJO)0d-GS)AZo5mkxc(Q(QQdmDwFD75$gq9g{Pap;%yt zzg?oz(Y1u>V9(G{Z@_RSVPA3OsUs>Zede(sA5hJcP>jPA-UG>iOl#JPnZ}1WPIkIm z6^>9Ib`GSZXC7R@7RC_;Zb?B8>7Od08tA?;m}HQ%i@|;kppQ*U2SlBu#N~t^PhM5>v@M>Lu zLB^$0SQ$a%3l-vP$R-WRmT0fR9}Jn^lGG+yl-!_Sr)8={Svm2v;)viwU|zjLLZ?6j zMD1u$W;N}~#I)(RdX~?To#CW&21U<~=y9;`RqnJ(Hz=6fHwH;6kd>P+9fB6}Hg7_z zm2OQtY2OqB&WOzb$4ziwiP1w`+dtGOhN)bA2o9kuv@x2pG9iVLW9`V^;l6IT4(t!> z3^mYT95yf|Guk*22|?PhSYSo4<{4C6|>lF^&Oqosay7VDUL3I<;wIq;U57y_MbVj)vw+r`_Tohby5 zAO)e|I7G-RH+&_173g){1tF|X{Cm<_u${d!yJ9k(bbLt4Xxmx~Dikj7+Mq57hNNeR zLRd#@B900)eDD=23|+8{{_=Jun$?h9bG@Q4fM$o^QL@;+8qiQ+TC!@)M;7YM_n5LNlUZQiLHDOM zr7#lpQ2-x1_{J84guc)|p+uTYw#br9?+F^pQn#y9`Y0)MvLI)ql)Vct7D2ExGlLbi zMg}<}dHiA6Xk53p0J&rsA?tanF>KVtl1VQN;;a5rY$DN$89v@73zQVn)9Yq) zqRbVBskdB0Ez7Qlu;l24FVRu2o;SUI!U=%jN<;N0bBon@x_Y^}t!prpfnoWnVr!2X z_(E5t&voXKRYo%Ra4!bDZsB32l#FyOLw?iTUr5+_@ea5~LYl0)N!Lrh%_QO2k%K;J zXrSRh0r>^iGuYJ!WSc39`E$6wQE?g9L8e+q=5>o2m=rs{%0g5Yc?Mw z%uH&!S*&2J$C0ItwNwHi=93IHW-OUD$%Ram&5&W0h^w=i@}9y;_ZctRK{c#GzUUdo zi(RaI2-4V)cX$)K92M6&25m*aF0-wl&2I#+?hW|p3v%s9g3`(PD0W&e z6E&{S3~OYrv9`&oUBhZ*qT595BN>UP)jvcMfzgTwWzs9s<)s4_nTdL3qAOXoEH67+ zkeO_|Ojg6LkjX58e#~@zGTmHuxlFe#1h-{Iq$o30f1Xt7U_M6uy)xDPtf~vTW32AX zuu*Jwli$-jvI}^V-7!~kT-qbbLdEr>7ADO}P0;{k(|+kh&#aM8!m47kM2_3m!CQ^D zci4Qt)AF8p9(?JU?{`_=w|2R^d)g6bTwjC_$@l5L(Dza{;dFtSXtzwX9ttV_bxwsF zg*ywuaj6*?dWhV!V%z$jwq|UI+-RR~8}?OXsI8GvOtH4*Z_{y2C6gp~)k~YfT~aSo$fkQN-miTCW%2IJV4n)|{}u4D^O{*AJrzvtlVZuOxPZH5EA@%SOo6 zW)hPobkc4#$?9QAR#&mPa!eYNdAomj2hq8iaGy@3B{`VO>4-F39W~}Y;V7GD)TvSM zcsVp_k_e1G)R!iy#=H~+Wxrurr}uG2dChx=1*2}oNd-~h*eJ_W6LYU|nOT#(w3iDD z^}F88V?Z-^)6f76GLr^n^YryJRVTMRY*XtU-H84fNSCzSXk=~-HNy=|o7tla*+_OV zx^^yjVWnJXRK~dmP{SQeU<~c|v1^RyjZ%Fe;(iV3`of3Jm!iSYoD*h#zcn>DV0y&s zHCsEp*FDzf6~{-Y8l#vsjs9pxxG4EjzA=YvqtWl}-r*g(!!OVoJG>?jZ9tAnC!^vi z#%Xia5lwkqEBX= z;NodgrjgI+T0BjLVJ(*F5F9wOPNa zvwqcO{i@3P<;wb1p7pCj{Q5c7GT+@Rue|bJyc);WnxU!ZBeAmi*c|FQm>>!T}BanT>ANvy?2_z;cl5~tNPtoP`beW{f zm+A5xU0$F|oG!=c@-kgsp-X};r+H)>n_#Xu`fW47ys87#@J(I$Y5XtNEA~NrFT(HN z*GEzZdRKf9iCugU>|1>1>EDc%$~%gZJ*{Qq9+*`Th2m3%|5+^{U2}^~e?etc(hEd} zGURmq6%~JFi{O$lk~fEiAXhyJL7vr;DH76)nd}MRqaAk%e0Q+(=V#*ayU~>oiSJ_e zy!b9*XT`URZA8=FjoN=fd{1M)5#MEOJrMnFR?glpzNfPpY*tp3VS<6C$|+Eyr-GG~ zi>J&5vx0$QQ4+DV@(=IZSqMWtrYuWD9?UliZ8nAOsQ8dlLy$}>9N0{M+N%3buF zO>Z`WnM#0FtT-E>NL68%eI|v;%c^ls0gI@F3N`~y3wO(=5p;^)jvmg6vL|a$t)tm& zvPg+0oBM#pazsGn&&p24J|GfbLF>1iaHKZH=fUVwDy2$vq$FNA@T1ZtQIyfL7HqR{ zc{9x(Y6H6U5;GY32C&*Ei-@7S5-#Ej6?9kSiI*m->8^$@wRBm;BM&DQ^GGPMlt;pe zCVoUOeu)>!YP?7;Mb(n4=+a1+R=RZ31t48XKyxJl$dv>PR}#g$k{Vq; zfeIzRh*l<1Xl?SlC^ud zaXG^$4|x`|t%A(UNb6)0VhwX;kK8;t=Sadf$xswijiKe&7mq~c>}b0XG_Fis24p0z z1~P~Sy@}O8Mq&d|N-w?e(ThFw;s_DWpVG6>(6cYmvtyL_2RyQmO{`;aOdm9}m1Q$o zn*8KPA(WV6NonG8P*LJa9`Uh>B3FExoI#CZ=$qisr&^z~uEzH(0yFaYAFhAEz#lO1 zKLi6uA9c|bI*lgIGMY1|(VRJr_|slpGW2=2D$eA#1PkhlnC(1ibROdON{Yr6Z`me35`d(5(oJ8i9@_LahTtb z_%NTJc${nhC-DSdnD``*xDyjR(vpbr$W4i-c;v>!=XqpNVvJ&q%H9R zkGwk(=aG8in#&R|^T_hVD?D;{;#D4LCnnmFIKd+;6F=pV4T(2+WMd-1Bb|wpJaSLs zO&(cAD~j&K86NQ@&T{QHiE~{0-$}+JJxLdjyenDGBRpBbBaO)_9$8D%_qt>ak2ELe z@km{=mPZyQ>v-hm$yOd& znr!EhhGZv?+>rF}$Zg5ZJaT*Tejd3a`5=$1PWJQ2n&cpl+?gEWk)~vTYbTN+eoJzU zzazPiFHUOwoyh}yN%9cCHF=mXO@5fyCm-j_l27o4R3=Z(o@d~Nb&zApI+Z%V$(o0C7_?&JyHlKd%OpL~P2CKJ3Z zd6K_7`6jH`U4er###{ zwV4l0-OqPSJ;(>A`nhjvknfxt;=85-d}u1fcTbJ+;i-MxKc(@&)B*mUsY5&nQD$W7 z!#p(gI1f)f!AGY)$@ff6@Uf{F-#hgbe`xCS{NbrdzHjQw{E?~W_@h%V@b^x|xu#7W z<630uWv;z%>J_f-pL&&R2c~|&wS!Y9xc2_3pK|R3Q*UtX&{TqJ<5MTO_Q9z)xfY!| z&9%c*XSjA`3Z(K!Qy`TOr5M*foN{sPu~a$NK9Z{7+DB7WTzfoK&9#rEYPk0C)I6^J zajKSUPe78={v@@CYoACh=Gvd8mU8WrspVWdnp(xRKTECQ+MlNyxi*n%=Gv!Htz7$b zs-0_}Np*59mhy1z$<$`9eKvJJ*Zv~)AlIHs^>ghnQ-fUltJDzJK9>q`?enP+*S?S% z%*Ioxd*M16quKg3%K-xbimUHc2um;lJ07KXQ6%1Yb zH*j?AXIKMi32<~R368E!B|Kb9fun0D!O^v!gQaW#4wkN+B9{IpvGiYnrEC8ImahFL zc)E5POkMjWxVrW$aCPn1VC&i$@OAAiFm~;2;_PR^*|pz-wQK(c)~=lcYuC;bYcD3& z4owh`xQMlv5^JAEti6m_dpWW8>BQP+5Nn@Fti6I*`z&JZvx&7=5^Jv_);@<=`(?!1 zFDKSsO|1P2u=dDYu=dE6#M*0!wO>W7{c2+E*AQ!;N38u?HgOk=m+A6$2IdW6yHceA zgs@#H764^5t4>3w+{`BKXK~2TJ<&J2`KAM}h06CIJAIM}CHl+gPoifVUW*i6kYI~Wg5>lBIH8^UiKSzjH!f*K&6JbiBd_w{54UVpSqzuk)J zy~vI(VX;yk*&kcLBiF~?!6T7aizhxS*2yCSiLF=^$F}pxff!bl?~9G`$U!!-g~g$T z>E?u+gRh4cM_&>^ItQBpuRB_DzUp8+JR>@-6MEFn@>5qtUy6QTqyV_gd#axN#N}5D zJ9-6+&Ek>w(=upA>{cH6Kn$z053q>`SbQd9eAE78C!)VRvahcCN%};8b!1JQxT}2D zRX4D@xvo0Ah%~sks|jC+yK>yk3s>W=0(Z5jR&!m|g66uqu&W_{s(7+!|FK2e9(Yj@ z0F-~!9s7@+M7jIwToT03qi2u1>PWjB{ibL|l=@(POT+6&Tz`6`{UAHyfyP*p$ANgL zbbMajDOb~m@V`b~b^DLi=t!Q)Vzq?Y)gH8CVgrlM62R_>{x*7|AwKS^i#FBB(qBFJ z+P)jtHN>N*OWvZtcbtyiT{EzH&3nQX(e9cfo?!8KH6S#;JN!?8aIfscX)HDe#j}ae zvN%+ppyU0=&UW+ho^w(6IY|}K6Wu!-1&DF?+4R%X4X^bKJ_h{vpI_ELK0oR{E0fdX zk@VwN(Q)4qxf|m5ZrRd5UN!g--kQl@6q@T(|7pH`LcG;s8*h>Pf%~>>*$S##$6^@L zn`2k=$d=fRJaTVhsV6==b{E~Pr@PHOawy@YyI~%=FBamF@x*@md6<5F1V7otU$Qu~ z;G(r>&~**3k8eFQ-f@%p zuZ2qX|NGhJA4h802ktYS(T=mx9~r58%EyaO6*s(Yw(bK+io7DV-ni4z7tt5dq!{oy zig$MOW=5)p%Gj+KT!~h?>)??O#NDvy@!KU@CZt1A!uIqK6Is&5VT?mt;vdEg*^{``ly>_1sm zdEkg~^C#k_w(`J}^!_WjXpdB|(ER8o7YIVqMw4qlwq+|1eiCoLHk*}cKmQ2sqHY5B zb80r=OA2Webab{PE`L&`I?k4SE8309xkO>7?u|CN1Q&3f+L*p=CaBXX)YE%(DZBoqpkpMYF8k1sPLVdS>13L-U zeHuY7v!N}F)e%+P!XtwTDE8*WIA|sjq`Qacg(k@5W)_Ds3~d5R_m83eWzQ8gGVRo? zXir5~v{6>3O5kN;HH%*^(srZqXa=SYSHo-LRifQx&y`SLT!!be`9@2sF^`o4tKc)3 zCf?5`_Odvt4Uiai(XCa{N6Il1fTfO?e619q_dilm6uu?8vHIzvPod#DYA{5iKQb`Z zZ#-^z>r`3vMFDUBY8Ed28qMvZFSmXJEV%?bA@qrhE5jU7!>~noBam`aVhyyQv9&ag z+C{bIusCKtRO`8=MJ^`tPux`$pDUgTmbfVjKDxEM^4V!}ejHd`93G4|8B=bk;<)=9 zwczK!0lb~%gX?P3wRcyI2V7j#G^~c?3smb7(M_gL8yZT7Q`@oST zqA<}c0X2b3Rb@O2*H%8$1mYAAn<}C;=RbNH!U+JJns(lOc4PQ&401emo#5A~;a7oT z**$v0W%JzU^s$83N7pb2NsD54605!&3_1pN_14$`_;6wzO6AyJ(9HI2%xtlL1|xeC zGg0)(>v-gWCqcJ|*#tV}O4%tym9kSl#^N>N**!!+qDugOCqqjOEYL*Qhl%h=g%*7n zl1{~k23F4wcgUa7_77Dp*iaR^&HP!udP8~mdh=(+>J1g)GT^`G+^K6(^dO2pRfU^f zbW?^KVmp{cCop2EttNrjh>5hcIDVC=V8gfWp^sTj>cgL!eHgvF+U$V+XU{*nO!k$K z%Vby4R8x6il0kYAs1*p;MW&wZPV*m`gx-E49RW(F_-aPmduZ=d7ltW9xx;Z z%2$&&pCM6{#Od!H-+=s3`Rwe0@rrdu-sr0t?~a7l6=&9N|0C5bbTyH7)%e_Kr)!+M zqMhaAToUxD-kn89AW4`k@HDEf6VKVs=-)?ss&rx{0)G^odg+mlGiGxAUG$MNM>2=6pT_(3>W&tuaV@@Mow8(u)#~xKB%D(!Qyz z9qKGz=}h>CvioV?#*~}Ohd;s+S3>t7);Q!No*sV3glpQL^nZSx7 z)(C@?>v20He+f(c+jk-biutc{X)v>-r;XxDj^ufxTk`5}YAH{4sitv=p5)q`B)X}( z{{D)b_p&>~S@1Y<8D!YhLSF>k^l3 z>$e^H-DH=}leJslhzv!=Y_gs$7N%F0xRdGPv$L_HJ00X{;|M!v#?59$E$Rl`j{0at zg&UAnDmCDBU`E}5tZbqIORSDS(#^$Tq2d1CU_T;@NDBg1T&=AA?G32RvQjp=pl~$J_6MQ&-FtK37eqI(NB#9FRttXs5{O}>i` ztYpUK$Y@A*M%{u!8XfAG&@Qw94DN1!IM^pT8>pPcCOfR26AIP+ccX^KEg&FB8AF!56kFoBaU62`)mV&6h&SD zN#9|0v}D@^deQEcvEr!x7}obNizm;5ne{*&BL%9|#wJIZCFnY3lwBH&x40k{n%b1e{A+J z))jyu+|U&|aV`=B5j4dfJq3>^)fdQ^_khMIoOsy7a(o)6tw15NhtlT-17`eLgD}Xm zimy+m$Pr7iw87d0=CF-fcE7Z|nhrUiEq2jr?(i7-rN|l?b{S3K1{JX!-OO~N;>Qsp zI%=SB)@W2?iL6FN2@Z8;RwKu2j5w!?<)Szl>Q}nm#8@+&_v+`l3i;>pE1gv3mN&M- zh;qLW=Q4VCqbGvF-o15&;4Fj#GLNdE!h4HwMMFNM4d)Mqe3HW#f`xhKqE9bXIGDxR z&k^Y{7^m8IE9*1r8_}buncjl^g<4rKhT5GfaCC|l2W1WGc`^W93+F7DBq%PhDu&70 zVINGI1(Adi$$al{0Qr27Wb6DmA!U(xa@M~iR4OBZ& zLKce5N7I)$L`;pO13fpCxD{w}7R^rSa8>x$*Ift zY7`vHO*p|CG*L|8{Lb=tyt@M#vF$oka70+Cwh{O4Ku8{9&hch1>q+j$eAe|*E_Gn~ z4~M*6o^~1LQC3{#QCxVjZugFQhnjuD8;E{pv=D~jNTyraHaN%Tz7Ze14mb_0ZIyU) zTZn-c@`4_EcMBOJSkR-9ie<~gCGdAE$@*?Zi*V8uew%P09qW~lPsSgBw-W0#ORfup z7$b8^#*_tO!Lb`<(%QvvU0~zM&(6g)bRl*z05O;@9^gluwvggk z2nGwM(vt6p0jcVX;6=o@*z2@Tq-7E8*W|fYktu_(k{VdacG=ETc&6XFbch#9+p@VCp-zFt<;Eo6_Vt0vqk zLueAz)Y+JqBf`<#JqgZA7VginZz1fSpk=!HPPl*ZdYZ*kUk6=bCd)$EsI zeh?0d34b6+i;^a1%4+uO3pB`366M*hHa)s1BBywEcwx!Y%ErhNN%~f`s30L($1{vX zjUs6_%8%>(6V6v0*7^5CeV7jPS=jXVgYoUhG-2_`Z-UnsrRpIoQBCZbdNNi53n+nmbI^0sTpA;ns zT_GlV8_wo{S5qnJs0E|e?cF@w-4rqQm@5fYkNBXJds&Gfs)}t4_)8l}=1R?+vk913 zY}lY~*|O~ZG?vY1$uAwXgfa-cSUn{<-tikVcR#X~s^O7^mxc|SCI@|Xjm7|?-ISH#)VT*gr*AxR-8LR^p7z@Do!ok$1o{nTu zPG%65lCIe=g0neOX!?{v0W*tpP(3hCp93~5s%Xemg}9jCK{t@IG$&;#VuUX3=S71#W9?YudO0ZmPE{M=0fo#GCeiTwr~efAKZ!1cp#AwPAFPsqmkpgU*QK9Yh!g zra6X3k!mgp+%jVit7q8Phc#(;D2PF$)PxJdqp47Y?xGsNH5dy!sW%Zm^@pn3zF;ny zdf*c<ksb&xEuTY(63OsNK@Q9@w#?7ut6>~LwxPN$7_kA;H0W+b9QJOrV~^J=Y_ z*D0+32VgEentY#=*7U&0!t}f)4F=`TghLCvdz3(Q=3*>95$?dE)ZiYO1Zr~9U@A9o zoBc9<<_bk!sw!lhSt^OZatg=(rZRq%B^w+)5&4sjGoM<(!NAwsMzzqVS6U0*Uh>xt z5e*?!D2#1rJkXGheN&ID3t|-gMm|FmR_R(PU9&-ZwSIrY$}E{JT>wmGu?^H4+$H=} z|Jeo9SJ;kZMzXZ>CKHInj?gCwf9Myl?S9N*{W|>N5d^f@Bc^@RM8uqC#cLU8{L%zy zh)JOfsgmKE^OB){=t7|Oi1o+s7Y%BSbgLgbJ5E$%*ym-3?=0~to7gto7xdC58{7$E z?_77+nFeviTG%kESuwyQmNvFa>3Ee-$E`w|BRL zxj!S0o&=Luh$tj@K=xd!Y>wb4Zrzp_>LZ6a4C`A*x^a@$5b4Y3V5!Oub7esBX1`wG zUPmiJ2m1!1ybO_{KATVtQ9-?qrh+Vjf=yXpRQ3#&B_k*rC8iaYg}VI`)u(o{#MdvN z!XEOT;-GUbwW@ig9=irO>phz2)Jl1^7KC~aWjR0sW{qAYZiNhz9l6Lx83<0_UzwFrb!|ELT?z_Mm1&P0=8ASWvV- z)Ef+$>R`&2yNWem3vIDlsU^g#v>Xdx_C`EPA)GB+l{M#O{@AvyA&rj}h_M8YHKcC2 zh`xbXQ)!rmMn*v^W0kFLv)|*}jbl+6wz_I|BP{ZD{tO%VtepYwk@ormY07}6&3@z9 zM20o%eJTuL%?4HA9UdrAN|-1M z=YD)62*XOG?_e(mIWh#&_c*}3?Jix~B+^`Tt2WwzpDs1XJItgob{f^ z)HG0hI+e*Vsa|B6+-aG9>j}G@1!FoEz1Sl$dVnk1hyLoPnY(S+@l*)b?z5g&$*GQ{ zgQmgo@Ghx~A%NKrVePsxmCQ5GAit9Y%#2 ztMEc|6vjh)iAoS{Bh4bB=HS4kSSSnaCk+exNxUG;P6g!{@Pr`ls1=rz_BkSmSz^v9 zw^5}8{(*HceMi{p>@$_kT!1g*-N!tr`^P+FodGOUP7rZ*r~PzHuyun0Ur*zrcWm8 z>H<^gZ+aRqt@zAW<}x0`>8cXdiB=;B_@<5xO=7VihSG}G<|Y`=SiC6YP_mg}l~Sm zG|e`c2sJ3h)y`gVa9v9F6th@%ey+fxi!K-&ca^9%YCD4n1rK!-jNeqW1er`Ff^>O% z`^CCbl8IsPX7;Vz$AfK;mHN}0wt%9NN(oAOR=5}-Ol1<&reBrX zjEiCKJqq%Z`ZhX6N%%0FJxJu+qgc5Ce&O15)g&Qw>0LCdtR3zfl!ox54(c%iqj~QI zbb@M&Ne)$Wo+?C$p?XpXT&LVvg&tj)3WziAD%8VPV3VRxQJ$_vqH03fDd%KW$?Bs6 zQOKIQoE_;+BkF{d9DiC_m}gQB@T!y}01-Y3|Jm{JRm z5DbuFrN&G$DMZHf?LbITs-)T}h|EJL1=jh)!~MEYsUjztORCHarzPsMJu;WDqs_E$ z8&%qR$&7M77Ml_o9!$nm3zGah&Jg}o+ctT2O)PHta%5N|3ypr#;~2^==-^d8R^Yk9 ziKwAk&bW4JThU%S)OS11rOGRF3xu zbD^VLjMQy69Wu5l077>lrX>|qfX%vUVVE}*{HSt!YwZY)cnROwH8mx{PH%7oZr5PV z$8J@$WE~u|4a>16nTP67uG3S+N^}b!JrbshFlhPf@tX==1C~VVc+I~WI!wSwbG#6=eP42qdb?2^00FtB|Y=t0=6)Y zD8>dU=pp@6CFDS}M~`m|CK=?k&hWI|=pn&6?{S}?&6yaLc-=NU3OADd@So&_(v%#8 zv{$46C3i>EJUfG4mDz$(j$D39MA=M0%I&EP3sY%lC@N;NC@e^0-%<3f&|HvyLsmGi z&LzJTVIxqkG};*MF%*#ku-q(4o3N~^TZmPA7Ws7g#=Jwi18a8p$Hgi}0AKeZ zv6p0Fld8ei<%MB`V|#4B(r8YW=Y&H2R)51eqLoB$}8a+*!j!GNFP?swhK^3}E z93wI-6H@3UG_D=lJKWdZ2Q#Cv&XfZU#(@P>x}(i!kr2ZpJQRYX*aGE|l<3i?uR?NS zp+babPp}oplg7d?@5DyiJhm+xLPZQj$uUx&omhXFzhoF_+La7}RQPoAJtP(OKCHyw zQ7}31mYEnrp>5f)rNN;}sKEQPGll9EP;jIo^uB>(pfMeugtEP zOedWul0w_I)`AL!+q^cY9D*U~U7`@y(VB>(0u4WYg$fJ$Ld)ncZ&#vO4cRrcAzc-DdQF7{OhXp;g9DkX4uqma=V;Gmbw?I;V->xoiV5k?)&T%V; zM+9rJ^P)&2|8toeFY$$N}zHFPEuI`?3w9}F)U8j*kOWFlK|_7P)`d< zeH~O_tRP3TchuL1b2Zpum&e9~ft9VBHZ)@=u+hFDwBsPGS|#=jIm+aB^d1g53ykS5 z?|Z_a4S_U?hXQobO+KCtPK3#!IpCu_KDN%jiclM4*Z~c)inbkDh#t){vd>KTY58K; z3uc~Xe;969*M)_DSFv|Yqvi3EwFUCR&RcJ$taW~VOU|?Gz)N{RFT~jBm54C3T=1Wo zon22I%zF@~5Isv^p0-Y+g|JR<*n+pKoajgNUuS2V?7sqI#V`W6hoA0?$w7UYbSvUlMLgQ(riU`4HwLC#1Xe;9Tiy~Eft*EP^km*fRNZXHHwbAcKb z9K!~BRxh^_T4ryB0+L41kYG|MV0{Ir!N2OhTK(?8pm_LW>8}&g#A;Uvu zPQ7N@DP$SA3(Fzv$jc}p14Vuco|CubfV4pm8)2v5u*#MhAmnH;j3%h61>2OF&}@@A zj~Q=s7>@yv1}auI!idC%{~1ZiBF+a1Gn1OE<3D3vfs!*NpsU4lDL;ocR%8#z_0ct~ zMkc<^emS&FCcTj@XavmOfUgn3;}9F7v8lZq>nLgyEv00jAafD*pj<@Nuqz}0OH?T{ zU9T+t3U--HSKpSlmNgS?&rJk~GQyFWs4s6KxxzCO^~*$CSyhwnmJTvB3{U#J>qa!IwI&whYGklU=VPPnT5AfSbTaw#D(h>&0zEfw3%g_N%z^5NktOLn+YG032#6UKQRIu45SDqEEy1~Ps|LD$_($y?b;kl zxynS0rLab`G)y;6;c>LMb#w0nnlga(No17o9XUe5bgoO#H7 zD{3JHF!6Hqp*}DkYuT9p(#2;Czs9_y4Z9d7)6!Yh!)B4_mw_f|nF(5Oxsfh6Jm76P z9l3Dq!jj`8=(hDeZO!RkX_V8~+Tp$Ku|97gJ=SZ?>NO*9mV8MS90mR9^)MX80J(ut z+LSkNBpO6E)EChrtn`dx@^YbEOrm>CX45D-PuEZo^xWiZ!~`Jfi7n8JsOAMCp0SA| zERKo!mHo|it~W&p?@PIR$Fw#AnBD<&mM-?L4wO*2E*jiB?a%DA7)rPP%yLvY9UT)8#?BjM3!) zkMy#MX~l5_9o`Hm{W>JNr@@QRzwER6vmfAlo_>EJU$aGGA>H7PqMyqTK+=aERJLSm zrS(^c56xz^=!-9Lpo?CVvY+AF%+8#jk3!esyGQ^2EB*U>`ZvN$X9ok3 z_)*S^%f$_IVaIr&m9YYs_-cl+d;C_3RaX| zYB5_T61m6_&!(;-fe9JyKAK_)hDh287tuxq-Bo$wC5dXftD#FRT^8}kLy5&a5=<=R zk&(o59tkB@@kltahDZE~Mji%XBqDAdz^Pg+_?<<;O0j zHr@&%Ia(#qW#ETL2HC{jERI5{+{)ROpuu=nw58|iV?U|h)jVEQ@qrJsQ*&)Vl(O^Z zsXt~40IozekL+U;2aDnewJccCPfb>IDR(E#Qk>rXz{pW<-nlOJ?v+;xvcvm;F9;?K ziaoX-|M%ekbNK%X{=bg@z4+gc|0DSSWBh*#|1>6vDbOb_uB_!1eAmf)c7x^JNjw)L z7T?ycIfrM9^?X@h7$wAlYlsD3O)U5-V!_u~Sa7Vv6TdD78of5wOT2j(@#ZjibL_pI z_`KM-Cte-X=oXp)%LvT+Y=I`yW^u%oYj&UJy(B0MI*iGid}z9kmjJ6?43>h zWvwWTEO`hHxnHCg)7efmxE*&B`0l_^J&tdhhnC?recz{le@Xwo5_Cz=-THS#|1QG( zloiW(B??u|PlC@EvuSJpFMDqT9#?VQjn7K5B-@fDe}MsG3y{HJV{FSdHa4)bw6X=3 zEv(g;FJpwXwijfr(5{3WFzm`EvAT9X8c0clsglSp{g*dU6H1+iH`#y#G~rDgN{AC^ zLfgB$q^7Bp5O78X|?3H5*WvgIQa!^&x$`Q*=oJD{ zP)lf3e~+D9B5IiiF~M}duVYWwiQE+%On3Rpyg4F-%xLb;n1`%mTKIWk%zm%5zNwnmb(vn|BOMWRW`K7evmwxCa z-=COCa_0?Ms5AmL|`mkBjKza{5?C zA8Y8No<5rC<0ks(2Ha!}jZJ=vFG+q0RV9zpufG9rPX3hE`@g53|AHz~#aQ%Hvw7@} z)J61d1(Itb};f(H~oyz&mqe42xT6jZ(j#~1?YGAlU+GBi?_BfB-sDblt(nfjgN?IotYftgm5^apfTC}HmY_o>V-4^Xx9&6PS zJa)Bqg2z^C;HcMVFZ0+<+DRVUs)0?n(aN$;!`9EQy~$(kn#N;XdyB`mX>aq`b=nyo zyG474$8Ocm^5_TJ`#kz@NycNhB};g$k@lUpCo6cYDOt^98w_Uz>cAuS-6~uT75e>yl6N z>#-B|CZFZ?$prT$Pw=MXOMFA}Wxg?ak~b$`oCr)v22R4um8sy>B13Z$7^8VBTK9D-d?@JxxgQ-vRy;!9`mU@ie zpL(1>fW>-WYLq{idXhhsdWuJ*sWBdnrJm-|q1166-Jg1vM-QYDJo<3z1do0^^%9SM zBK0zl9!#C&(P1p>(LYF?;?a2O4IVv|dXq;Fr!*e@B$oQc=R)=cX;#< zQ=pg6ra&)`q#2Jsnl9nd&!x+G^pDaNJo;F=nnypMp2eeooSws@Ur5j8(Z|y@JbENu z%cD=E7xL(zq!;t(NO~!celfj*N57O_&7)sVujA3tw3kPZrW<+mSb8Im{%Lv>k3N}h z<9{pzeaUOj(J;I~^J3Y#y z-%3Bpqu)+H#iNPz7>|A@{WOn0mp;y;&!?Z|(G%$ekA63Of=6F~1QUHR{W6cfls?I$ zf0KTdN57Xo#iQR(zrmv~r{Cn!|C82u^ats;c=U(qw|Vqr`V5c$DE$tPzLGx6qkqdr z)_M|?-}{x@Lq(cgl} zN8bULkNys9KKd^BeDpo=`RG|N`slxc(?|autUmfaczyIg!0V&q#OxU{dk-=D5@Pmc z#O%w7*-s{BKZTh6RATlO#Oy1H*-s;8Uq#Hmnwb4`V)iqL+0P_qKZ}_CY-0B35VJp* znEf1L_U93^KOf9Kb^(}uY%VeT3yIm!BW6FJn0*Z~`-_O#Uo3Qa*bI|oT;?ymq)O=Wrf3(^N_!Ds!bYBC3F!ZI5IP0vlDM8uo7p+~%LzR_?0)1Y#RKH} z?Cpg-`dcW#m6O>#4+idY)EuxQ0nzxWBrsHvz%Z2rhN&Q@SQO;+p)|p_6M%jG#C+`# zNgle4yg+x+e~=`211&=e?mn2_VsZ|IQ*+qHx!z#6D~Pi3+6e zxZ}2Xa{Ke_NG%(k&SM8gYk4d-3bYPs*YVf_I?;J}bTg0bAC2+BIAgzp>(Lo-Z0rj-QD)R1Y^*qeT3*hhK>l z@BgQ>--zfPs!QB@%Pn2Q)u*e3lK0z_yeuja@giEd{kd}JSB0u4qg$mW6N0B{S3qe$ zdKD?{H_~xb3y*zb^mZOQIEsz`FdMmtC8|V|Z=yEutBCiPzvz2kV6>@1R&r|59o1`p z@nBK>7Z1Pw;DUHl_2H6eoL3w!8H#_u_-~5;=5WPuyuDiF_1sZim%v!<-#3dzCdGZT zhUZ8$-m?8UD5jt`ADsj2jW+VwAFz>gSOSSaSKL<)B(UEwj_UqP5` zP;F_eE`Dw8a}Snz!pMAj(s632Lcx(zHd=+TU?V$NVg_RtI*R`~{w@mqL4)T;g(sL4e>eSpPh?$ggi7}F_2FZ1KBycjK_A3HWJ!8d2IVAxa|DVQG5t4 zd!Fnny1AbkUnLqpS+BiXuf2Nw4{HG*2w<3T4b{Mj=zd)SHDV6YRGUwLSA#m)$Yz!} z-@xG<9r!r{cy+uKIH)#yKI`rx`pNaG+N$F%71W`HszV?@nrzVjp~-i##03Uq8XeK> z^9AHtCJgF5m_z6`;Va%!9e*vpbo_I306l(&I;8g{{(_D^bcQGl)J2`CWeJ=PNTMS8 z0g?tW!jt1(IdqE5rnAI_BHQsIPYDxja^z~3n5T+XKk2D@^tmUCKz#>J_LuJ8SHU7> z$34h;x^l{M{bk1)eJvuMgq3e(GDB~1#Y@HWY@~uEYM{9jD+Rd0B&HSli`hseOI(Dx zb|p;_k12f$qnxN0|A07TM!u0LtkjxX!@e|)j><})xu^MtzqnElQ`ww<(Yfq=Q*_Vp z5>nR-tIQ?69btA5L;mGl@keA}2hU|uHnTC%-Mc$j1D{OT&erg{WlNT_gXQpatMD){ z%!TIT>lBA7LU+Lk;ot?xQJ$5fJre8=FL8P+;_<4t1URTY7@M@i z@4MMw(y*n`H)%&>y}!OheC$}?)>e$avfA49ZMU?vl{Wh5S&pCnTUveMH@?vC&Gjwv zH@?vCty{LW`BeTX@^@47COxIJal_Uv?X4v{{F~Z}@mIQ|d2_RWM>%~`&80hhH`8zW zq~GkZGWKT>!a?xzTM4EKeJf$5SfFIuOfVaKYc6H8v1Z6`=YpgDaVeWIUNm0J#+D;e z0ol;iWk=yBN(xeNJTC-nM3n7;6(U>(I`=Y94I8_HEs%wlX2qn`+DV z?4YwbRf{<-P5{y5`D|=4`e%mhwJ+gip4h<>HnxmiA<^N|L&-Wku_rui>?UT3D(6sz zVNuwNn*y>;-_1^_%yS(ZTVV|Y;t11-fZ|ys2dI$V!U|SgKrUTY2biapjcu|9zj3Hi z7<(cbYa6_kD2qS^8M^fw}R! z9lh|b^TfJ=@y*uwvktH+S-R3Xga8Nu;8hs*Rn{;=4geAAgWgbYfwV?8c0IF%esw4a zgwq>=tBg0FXA>K{&KfP=0m8;0f)(i?R0Cq41fn0YM#XRdph6qcTQOSJ3q>W49S{{+ zUU-OWPwf37HWp(aD`?^Y8yHzQ7-c)z<)0(=aUS5hxOD?=07ld(PwWx2p-|lBkTM20 z_B3$YVhwoUfZJ`s?rz;^)d7Rdb2}SbV)b3mz{4#CaN7Bp5djuWC}U$w$wAE=EId>T*3|f#J=C z7#AUOY7x2TleP=7AK3*mwuBdkS^n11R&cP>f1w& z>&GR+L6+a+Owh$5S^Zpgo@nwi+Z(PV2`!dce+aNr`1cCo->a{M!o4=*uOT9Ow1$Gx zOBb!#lwC~7AYm{Hy}T`pu%F$L<+zI}s}b_vue_kp1bb&Tfrw?Ych`gS%8iOwZea#7 zwL3cl0Tpw++r~p}ia^~8TDWwATOpx+o0WnR*CSlWB$6H)ScUXelY1amE+e-feX?@b zqOTNpP0FSf*wMn}4SP7CiB+|9^vZ$)t6?@-b$VA1tAzO-@({z^Nn z`TwqNT^av6)lOTt9P$*-#)sF|>BQt*r1$$nxrycYx#c)R72rr#ZEdnnkz3>-{T$1M z>XdYcvG_6>et!kKDrLBVLZ-=e?f34kGfhmr)eTt3N%qn^+b%ns@m^J3UcWaI(DQJa z=UFygM+gGxi4}eq_=GCZcCU;rHMon-)YH6B3~X+soC8=*b!5t=PDp^=!e?jzELFYK z$%6d;WV!z&cPKWpKsXLjS3Fe~-GGn}a41J7gQTfraJOJ17qSF?ivndRAoK!JnC(53 zm9dD8+-vwQ6rZCX>t*W%LDvrvu<3YaVp(nJ>E2s~6pnZ}xEi6Gemf}f zsFl(ap;&{ZJ5-QehL=Ne(KX9NZBVa>f0Mg{VeF>50zLGt8CtfET~qN*ZIHUE@m(Tc z31+U9mS`{wg)utxvC!NQeoAMkZVP5C^$~iPRZAynu*pQBNhP`kyK0|!5D+>_d{~rX z>~%a>n=?%4JI)m8pJ-W@je0p!r!>*lOEgps2-Li=L>GGjb23>}PZ0*~=2YmV*t8~* zqK6t%V90X4vS||>5G^li#S618N|jh3USJ@^Zh`1ay|`3V0x(z*u{kllxeRY6H8mmN zjLNzY2^CEy^^;0pjP%J(LUe?YxJq1##IhziK9n?ty#+**=hi&@B+Gdpo^(KcNIk!< zp1)Gh3qV%%{!#TLnT&oD8<7Xw6=qWSz1q)jKo3BBM9}__B=>S^IM$OS}8Z`^-vAs zWPnv>=`{3mIrV%@)HBtjzmQq0m0y6-B+)>RH0oCrDtcSQDlOzGk}Fsl?$~PU4Z%S` zxD=FUyA%|$&x)F7qE}WGg>1nrR+^0z)q)bTS5O`Za6Z_oPz@PKY2CIWhHt1xC0DS@ zh7NY*I%Vb#0|wnv8aCv*ar+h+G5BB-(*%o{4KRq=2z!`jn8VxvYnU5h46_M`jx8{S z*-W04leG?*D~5TEHjKX!{5=D!#gq8^CHQy}$1X)lcm)+DSMtVW6Sl%?+Gi&Fjg@kyM$r*OtsmikLL$HBU7aq3n2^$+-R23~RL3j9jL0%dyo zYWn8mHLxjLln&u9%9o}82qyJuG@~s2B43#PF<%Tjt`+I`c%$%xEoCJ_m$bQ|9^u>+ ze#==ExPe()X(GWa)sG_yl0z&!R&+Iwp3g$!rm2Ohk6gsGX|Oki-N8%^NG{XhYBdXP z28~(|{d#~eVk2Mnz@|j-<+3a`L9Ac?b69-Hf2JsmEa=PU zkO#778@<6wV3V)Hyfjd@g~&#KCUB^&%6goDoLDHv=`TBHg?Z&5fLLh8=}*rVMcVjy zg?Z(nZ2CVv8-Al3Wooj*kWC0cW!enTyLJw5&{op`UQZulVBtttM)R&ryNy2XqK_bb zg!z2!a~SZy#MJvczKC4iijzxd=C#vAd;rrg3ezGs(jeR+vdn!pmb~DtjlSSrz+UiP zioeV7w~)Q)U5qiBkDOLB9h|dHW~1f26>fevv5}>WoG5ZlF7jAg%phxvHZr!DHhLc4 ziZgmY8@ZgJeXK2hs_L1Br58`4HNEPY*)@1vbF%7@1dKpnHX}}Og$Op8ETzE0(5GS# z8IQsGq<#O1x8na4KO4WVVs{OD87|wfBG^#=7RpKMgjU#WRF7}1RnMhl#K6oZBGM8i z`p{*pzcTiaz(9XV-Rq}|zFri+uR8v6-79qo0nXo6^~{Fa)~au9xYPi8xUm+;;i876 z@t2IONS@Og-yrR2L}6sgwCUDc#P|3PQ=6q>gUb87YO;7P1tfwttF>Lc-hX1wc4;AV zEgQXnUo=|7FCJZoXFZ-9_{!0{`I6B``IVzzLfW4q?W=e`jpsM{oY5cP``_aGZ}>`x z(jKxm!x{hn6E6XmumT%yKN~+44=;{=K$KARsPDI`-3#!W!RV;!8Q=TEzPIuC;Gp>R z_CenVumn>LmS2|MDjGWM)6|=#KRUa&c>hTNcvH`7q`aZ0Ywfq(~!b%K#y+dR-_M5}|o<4ds z{?lRKvvJ?EFx^@B!Ap@+{8gPk9|`fnxr_EaUH9(k`E}#;v$tkZ|MAl^j5kKw!{hxG z55L=Ae&BV`dE#`@nCGDH>(PB*XH^Hjj@1YGM6V7cst$Yxzm=8Guq>^sFTK6(ycd2|8493127qkH*9M;}Dm0i=D0pLg_S{6?N* z6@2Bf%kkWTX8_MQo_~Vpck%ogo^SHie|iPK_)nMem4A8AT{D7?OV2@g@@Il6Z$~cwYR6sBwvo3bz zTsB$?!CtE*vEqDw+vsKZTfuK*BR2_?TVk94X4G^b5m^`ymn(swxM5D~He=~7zHt^z zJL3(rM3twf-OaYu{f)7n%jI?&8=XRxyrBfpZ3d{(!ugMKy#K_Lz*XJr!`t5<-hLMLz=!u~SZjWLc;DMc zj}EuLgWl~cr+$1FTu|r#V$oj;gIl%yV|9Ocyz0Ottc!=06?yCKQz&4u`W1Mg<>hts z1E?xL+;Yl5^_%sO5hdBq7a<>tZy3ljr0O}(+jbaC z0b(98RAiTQbRf}x?{I$t>~h$5BL0FQfItX%)A;f7-LPwavz1d}(Z61a`(fY7c*9~f zqOSseLX3+rVpY}l&x~V55I>@wOW!+PLZ9z8EG@5kxE?5p`?TQ(Op>>T8)~x5($5ax zUmbt(Yh|J&h(6x(CX6*#k8kU?W~(q|t8mU%Zpv1k$rinT0qb8M4_9Z%Pl;BdW<#b` zbHYJTKHYw}<+y3Ijw9c0+JIGkt6>3fLhIp^t??h2vf!th%BmuBwYn^PV|5&4CRYUr zYQqh%YdtgEFbC}6Ea;%TVW~m>FXOjPCOV-hZuFMqJ7lfT*vH?y?dKoc@XGZ4M_6LZ zPv@Mh-1p}G9TlDR-#-7&uSF)!^OjMjBj04&Vu~iUjo+@p6@EH)(ep;TdEMx}eEH}w zMCs4q#bcCXgkK1soC~yX@e4-3hnN4zmmjU+*MJFMaEw>*8g{RA>eV)Pww; zw8(dS*!PZvs*C0TuL_@y9~{A&@!n7ID*oxm>FdvQ&{cmm>C>I_r8}>aJ_l*|&US8xo0l72od$QPfa*^+)Vc!^Lk%Zn-eYky8d?op->aTpi68%zd zPabYLLAAo5`VrI{h23%0f%h=nak9ZyFhpsE0^cZ-%}CKRf%EkrU)293v1>2|v)FRD z}X<2>L%)^F~vFqvM$n$J; zu3RrKAH9aJ8}(7-wl;p%=x%=P=s|w@=%|zzvAdC`2P3! zo<{m_F|!zEQYl}5>|8uA`ur#sXg>%kyu$v4;#Z%}V6cuB5KvD7V!+3(%e~^tyvFtyAR}zzg>6({x$U17PBfqH4 zhmwFy!xV3SU(u8?1V;fD<6<2GvHqiJcq@6^Dn_EwOp0eR6D~AfxK*vvp!=K&26qt{ z+{L5!L0=FfAF)qT>gORC{0VisgC(GU14G$=;w(0v>uMnk=mHzWsC7`-=)2F#)91qN zU~Ne)Z2V7O5^tzk+gMvd-&JydrApIxo44F5#VL31n1cL6U=t;Cy35tbCsDLsqVu|hqOH_=gs?Q_Yn-KKu^`of&oz-<4X9^F*F5VLz}+o6}pb!RY}>&s?J z!$gTO+58!z9WeWE3H0>qpv>L_B8(;wEVKWQ1%H+Wzk_|fq>^4DMzY~I5si#y&pcr^ zb`9>W6bACEvg}3O%EC(iVzL`wE(_N=7nTGiZ~7GR{$V5gC~Ra`ST9I)u9Aqti(-g^ zSx}W(eWk)E`wFUDK(=^ht5|cDFmkP1vy_616_5`W<+IqqQc!Ir4q^%@u)ILPb5lhaly~RQ5bTw*IRp)I!lm(Xb%K_F34Y-^|)3 z1OdmExb{l+(;sRI%FAqW0zP~C+1OUAQMF62CFp!WFLbBH6-zQS{!g0E&4Gd&HK4LU6cnZ;aQ&y!ou z3o}X6{U)o7u-izlwM>W%6wEFM z<0wIVSd4_&^|oy?-RPSS%eXO+!L`z+Q`ABeorXyCvB_p8_SVh4trRb75+=AR=%k z)3R}qQyzWh1=X#2)?S1CJ99*KtraY-l)^Ai(Y?65t9j3^?qE2g-^?oBZ$bh&TH1^R zKs3NOPu(QrbYYbAda*WFuU@Ql)bOBkN3zM-useXDR-$_$=UiDATxFp#Gfu`*1fP-` zv6|+x*o;7~R=DdWdn_;BqT7WV%)!X};qIc{kO$y|*i(HTKm}hI>9{lT!ZB>iUAP)` zKv==UkFG0EeJW=k2vUw2JnvTOY&GZplbnmcn)UJv*y233VAkjv1qBa)#GN|;YID2S zEZ1lnn^0 zY6(B*hES2A50b4jzVhC3#STS0%kmU5UFotpCik)xEk;|*@i)*eFQ4R*>L znVW5l*$h9tFh&@g7)zvmYl88rN|iYdgPNwp@`129w|Yi{|ksyC$H^Sh-F zw=PfX4b^vd<63gWi@=xZy&;pThXgpsQw%^sIuswsEd~fW2a*6Ccaf`ho!(Ryp3!MinV)?uonrR;B9Q=tDXcVNyZFc zR!#HfIR2_d#~Hg6&r$j6zu~7kYCDLgl{YDsltRd-kv6+&OOK>2v)V>wcnle;kU^Ml`6}3HKWK za^c63lo;0%GCrR~hp8nL1+;|xXG#zS^iufGw8MXf!+&NQP7gQ1f97WR&)f+AnOopL zb1VF3ZiD~K?T92>gFv#I5l6NjVPv-;imVqwWG_Js*#!t8yB-l_8xTOY5&j!KEed~} z1MtT=2!EVI@W=Ty{Ba(k0Hlw>8Rv0+5qxhhM%dU|M2(%VJw;YCc>f2Ah^m zL&dC^Zk8)z#XIB!A#CtVjOLNWVhAV6ft$`7WjlxF!+WCTTC>LxhUToVb^K!?sm!E#x~T5vKOnI|15L_sjn z8d*4;DZSi79uYd8bvT4R`bVa}^zS+Roe@4Vot%t zTrq)(xeODNg7{xynwU#4F|R6YV#10=&dY_Emy0kj7h_&tiD1sl5zDzwJ5CKcNv0S{ zGPd|FW?wPio}A5F*+{(zgiK>xW=5M%1aq7x8{t(_w+yd5m3)P#XtVicY@|iR5^m#W zzaIo=J%iw^(h~)Sb&V*0@Z*@MaEP%B#EQ_6&od^d?+6@trtmuujdZ0p4_pL}Ks(sT zbt0s&usb6=+ZRgN&i&u~LuyuJKB9d_OaX)AC*wbi?|%fZ#y?9dgtP&f%n+mdLU=^n ziGb+RFc?N`KaKw;es<^nZ~w7`)E3`hf%Jp;CmttAe*~V8fpi`q%_B%L-T1d%SpSA2 z55F7ffLSA~Oq9z8Y*Yh;mG@!5mO`Q9s+@YM=ir>o<9 z^;D6pQr>M^9j$uwI|So-*qOE6E}e3~$EcPg^O-gi?U}63MG+;4!55=PGBS~n9{cG zrl0ZeXBnizAXe6Pxcyag_E%l9@21$W!kEqho`rCtG(y62=*@S%=XfGjFsX~Tu2VKq zrzeA=rO6#dMb_v8HfwL{LrPvnL%%}ny~DmYUi7^~9g!XNM9PJ&;hEELBcc3cfLcC& z6EdFB6V6B!oT0&v6vMOUa0{sB3`QLGxi?4m{Yz2R0W$wO+B+K1!}~NE%Abg`XOXKa9?Loo={OZvdzl#iLOGl6J zP7QbcO(#iZ$|$VyzAI(z7|%t>tO45?XeAf z^|3xYAL7@g5D~cg*yniwJ`p?F$j3x9Y+>+r1{UqSJKhwD3(6~Qt%$!IZ`v&oa(Gj1 zdHhwplY`Y*TRvP;8*i+w7>3cCQ3SN~7an8-S6CN6yMO$H$gFMKjJJPL^feE?+P(io zwax`cFXZQsF5<8-yI955j-R=E{ORkbDxpc*&dq`C+$_@}Zx}&gg>hPec5`IhmTfnugO^rhxwdh5AL`SM%yKQ+ zM1(kHf>x7bIoB51+a1^#3f-dvAiF&~+dHvODa=04$zE=j01bON9DC3yhk}+Te6J`W zH6`YNzvlS4?SuZ^fjt2Q%Q2{LuFas5{Z+wSyqPTk1z4QO$iTmZQB`cF-X+lp9f!7t zfrhc89@$NSFw!kXkmgQiTPvDP3~d4wD}0&TZ!V#_}(P3XnR$hfvpkoAPj}9@A?W>VRnWtWwX9+Yq;+_JHw{-N~L-9Eg zuf#Eybf&7#z7WE7>Vpr|&tqeCF2kjC1|10N*W9y{g0$ks5`;Lt+=0Fng2=vDR!p{| z-cmw@tr?Qhx}1V2*D>is(1z4ZdoGcYTLlgs$V$~+Pnup4!P$)_^sr;9#Pt-a`Z7uF z4&*1aqAEKKKq*wUJqhJ>KTL@G>=dH#tK{U!j`r=0I1FqIchx&Z{?1cSp$>tiFM&4P zwxJ>%Ev#8%%P}?=<;}Htbt;HhNw3iBXaE}cUSUX!Ir&Ve9Cl!8Hmp`+z* zqUE<+!|OVaIoWe$$~&AmW->6(`EH28urDXXApUE?eKRa`JTY7eo@-Z}r~PCGBaKPw zyMw!W_9#{i1RdlNRmWT!s?Fq;B9l`*GhDI0TMi*rZ0 zIlMmD7$lC~776v~W`!Pbfg4#*g+&n9f8-jb!>)V8cPm^44@*$zedpj95?M)z=gZhWbw?RO>?`5;hk9 zRyMYS-8x|ahE+*j_8O+!E>k5UkC?RE8LVFm?5W;Ff+R-YV&p7rsQscBltsXw2=N2H zux2xvn-O2x#qOF&6Q$Tm?OWj>C+LT9l2UXr6*bWn91}nW1j1dx9;ylc3+GLExDoT* zV158Fd*T3sJ!SwGu(9a>O)Grqo>OnV@FD+v)B|4<7W>H!WHFYcYpsqX`C+v6c67pc zLk$GRaX0In$T*4(jvyEZ*;Ua*M$T4SB2?ANZk$L}{=lGEt(?pvi1}PwxC;ge6lM~L zJ%t5yB2!MDTO-`NyyzrIE<)IS^|5;j?iHC=_D2G8?~<8(eRU3dQp+Z%pQ?3iHc_Y>vEilpmj~*DB&W0%+*WBCC}HM^@EO}b48$qx`a*kPrWNRr?ua(%mv4zL zSAnx^qzfDh6c$?P4lP4iG`TyVsr78k$Ka_pAy7iL%7z1-z(fRo*m;qlPeC;eris=- zCw8mV?=atj(=ib6hYCR(u(lKQFcGY^hXY%KyLLzXA==l_lu`>D+KH{i1g1bZ?=CiL z3VZ?Fh&-zc91nt=*+YV6Cm=S3#q}l`2RsjyRTE%@qBxmj(1Zb1&ak;g%X#QTX%Y9o zDNe#k02X=P!cV~!8^4I{ovvAas46OIB*WyL!+%raa%yFJ| z&X3m88BQC#1%jQ1R~5OB=m!eM#V2C5@j0rv70qNsqcB|2?#6X(_5G0$85fC>q9va0 zC7Zw!ufu5!-KT8q2$I8y0sI+^*KLLKKOOu=MBVR`h{_8e!u1}uyYO+8NH9ycg^wo5 z!MM?H0*a+Dd4II^b_XMoK%X>c&C?rzzmtvK$!;sWJMib6NCkZuaGV$1&mt430)Bey z`y-K1kG`m>+qLWsi|y~O35BFQ`8yEX1mxhbw<$QN50-LOAuVWU;R+1J+347|9`W}x zQsdyVHOg*V?ZCwaGY}@1)2#R{$abRq(<}-?h8tj^!m%uE&J3SAhxueUWf;1VmQa5< zAcsfX9#?^iKgIrF0u>AK(D3rgwU8u&&9#J|K#YXxG+uXn=oU#PAO#GkwqV3Anvnb- z_6lW&No;{rnC1CKaz57C!&BIDKA6XZk_QNw8)FrwgXSu=@+6Oc$7x&s~h?l|^c%!VC> z>ou`>3TqqM4e3ZGOgZpI;@ZDJ4HM8OIMqN4J@`q^#j^QOn1BEIc{HrKa7Bk{(aNk? zL0X&8$xi{+1kQ8>EP3aed3O~X1(=*$VDt45$_^4Ic`$IFBG_}OQw49)MMIi`ec_0@ zpFmbdurPK56WWjoShQiZ1JyG@eus{UW@Xv~-aU7M{0=}iLy4Gy0UBC6ar_XBoIa5% zu6{y$bAcaRDlrg*e2~?qI-AR3Wd;N&vDr1CUpgG8c5NtJXCT^@1xJ_aVxIs#PGC!J zh6Euo&}Am>)(H+?b67YRXCbPFjonfB5pvi&QN_*A(cgS_0W0ZrJr+Z*2{ogYtW=~f zmu8ld)$OHnS}W?X=Hzb%1^B|v2`;(F9O5ZgSgVk$7YdVy*@J}(Mb<7&Bz%g)VnIDG z!PWD(Fn{6gXq$kB2{P9NP8l)vLXnPcGYQ|7Pgb(%>gjHHTxl*%p%ia^D8Grq%H_*g z5yXVM*meg5*wsNcUJ!6YVgabjzG~iJB3XuDV#l1%uw~_PgyO;$A%7mwAQ*D~JfJ`b znECU7{;+6~!IG_fW*_n^=kja4t4`s*zEHF(2R_VtDFz~n(l>C`%(dYnO%7WD<>a_D z;rd%sx!l9zocfI1gSqFg*_rib`g;2Juv@ZwBJZf)*4NQ1{fJFG#^LPDp7lT9gAo|_7V_g~r8e6w)9kQ(SUNu=GWtnwt1V~Nddapu9axWKABAO$ zMbo0^(>5y|V^eQwl;|!ywvzT$ z6d0KtnZ?X>Ko|RjE2vy@vTy81LjXHmU_)|A;rG%Ui^yX`?>Ms~S=7Y+3KLn`A#bte z6g(GtA6->T1Lrhu7SrDx2p|C2vP{sJ0jtqcP9b?r&=LrTJ9dd9JeYc_h>$7~Ql|Q5 zwb#u71~I9lyR*L=8btX{E^rq0cTO>r753GZqe+}EhW4nVN~?E-4IJaN+O5h$^^=y9 zvz#ikRu-wSEpTap9B469$^qW2f|BGT3X4^bBMe;-pxa>c!Lt~-<-nVWQo@yKO<@1a z4*A(q7k(i`T+<26fHlT$PHzl}@taa&EG@QJw&#>EnOM=-)4{hPTfx-IO_swPZGk=| z!$AhDR+RZ_%r1)R5DC(3T9dDAHseBB%RbDe?0|}oNAyzop*BtpZKI}}q-O9@sUeZAjw`rqlUT{p}pqr;7)8f z!2)FwH#Dm?#c&{_QiFvpD{jOF(Aj+sGTVKW-x7M-m=!O|Sy54J*%Dd{ShqnIrj(n# zs|$uz0p(spJx1K+EK1fD$D84_5weh5t7ieU;uhtd zO-*F-BJ_t1f$nZyozR!le80#;&VAS-tzVA7RB$+g})&lyo#?U@J9JU8MGur)Qs9*G=Nk#D&5R8gcyLJbO5tl^aFu*eU7bOeyO5MPVKq=p zod{8oWAi$b+fq7}^#}GqgVniPF}@bJGGedS9QN($jfg0&`Fa&}A1!H}zSX~J%g&u) zTreOSZtPK- z=oSfp9y<02NwW`AIopuWl)^ysbys3RvX6oa_^_!)J%47Z1Ah#!yj-V+OE! z!jHq&r*FCCc7}~X;Wyo9r>C`gQrUDxDaQ~PvMbQjpvI@EyF+-EH>e=MqDv;!WjbJdJ1K@|L5R>V{SNMm8qG}WLl)@bh=5&(p zhzaR7U;0+)O8V?2e))=QUd8=X0crCVSe>Svu`W*D4F@`EibeOV4h9S#I9ImI4%HM} z!bdn)FRd#KnNZLS7P-&6p%nKI$=e~M&`KvBVgN0BQl%#F>ai;6gqK8>!IgUWYaxm+ zU!8?X^N$@nmS<$}l@P%#iqhP!N&PViO+qq~qq~$Cm>%*3_Gse#Bi{rJfH299 zqAPC=;_zre@z-1jQYqX&Dj`|tGfq-bWn(uHG55} z<3Z?x3K^x_sn}*OLk1z@Hk^pl!-= zw-JHmGkeLEmZ>RQ;58O28}2%**mWO@Pdzq4TmoG1)VyL34TtoCFqw8W#~)zfU7p7 zF$C9;Om>TXjUDKoF5~t;!fLKKX}rEM*wbY_3bTVKh`xX&#C-(Gv0WlW5+0%SvKnQ_d}XM}h#n2~&GHSMzRqEt1Q!1Z3e zvAJQ(<{dk%;d~s*L;0Zl59h^<{pQPZ)@WZxcC?{a@FobPpwK zH59J9lr1sNH?0?uJNA!cKNQ>2FHWsU$7v@47BKNmtx(G=Y1P&gf2HvD3x%-*jTdeN zf-cyoX+t6BTfcR06A^g#i3zk<+>Yq$Mwss&o%*tQlasc)@55`mKT}})gfl$_wh$ts ze$|_?%$j`dKbimyF)1>UCbb|K1~us;*ZQf6)Y>lgA9+I#eB=hXYos@Z5Py+u9^951 zob$3n*A@p|yV%^**(b=4B5!r{b&4pb`M9ow+le*ol?}h3@BsROrCa+c%wD{J#s|gv z>jo{dtZ$XBXqJXr)^oPZB2BFnd)dqx(JGt~xob*q4D8L^7~0n#{)hz1cDi<->=|hp zqiwDm3LL{`G558oYM@(rp(1KiOs`bc=Ait`yr7N92h==&Kr!_G!r4!Z}$ zA|fRgYMEMqz6*DY<1l&4CZE2*0H0aApg!@l&A*|=*Vx?N60!yhvSIRI9MZlm-?$a9 z_O*fOfAqD_%{I6(+hpKTSE_~CvNYkl`4&Ms_Pe182=6O0Vn|)>f?m6w2`1F?Vv8BM z4d%%nR2o36mr-bNOtjU#LlJlt8tP!`Q|>O-crCQW?n*5oUS;IizQL8omK*r1&1=XMv+hrWLJK=&zIW64aM6-0ThjNNQ?8$7IL?2e~&8Iwq3)9rT9 z2K=~AX^M^!LI}pLjim0BTq;Mv$hZImu|Ib+8Ah`dOkOC=kkuhChbnzNVN6+CgvA+& zJ_45D!8ye+UR_kExhKmGJl}EyGyb6U;uSl%po50p{XO?cO_gLLIT5C1%v}Jq`0wWe zq@WdIGcSca>mjY{@j2AL>qTt3MsS>-FpO-KeeNL_szX6xsvKWv6~$PwMbcebc9teEYUx5@P{%glr3rfY3ZS^47l3+qnsQ-;kho$6i^pcB5$iR zm9w}b+qE(K_$Hq$nL7!M;~OiMxjbqiyVqvOXoId^M(DHvR>)yho_0WF*y2uHV*2fY zIY%M2x$*%t9hXpufYUhXfJiEb%yM~x9qKoCMCiJckD%nYxGmdNqMQLuMB%oDLWd}H z97*dL0|n|-!OJ7&RAsSS+cv@sg>Jw^We8b8@WGVayqshqTcu!sn7JKE$Di$|l-PjQ>j2T;{R@0HI z>#-Lq`RIF5Hx=7Nl)SF$FaoCU!t*WQm=BwZ-2~BQ<@bjWquEyqII2c*Tx!L4Mo;Xj z@Yd@Sf_Oo_uy{o0a2o?6d8}ngHv_H~9Y)zuPY(h@71Ex_vR><6&*E&GyDSmh`bY$x zbmA<`5|+ZTN4R~Yv+#z6;HGW4a>^0%htZ5&;ldM0@2-oZZl|jmCuWnZ=kC8r*LXJc zLn!LqODI($k=v1gInpsPe9*5luUAoeC~Wec-cS#Y8*^V7+jvZ;^NQEHAYn^i5C)S7 z)7P@OA!Fvga${oyjAtx&f#!^kQDA`Wa;ZmTwTi0_>c*Vunh}#iu8Wtb$l${H1hrhU z7sU)|EW{owq*FAoGFZ7BE89-xV?hoMMewCtVI6kk#=D_HT1MgtZRDgu1a7y>wtVNN z-QnPe@MLonBg9A;{EZx?apWoQesVb6cfdrX&Q)A(?GTRZQnGLD3w6?1*sbw;92s`# z`eL_(TEvJY+iEg?li>0oFv#3QkgW(xFZP|1Oq_O{c_}5x+@w;fC9^7gm`YqtVzvfg zhlbJ2M6NU3{vF2ERh#i)*n7Ln6p;Eh@}eYs=)oQ&@~s0o0eN8J+H^OauF$1#C3ArK zo=!w^q$Uoy$|Wd87Yt2c5ZnxH5+o_s)IE8u2n z4|qz8M9qYq)>2G!klpzWSuuj{mNwF8E7w=c47cOU=UNfZZF5eUt6i|s2>u*V zJU<}BV723RbNVHeWG08RbQpB?;<8-t<8~sEIUSQLm9CFqhrUC&wP?_3K8- zu3kq3EaUT-k2GA@NjjMcM*NTrXe(VW4a$QdrwBT(eyQ^0KuUUs;{uK_4!ckTDd-{n zQzP8a6U1d;tFr@vkYH`=?$hVs6XPacHzTlmcd!d*`h`%MD+ke|U^WHimfFdn*Ud^w zha(R>C88X*jjqR2CJR$(XGq|%+D}*|!oKe^wnDc+`VHA&+Y0QZ2nT_3mELD*SZrkh zeqHe@Dz-*|L;y%ASF+;BwBq`fZt0H#LGUNm%M)^zq*mPfBhHd6Y|>3A-5P*lL&5gg zAy+FF39k?e4KX*aDmJ!-fO6HHv{`N``EW9lo5G6Xle%(>g5qMjn`BX4I{gLgX0a1?o_ty92|nOe~urYwX$_qSd2x#-5c?I?G)5JDD&jI$gLMh>L3n zy2OMM?Fzcah_oWY=ocX)#wMexLFC^hF8sYYECxXt(+#kCFR|C=J-fPt z;RudDL{M(Aq1;3XWVuA`cUWp*7(_;`N}G;He#n%+B@QD*QCTz1=u zreBl`2kQ{_xSNAQO^i(gI}~G++PVXQUPQaK)=Eb=&?>h`jQ0*uXpT90u9l9-ZZGU4 z$|bDEnxEt&%8qc3 z!u#1M@&j4qA~w%jq@g<$4&>JHLs{Tbc8RqB4UEX2u{1ZTIVnqB$*vGLt5}$zGRUBi z%_N_VgLE3)im4xxxGrcp+*zm>%nvIVlr zrP;?Rree~_Q{74vdsUXZoLwr*S^PK5O_f$adchy(RH%y>M896YWNSuA_G#bWLR}OOpr($_)agd*a?Bk*`G1!RLFK4LG2k19{i&?6)NMf?eQ%2a! zET<~Ji(LGsM2GuP~139_mJIW=ERtQxC>>PVoMx{TOl^*ug z*k?3!ECZ~;J_k6pNob{7^U1W>^Xp_neFJk3_4z|vH>?*0buT@`;#It-Ak^kU{`K&$ z+fA3*i`V%|VjrDVQXxsKE1CH|4fF{7Q*$L^t&k~4AuYOCl3Cu{BJ_!z zl7rJq#$M=#x=g~DCF-|V>@B3ql-i~)2&4UWRw3ljOVo$=_H?#&!mhbr?E-u2ble-Z z0%YwWGD05j?~bVP&om+n9x0;b9U#5Sb#s0LQfr_ujO7+)2YG>XQGjynV~|hRG~4c^ z>DpY!o^_)kdK(2cVEUDnS-rG}43Chk%%(nqbo6Tz2re$5UB(vW%?`Y&n}|hbXrUZo z3q>GenVPRJrb6^z-daQ;Ec1lVGwL#GmL!x+5!m463&sqCPsdgZX{w$u-i$MjzK9HG zl@IIaO{^e*`r?}p7~KI(|Je>Dx>c9ma@EkCbOgDmc(HRcaI6yP3+>IINLUp+VL|1# zqIG~Yp>Ab#Yu1PIWDRxRvo+A$-O(9n#8){z@?oVHQG^}pjVxQXCi|Vh2?hmnwL=-f z_Qaf&CO=g50Lz0tTBfq*u-K{PgH9T=5VVNIAe7=Y&s+igM%?Aw2}fR-^bHCo-q*Wo zFg?OuMNURw;!}2kS6f}ZM0K$V*9CcB5WBN zdxKO)+@0fC25Tu~e6=z;4`o>w%fo@qQ5uqQe^*XX~+jt{+=vh|gZ*C=8=vVXx zn?I+ex4B7a^f8aLy=^CbKQHWK)Arj6xnrD@q?=IEBip*_2a`E<$e+wHpPjsMQO| zmCGOMhheEXQY18qwiQqusW7?xQH!xr5PK5^4C0e|Da?;Z&5y}3mIqQqF1TQv^YY@SK6vL1`JHghGOf8>G;A>WP(xT zl9ef&ucreOUO81*DaDkpsXGLB3K=0&s0D5Dj(YWsv{GAZ!}axsBO~^%yT}-sM$IscH1-$z+nkwaf?z^Xh(VWX+jG>kxnqy0 z#py)FDqEVWkh4Lz(xj&5?UOm9(I;aa)8FO@AbN!~QGpeq26NzldR8*Q58~*_OikAL zZ(0ig*(^0#r?V-i%!Z34#HBon{;XVBZw0_gk-|8}8z3VK^FT=RYQ5`Xg{G)VVy@=e zIvejXi|%)%9$6U#Cif@yN=iehN?kn(>%FBK8WK!JF&uwf5 z;ZSm0|88s=x|+M#RHR9<6-}v@Jhm-_vqrJMW`IT4T*;8WtQ-f1;?gKqA&<-Tbfryd zV3W5ZKlY3ORtlBHdTbfXsR6#OT>&-~e3mX(6b&w=a~Whq31N(guTz`D^h(^Q!!Fj< zfwFaB@2!Ep5EP%GK6%^D08IWWW$Q3Ty<2znK)XunJY(x=OmIQuS?aN&wG!F$*<4vh z-}oA3B=XcId}y7_K9`**vM;j<4rYwwwKDI8?0lJ5UnJCh4`Mux%-7`yOp=e0_j;Lk z2O6RD!V;u_NJNfx{`~A$giiSe?{w9pDs1oybX8qtMyh$_`H#Y~}X4EftI1K&h!l6%#A8R-D0tS6U1e zqME760h1X3(Om;Wn9766fAH9qkg5G119t0&MWwhl0Do(ZOYds8ZnHV zj(>M(`9{K+#da`qX6hclya}BU*&~z@5_^qEiN=J7HW@cribiFQRE@V8;uf67ICZd5 zCTFrj!<~ym^Lj=cHH-tAYcTx*oI$!|H<_2(&4{e#ntTL9klvjk z%d-gF|0frKOLMYHU6z-;SU~jsVqY=`-%lyqPR|=Sc}m zzZKapTDLLKjTH~`KO@fe%5|%nRf+;v6R*SdABKQ&uYCVg&r*FLq#+%#!!F5@ybc!B zc}h5NM+5JO@~5=iQi@R zDi%7(2r&CBgs>iw*aICs1|d8s-_KxGf>74Tm>wV(BgaEBM-{6OIaalSi6MBqF8oL3 z>*;Koc)hv-;l9al*~k!+8O~+1M22NL?S{oT7}4dSXwwiGy#n2GKQB4#$2wsMphn!=4ianV*0s~de6gQUFmSeZb-4k_ zwyC)#P`kn4W!u!588t39yD2j@b(2)RI7nj%6Kn35Kh(MntElLlQ;Bo4N{9(8*`*=Y z2^7_b<#j;zCSFd!FED^h4Hx23LAVk^>j3V&*V|ej7Gq?OfL^<$D>J%;6mccEF^EAE z&<-~1CS3z|7v0@-PdTv#iAICc^ibWsg9E)-x7nfBGU^M7 z;;yc(ft^$e@};p@iC3FG9Qrd@LYoAVHU@Xepw@!}&}QiP*WYIKb^wNJ)(jb3pcmi~ z>OIAEPNP8B8$zQ7-oAqA}c;aEc5EkBF`7Y3Rjr=1qnqn4Y*sk zv~O;d=w>68EP^If zwKGIY;azkbFu4dug^o9+sdhOtsl0Mt+)3JD_z$~&zyB<~y#^-}l>z)-hH zQ#Ew^W-rN?Wu2iWn7DTVPTL+B5K0>fy(Pj)_gu6j7$%VQ^3rt>2^mtL$hN)?+|w<# zDYH962mq>n7|Nw7XgRF@s4!qkw=!zzEM;>?rqkgi5+h+C>Bv;3F@K^+n}-iJaxqI3 zOU#v4TQC=%L@3J?Db~&vMQ&jUU|JyiT)(LnS%+wWK)Uz&IFto>|8I!bHKN>K?S)x=mW164L(G5ewtbR@nch!eG5w8n_UJ>pB z$5%gGswvej^rM22qbyOz*v+ap`h$8;|Mc{o_&!IZO!ULU?njaJ?45DOW@Yz73akqR z#7TaHSc^!cmNSnU#S$o=cNPh12nYfy)2jW6a&3-3QKr?<$3i}|Pg~4~`n9EeXh2)R zhwjr>^Pxd)9Utn`ynHCEHS(do+D1O~F>MncifFBT=zh)5haS*wCjk5Wi3_!1K6J14 z8G(-`mME8KFSBi~juG_6M1=;eQ)fWDp!jOh;BxJJs%0J@>SZI_SYon-Ef|I#cA4h8 z=&&e=vSnR+_H0J@7KE!8Hg}0U~&G}7tFfvCEO5qel9F(-N{Y=0?5t}={G=`|+3k-LI&>rIiF#RR!c(A!k)5+3`R zw$`7h)LQt^wc0NFi1MKawa4h=tMqY#K7K(TXYrAo38G0}#)qy;uBDGQK6HKZUVq{b zl5xz4+EK2K#sw$Dlv8+k1I4X%quB=Jk5~q?@AgR2mt+%5S{9mOJ z6_z(CD+-mi4rvulCQE&l?nQc)NEi3ni&aj&YQ(FOCVz(xb|ZdIX);6rm8%)4)0?dF zfqJ<~6xjsdIWt~bky_d$1RIslkCdrR>qD>x7dFl+`DT$U1sXcSRLL~96?&DX5xo=X zlbb{ktM#ElmAM0nQ)D6?<*C%WkUCYS3LUaa?h={B_z+W4sn`SqE;I__g-Yy4;_N2f zq{6&B7YPxO4I;y&CSu=IqJsSeNOcn{W2fMQUc|xWHbDx6-hGTYfv?iE%&H$u9Sn5iBzUmSMjTeO+g8D zMoGNpGLffD?I87{M?T{rIj%yhpbxCYmD()&h6TBT7UT+AkT+?I{RyAVf_#7$EjAWdr621 zYl-7EBvH|% zn7W4#MN>nlDD}tm^Q%1ex2f;)*sG~Gzwgv zNTa_M>2CT!FDugMU_}~zt4JTBk4Na^>*#*^C7?b18Xxkd{~1_gBfcW=<`U+~9@ZkZ zN(^DCR6yz@XgByCR@xTo@9Pwc(siy0#~N@^6#h*IUSud9ikM6FI2Is=4YpzS ztkv^chR})>0|(f*2}#BuA}&Oy0azn+AUQZ-M;>Qdv6|71+U4}^YChDY-9VFh8)#VD zL1}yV(0*gKU8_AoX-|=m@z?&uM(ro`Gs%ZGXuqRrGufYLPF{($KjrbFblgBZOk_r0xBY$F3@@@L|Zvc?0^e0+Um(zS&hBP)(Qi4w*a+KL{I$A5} z-yD0ne2#dz5nFfqqP7=Nk;o<+Ya0E=Vl48=cPxr$)Wgd4)mbH*ACv`@S8ZJc&3Q~U zMvGa6$|_bx%7#@ct6@aiC2X=-%V^o6Ja<|0P}(H0ZNmeW67ICgRLaWrMUk>#_T_Z2 zj7<{cNbj+-jnkj0$&gcL*)4UHy&75R&zyZSDG$TF*6NQ%yY$yq22 zd%#c+Wr2j3m4znoOjf2uKFU#R$w6te;NU85SH<^6yr)0CHS~TqD~4o8-!MQQfaX6i zzdeVQkX%KtXIX#~t2~!Y5?GzjitfQkfbD)#KE5U&Kar3BkdL_#RFH+_m?BoZ2IcYC zE+0|(_;dMqMLyn@j|{Z95inE#OaF zt_A&x1+?vHBH>2iS`OsY3*nJ#8uk6SgN%T{D~#W3&3uY zm-rJ`Ca)v5)Z|YrN`n7gmb`Dhbu`G!d{@&!j`4h`i zGx05Tfj?1~T1DT$^;V>Afhdvc@F!NL?uR^)`Xasf9%PZ!EB?f))IZbD-}w_)r>p#l zk~FkktJ7CO+DK#jHk5|sb1#Vl*QAMKrawm?N9bdeKK`6OzD6J4pbz4r=@;qa2lVk{ z`gnsr{*gZZi9UW!A7}BwMs|4;mE*;SC0?JBj~~g$Z{%Yp*bqiwm3-VHANR}07vUSN2XLTDN( z20`GqQCbqi7y= z;2HeYqMI|&;`8wbiu}L&PwGSzCDO{F{$U{LA66Ua52Qv+%||SXtWqUO|7Z201}~;I zS#@x<)?hnrQNhs~g3RIu1e0ctL@C;3)3Jv+8H+jX-$`!4J{_^BRF%};YD3*Z3So?( zdbb+yD)sHQS($*v82X3JfdC?9$VD@ZgAY~eTD*itfC9N^)FWObR+IXne@~C$d9F6? zjd+Ldk=4GeT;LL7BlvVenMu1LljQ*|ReB*?0y@|P;iL;s+ULjdyaBSvSMa2L z`Um275nBNQx&Z>nE#g_lo)ph&_G|I{-S|?qzdxj&f2W>Ts{Q_L>iL*@o>k8^Q0G%U zN7VEC>RDDU)7Pr!pnATD=M8K+J0Gg)8`umsj3<@6_JaCK#q< z05fy-4q>(TEJX28S}%AhZQd>e20@}0rGp18q<_X~z$M~mG3;-EOZ>xufi+93crh*e zWJ}L&*;R_H*i}lBP^1IhEKdFvrHQ@tk-ry`#|+Ya%pl#zOwxVKAVK8+XYc#to2u^q zPucideNMTEIe!R*EywCKRO*XnFw!q0j=s($+S?GHA_h=_I=ix(zqB$<}Q* z#qI01&M7kY!%Prxx`~rbHgyksmZZ3{=V8yopqtEo-=A~tO_Mb3jk4$a$M^NPaeMFi z`8nr&&gb_z8Ar5y92r2ykpW}{89*NS*anbgngK*=SxyF!ZNW;Zr8KxjYFQerb_A-( z0J4k>AWkxX*vSC0g$y7Ml8CdOM4Y80;yfID(h;Z$?sEicgZmwUy5Q4}fE?^_1loe% za|EsqzDznDWS@7*9`c(|3O(Qg_)X{zde}$+g0ORS=+nefzCfQ{^nf|zccDL0kxO`l zuR>aQI>sB$r3WbcxUhvDCG@b2Zc0XX$aC9=&*UGkT=c;~Ga`U3V@fqT7n)>4sg$*1gFQ zSVp##@!dX5PWP9D&WOUY>eKkM&wf1 zy*y`lGDaVBl+mXF!AJxL1TFk$hIK=XOq`4qV}fZac60KwQQM7^*c8SEE{(FLvtitF zL2WPR<>|nV%~{{z(wyC*AAQym4-@l(-|NCzWA1(DK5uGJkcLb&NM@TWZ*LX{@G2d^ zSjYxo0I?*nGL-Ju1cx3*gf6A9^}I5H&Ld8p4Nf@VV>^sPAYLJAviWxk8)Rx0@NBYD z;cory&RTavY~zXNISRl^-oT`&1`^Ki6#>~l-{MFVr_uTp4w!F}4}{?@29gH7Lfnun z?7+>O%x@zZeLTErpq6(ML4_JPo}}>FpbO-JsPFjTXW=gkJ_S-3{0>ptBb4zvW&956 zUGTrb1jydp90G9~AG!vHv(Owzpgv@$&xcSrR6|5|7d`em0`AavC=L4D_)vhV{hK4O z9kwu#TZs1K!()l&-r)#58ZM@^C6u<-5oioQLZ3V6^Kp9niX*Ti`~toGl+s?Ow|A*7 zG}KRbkHcs8wa9=q8gK9v?(BYm-ZoHL9lb$sZYCPp(EU6>>;4I4fKcx04mkp5GCGfE z9S2eZ<35CF!O0UhS()RaRWax@G+515YvB|<+zLn!$xyowUCho zjtoY?Qi_XU^rJeUS}QaHoz6y-h{J` z&BTU9Sdo>djL=RH>9rD5480bgz7#f$$Kcb%vja!D&TS@jyD11O@*|{Qj}I<%1e${q z@H+@0XFPOi?AXT~fyYQn8Xx=)8LnTV!oMP+33A4G7>8PJhLqHDPiPG7Q>fN$A#9rQ zp~Y~tht^TXR&twjdFe?<;4>juWyXhogr-7oKtu}#=@SN?$HT+XKo|gx58vPjEDz6w zC=#AapG5#8T`A6@p5az!HR0ubV1x4H8E!DprhQ^e?t8I&v#?G%B+M`SdTg%qY^> zgla~GN>G6QB`73z6&0F<0`xbw&~QNyC}TPb(7)IMqxyAUDy07xTPU5P;v{F)qq;?C zd~hsgPZA5Lk~AS+6hI0Yj${EfWU9j=kFYW zGQ@L$fxsx*99lqco4~z7#1mk(l*1uNye@<-vOuOlsqvE>QCK6WuLx_U0njcIVSAYr zR|J}GN()SgHH1*PGAJ>rS zV**({Ccx?;|0F>i=7WgCl)pw{9}h&rK88^orXKbo$xp-TA%75DBYEBru9xJm!|EZQ zh1ElTgt)T{RuB0@h}@ zBL5@!hSYL(@TAl-E_h0684nA)d^z}@)RGxIBehHj2Bns3f@h_ciNSM{{88|{)G|4E zL29{{i0_wzmnHcNB=lbwN|RdBLg`Y=lu(A$GCVX^Y8esAlv>hZZD|<=A}#+plqJbM zp=?S1&(L(q^KNK{B%crEN}l&Z`I7wCP=O@>7Xnn|{|;Fs`9i2zlK&Pek>rb^C6fI2 zkX@1?k4y4DLzut5&>E?Qh1N?grVy+~DIr*mQbW~J%g7MsZ+plkc}|2HCC_V6Hax!w zc_h#4q1}?_jnE#+^UF}XY}d43)0kUVdPRLOHH^o-;o zCgS-mtU;c4LeEQ{)9~DTei!_-4L22^zg)x6DjQKN#F@GtD-jP$on7_2}8c7}& z2L25XS4#4T@D@o<4_8a_$S~$_RM;iSqr;7ooDtq7$z#GENxmw)TawM;J(4^&+%Cyi zho6+>ap8TEJU+Z%k~70kOY($pha_JURwa32_!&u_6n<8cCx^Qv`P%UFl6+nGuq00j z|3s3n4+kW9YWS!m-w-|_$ywnyB>BehNlCsbd`gmU4xg6fY(x^t)52#Y`Ic}{l5Y*4 zmE`H+bCP^p_`D?F9=;&SIpJPOo)Nw*$uql|B+u$jljJ+P(lKcSlQu)E|!;-wF`zMmTwmTrn4|N}vZ5y={_gP&hGP)ytVs+BtP8U zE6LTYqtFzXa9OAw{~yuqf8|i_FiniVvAl?W{JN^19asnBi6n0erb%*5FkO;sgBg-s z7aS|e^}$R@ZlGv$cW{a%KN`%ER@o6v*c_&Qf@-77N%T2)oNp2?Q@fetg z>;dzT@>*Fc105U>@>Uh?h{YNzCIQF^^}!JmhbKdC1=Z^N{}!F^^}#Jml|!dC1=b^N_y} z<{@_x^Y{Unhx{Cvhx|ig9?ui=cmd2qJ_P0=zX;|b9|rS~e+1?szXawX|Cn_wW`S!o zjc~AK(6vG{8$>JQ!hOU$dkuMfCj@VlJjYqb1s0e%xQu^V!=pdoujgNhQ~wX!%3^fp zNN98sy!|UT7fd9vW)g`t6G^O@2+Z{S7?_#xc*#)Ftdi%4z)Xr}<<1<*^E@!q^E5Ei za{!p>`5G|O^L1dRrvsSj`35l4^G#r;=Uc!`j|$B6_<@<8PGF|zATZPO3^3F4ZD6M7 zJHSlO{{d!to&{!lz6;Fsd=HrE`93hy(*?}*`~aBgc@CKAc>$Q|IRwn~ya>$n90q23 zegw?)yu>;dvH;XwQr|`lm=PU?#wCYBlY(%)OblKtdA`az{>TE82S*{jVIsuKS-|zdhQzeJ`?plLxL}gIK`iR^($Yv%Y}W(E?7P1-yI6m0r2<#0WqxFPY5q6aOSzwB`fP;_wW3J#73bDD4 z{1rH8HkJjhhd=HRQ_|W9p^vZw!&z{+BVb}3Ar`=qm@;YQ?q9poTaTVUO9hm_D!*5H zk1v`+L6OH7Wl{L#@kP@q#OL^;Tp|(27v(xjx8!-#}>^%wjE}l zzGI7K;?seDi}9}_<=CPP_`4qeT=?fkF$=Y+{3`F)%5l-N_|q0}-L9ut3cQOlqd%5t zAG|_|chMx}`0jUIX8u|E1o~yH^=O82eBDD29>F_BZL$M-OdTz?Jg&}{TJ}(M;8e9u zYFVqcI|3FJOJh-0di;PMhhRxlUzJ+Ctm6h2xB;`HoK}tq@M8X;c|s+uf|gN!K{>`>Bi;9E35C7xJ6nIzr)O)E)Vr*$Z`agC zdGG4w3s1X7D{pJ1deVxPxkHMr#A`i|P6XnuR?5$7`Q}6AAA0^hGE`5~hpahWZwX7Q zglNWGOdTb)d{)JlwWvARSZb~$|5PoYSuA!0HmaqLK$g0a9u@R}K|M=tzzO+(FVK_HW!h7meQ#9ZlD_6UBA<$-#EuWq$pgIg!t-u(Bp+R+M73iv9~$-s zjKX`;va$QFQ~uyx15mZ4f5YbhkdykpunQ#=n;;Tx0rS_7?0(lh!Luv-Ugo|Ee=N*B zA}{bkJ9el(%3hx9TIgv8*prmn`E7r3E%YtP&QQwjiq*m4>X{uWt$aTV^ay39#+MmG z33;G%=Ugn-57dJ%3JV7_CV8sgCT7)K_{& zD)`H*@|kgEh%86zD=De3v>vTqt5HuHbP2Pv1@OFB*$VmuH9gAI42U5r92V^=!d2T< z_`up#xG5|uyvOgU@K7k~c6xk^9&lnS>dTZ1!DYGnYkUT=v2IrXiE4gCNw@n8kmg6k zw#APSop!%WZ%@+WoAeowS{8&*_h$ba^a&To^`Unp`ImkKYrh}*rzHO>JVKJ+>;wgF z@0=>hKkK}W-ty=T{=N22c;$8{X9vgOp+Z|~XB}o1m_`(#Al>{_Am0R9fcqgJ ztPqVr3cgVSY!oHN>LqSxf$5A%%K3RxiR-#OQVH0gMq#x-WBWe;JUVF zwpC{)d9NvNa~vT~H=5B<74;U$6W~*aQ`G>VP_nyj@k(|8@SpOgg1)|w`V8PeRDFed z6Nh6F3t+A}90j0xU8bH%7mMbZTw}GFHy`o@(i!VX$qVcbxH5I};;~>oG{BT1yT*s1 zbF`~7=y8|i`5fygX91if!Rr1VGyKqcSK*$A)?-KIXmiZTtWJa#%Y@Ox;4@@)Emsi0 z$Vh}J!To>-ZeZ#J>IFkkg5Wtb)jXP)yCu)hRU87W;OqfmDf8?bT%-2bH{|`AgNi8} z&V>+B03zx-8AeaOct8T zrKWOYN)kq}j%!&U59nPorSPq$6y+_iJxj4?Mu7~OcpmG>VgZD7puH4KN=a7XTg|-e zl-ROZ1C*U(3e2UMUkNtY>&?stCrryOv=x|lUqzIDM)sxO++0(jG#6ZY=jQp@$n)9? zlvhU|f5wBY0Unih={S)}^HWDI9mn!B*9dJ@uzXj^OAE*$tEF5zPK)9i2AXGEb?H?P zmCn%e_!v+Cd)A$P=~epc8h6oF;E62}S!Asm5X0o&GHOkBnrnlW)Y_QA+~v{D0v)Z| zx1#M*5YjXj;qy8oT{=!MnV2{8pR>Z#^`rH7<@Ei94^1-f{ypVuWT{sd$Az@c z>nN`)ztxiCCsaAz`WWVKMzq|H`J1wpfx!G$-_)7{piSmQ+ubRyYg?NqvF(|9x6-8h zl=Qyk7Ugu$xc;<-T3YLeeJ-8TYGezO>Hix8F$e(&8KaeAG1xNxCoj7#Ghx+rX8Q1 zpa8fTj#Gi&{TKLIQHJtczAmooe35rw(HfqH1rmZ9k+lO8z|5tVy(%10PpDbYk5mN6 zpu-=eU@vRC_^k5hCD>69OnUCf5Xg zE1uR~3Rh*huJ+dG+g8KWNE;xoi?t6BT4jY^gs;!4eIYD`3?22 zc=N-={<hB zCz?_^Nu}?cMQ`_j4s;ef0$V%p2bt+yMW0oaQBOraO`k1}fYj-w&(EOA<5mj0DRU-|l1K7D11FXg1R?LqCz_m<;@I6hvksdf^ zI8DVdv1zIe^TQ81A%u|IANGNTd*6DZLPx5Xt9OeLE-l@!vxq>_+2~uOZ#n z^gK^!ZC=CNGcQ^{N`d%>^|*}c9eTUI(&zR5mGTZiBi0v%w2pZJEtTT{tWHW(AgAfD zx2bpKP=F}5e6RInj~U~(o}|YGdi2ubx$fmScsIwvJf-|>PFe9=|G<3xnkm*`PLV~M zJYVU44W9kpq5{SGG-mUIsW5Gt57}laC!sXg59e*nRGQ8z$8)S_BD0>aypvPjt2KJ~ zL)i7i@>tLJ*yg8zV_DBCOMNBPdJ;yjo-A5M>lvlImkLEnSkH?R zMfv7K)^}%^>c@O^!n~U-nICizb8XJ$1H77VqS4-+1xk@6)s~;yOCYCxG$WCKT!C_(Yw2@~ zp&ZGfq;=#KZdQu0(C4&8#9JzCwMDrYpoWsCdPXbOb68vJ1+Vp#7|{rAMDDBjT$}>_ zfAHVHvYh7poShaLVF+KeTWq;nCD$aH0{1wK<2LIl#Y%Nh8Sjyaa3B>U*YWQjr8KCN zznAlixBiaty~Vuu=PLK-rv4&zX?|hpspip2>8YGwDIXgl0&^KY&~wlI ze30lHD>II_|DG2EF3Qaz0zhzx9!2p;VFHx0VhZzxF!Q}Ll5&eId3{4DFveA!kq=QmkPAjb{3@Ww4^rYLsBzgUWzhIMe_+5 z^X@$~k!86GRx_rlTt1|cV&v#r-<(K>#;WYhP3=W{#PVD>XncTAjxHTNP^#ZY#^dX(~gLzDizzyqsSS9-^8`6+m*&$^F~6KsV4Jwoh^ z^_&%Ef0A4h*d(i%`aWbX6@u66ewC!JpO94ba~SqJUn5!SUHUvjp9t4mcMzJ+EeG$V z#}*KdgAJe~2b)1h4tnWxALvN;H$b%xej6Im!5`2A7f3zHI=n1!kI<<}&(hV`vMmJS zR{SsTwU_L8RgNhIeJj&q((6R}q`tB=QaHQPQgCJ#*Bu~vC3E7YgDRXEufknnJZzpN z$}d5=8ZDp8Z&2NqlOb{;g>BqJcLx*&A+p}XI>xa8Zf`16RyBIBOalO zh3FWRAO@R%THb4i8_^(Nwbc)Bty&+&NTe6x3`{kyl-Y(cWU*;^OL{iKP$sc}g@$q(s-%LT^N972$E2qtY@Qrvu=$N-fdwGX_Dl>_ zW`TuVex+3rYB33|wWzHLtwPA0G2E3lW4QYoU-@NiNDAJLkXd0yPo}TDHZ}p_*A4KjP&!w z{IM05n*zm}hR|Ck<-O)D?Iq=eW_cd)N}uJZ726#!@9qX1WErqt{zeH(f&RijF+(>4 zNz=7Aa#}lR}F*9g>!|fS4Oa60@dAx>*@@O7tc* z^+Q*f6v?jV*oq-!gWb*FQ#4<(fqPgW@DdM!E$Ikfhlq6rP8OX!L>6HIMvO(^1vO+j zuOSPj1)HIn(NV)bKB>6`jkT36m#2W`1_P_7>GExbjthqXFbR)dB;@G7A)=4j)-ZfT#>~zfGk3<*TpI8!TaY3H))R+&wdXOkZ; z+Btq)D!9pS0qOEDr8k(vZu8@y+9to7$P|oVoBU7HqZ5?O55l&|e}stG`}EcgnijeM znEjVQzep*>Uc!~#i}?)3eA`9Wts0^tT;q{>MKfS-6nl-n_Y}>DP;T8W zK87~dwb)p~$&E>YT^u(S-52R#tZR;r{M5lT{ll`Wu*mMZrvKAhWFMk=@>WyS3Yx!( z1@1@lkacu&rO|`?mk3*^=I%t;(K<4ATF6s@NcD$J0XQ(RpO^E!e;IN+UuW+K9W6Ab zrlo5;S5FlCa$w<@{VEFk$}$B}&_Acder82~68Rq3>h$;|6q^~J<}1sMZpl}cAN`3M zGvaHISP?f|Us-Wnt&;d6-~sVT+%KMqE*8h<2%+{sEey>hu0pWe43{|eL>|3?g|@5r z(_0y(t*18x9BfvhS!`B)^uUF0o7Ly(ae^N2&?8Kbf8gPV7`xd&g&x?3oBc)fKvcwL z9#2pT8$=^)5Imlsg9VmyD!#l-iVoh}Q{JbX_b#2JORaFIEdl#bP5}MHf<0&Y21vRP zSe4V>hagBjH-h8Jaa$HCB*$&p;?E56r$qb_0f4rGIj9Uvn+>)}Pg5To7lY zcE8)KJA?Tqg#{aNS)WzSl6*p)NDr`8C;9CVrZt)cpo;M(g!kP9Paf--$O5=VMB|aV zgr>8|(NfD+*5PA;6?`+b9=)Lamv4Ty-;bck(_GJq?CA{hL;b*{oPt;aU2ne9Tlgv0 zqSjCKHD9;Yr09EJmmGChhWArOC8!sZ`4FD1M>7PI&t?>B<5L%qzXGzLPhCzf#*Os( zFnvOOMN|gVAKY8uwHHAB!QdV7gMXL{2<7C*cZ(GV6?jFTAV?C!a3&aQ=Go6W9x(+Z zO-eWis0? z?el?Qg!C;iiSX5pP_lD{3p35TcavmOe$KlvGeVkCme+!p+(N+!)=QqIOU!+>p1$d4 z7load#3Z_fQd4rYY5+s?OhtGu{g^|18-EjRXbQj|BPK54#kzV)L%^WGOQd4&zx=H1WIEJiJn z$-Mi^aYa$YIt?G~H?G!!U zqas1R=MWgP3jUClWKa=v=PSulPH_;aBSCr4&kEL2$pWkSM3*bSBkPN@XcB^A7Z;x1 zK2>=;=cMwB)Yl+POZmRc7NsWNSKKCi&OO)W98pg2JX^lcu|Ew)S6|wG-i02DA?171 zzM_SaASf9G@QdnVde|W+u#PGgcpw2m!%8+!iA=3Re)l!Bb`$7{5V-!h(R2hNrrVWY zk(CGZ4mPamF(it0pazeJz77)|S#9`dG(5OaTDT z-IixG?dFW)hOIJ48kFX})YTW-#i7V9_B5q4cQJR9X+ko2v-mXI%fxIqoy%zo7B*dI zx_&Dp=`(iaO>Qg_ndN76;d-k{l=n1c!e#JF3{K;{=0iN94dky#XVCr8Y%x7qGvG!G z_5%Yv0zs{`!cy8oIt#tHTkT7uB6o%YL<0YMHL?Bb0$wu~+?p?L(B&#D#E^1&S>PdUtI!DpZD)E* zFDR#vThD(4sgHI9j$&vV(ISFo3a!15>C%njq1f0uR_ z7jdLQ019tvc}8JM(`c%n)}){1fT-sH9ZhR^$G^NYjIp^Z%=t-paU_|hKh zsn$MlUWLfXJ6aV?k;%2IusPejFEGlQ;d;c|Jj2^`8qxXZQFV{?y+}#UPq+5DFZa27 zBZ<-6bg8HGy|hRUaa30!mt0_b(0Ys5&N!qfpWZX+xJ{@D*3+aWpyLaoV?GAm4C5<* zgVucu3=kizz#{_vM3d_gPtz&pe)yvGy>xd~Ytw1AYn_&@SV{`5r(HKH<$7=tGmON4OgD?bVa;21$MUk3}^ief#>ih+c!P3PhNYo!Gf9r0jX4u_tA z$r5xk0!lj0(nJ^eQ7_88#IVoM>=zh5^rGeGy$fC1hp+T3uuIe{J%^A7AUuKLpaFmI zMn4D_EfZ7ByMNrDpqz-;2rb?p?nD0Z)+h0#|GYnZ8qedc`}tQt0FtF207QeOx%876 z+T^zd9@Rrl(QO)nF(0CXBk34Rdg~rEhG5J{%B7Q8JS{SyGm!y7*Ca>snaF_jG6JnC zfl)vollwXde(|`fD_)9iMr&_hb5>z5g`O&KIfy0E*G(^nt#+}WQZNgkMDrA8%>SeH zVh0wOR2a*+;NeptI{MVrlILg;q+`AMI0-<0diw#<+GF&1ljO%U^!7J;{1bI?phymc zaME(UAEN$VzZI?cE9tQvMf?<1;RloQ`Qe82`Ja(IulmuR&wrTSeo1e*sq}>Z0_t`S zL$E{VWTbUM2-@3ek>sCuE}}OCS^S)0%zT~D6*qShX$vFLVtv?!M`tS(i_Ryh+*hTR z8#^K2`Z|AswD8-K=gXa^CC^vFe{lpV!T@)}!8BwXyc$Ig0_MF3r%CcF2Xpb+Z9!Ar zEATj2K^brm-FUE#-T?UWgWskaV6lONuhHjQ^!Yn3IbY2J>j|sHfBdE68WyPF^c9le z=RVJ|Sx9=YUXPSyP~clwU;{5fHYDc95}5_r57O15g;mQ`wLNU*El>gnljKWqFK{SnfrUj6rOdDr0>jZZzjR?R_oER zno0nEhxpzvlO*&-bsOI3%F3%%9JBd~dXRKS+z|P?`g3Hkj{m~~m711vR*wwDNk%ce z!qev6Ei^jq8zRZ8+PJNW#;xTRo^HBc`ER|o-eox-?Yegn+VJLgfjM97nS<#U?Fi8Z z=LRq83FY~PY0o`|!+)6St{<9EK7?arztTpAlbs`pSC5xkzDmO1SLqChoxEmOtB}dQ zqTWZjE9n88x2wQRJL`bQwMrW!&|bgr-WHQP&w(AJZX_eG zTau5mjwV{!wq4wnZv(I*05RuCiL#P(!J?(^b3NC^6dOau7Ug@V z>__HBNMfRjkPj&goM3^iz)mMx?5dp$iLfbcCYw?g_8!i|ZUGGaVyGq*;!%`nKpi(y zHzJkVj4rR2ZeX)w0$k|34lOnSG8TaoN77h1Ak9KswLFxkp2O3Clx--z1Ai&Kn!jy8 ze^hb{a%uo^6+&jR&^v|Ch*oH%1Z@R!aOe!83BqY`*;q~x2cw@3PNFN|v85j-yZ8Y* z*|Cjv+>-)_kuZ@Q1`y%z7P>E(cYoJJTtt{xaJU$u0CZA6sxchjffhwSQL7i@YcH%F5U%x9uk~!+ zugkYg1h|!bb1J_43T1SxL8H}iDCE7gUYdXx^L}Q9&R}{c7 zx=^GYPvJ)D!>LES)_~-*9)^)e%Yd1=|3f%LdWM_Uo;IyLp7>V z5!V!{ic%u$1hP)VWu4e+;vFw_2O=hwskFR2_D>8{fTkH$!qEdRzrZUCp%|p=Y3_|^ zCPuqly_X(f70ZP;0z7uPx`$}h0ebsB?QeLyt|#I&gA=FVN%$+tE`5_9?D__OKFay4 z$RP5t)G`w`la^U@^7jt^0ebtE)ROCeU22(4wvgF$tm3sWLj#?NWw^d`oYXR>6N1f? zozu`tCnzWmE-Yn%ZA9I;A5=F?LqO~g90NJOWeYgLV?LaefW!o<-=7&{JK>}nI|^>0 zn&5Z}G(&G+2tw|?T1C*z*HjqPVM+M~3)Ju(wY-e>k@fO1%RMGS%9ntN0x5ND+ge)BTDmM@2l1m;T!$b|yL}m=32~uX`Zb9f^~c;? z6=VSf*%5bv;H}xj_bh>Idm1ut2614B;mq9rT>#KZhbGPK-y~QxE@@c<$u`a0zBek3 zZUulPN;e`Ere!}iO8GuFJ-v?*oxdo@H$~Z+t|HM!H#4Q4)f?tErowpU#o5-4F=i+5 zFbL}icNDfH0Ns-YrgnlJipYD7F$;ORIUt&(%{rAw@Qp#9K3%Y^c5RC%4>Dw#-ZZ0spSLx0B3r2u5eL{>@ zH1nv!Me>FHA~`a&1F77>LY5R|LI<7*-=5fCSt|qN>r2aqfc|sZCC%4(tFzPI#fEq{ zE&C6D7t?gMXN+F$ndga$zp9@iW^@eBgQoe7H`v8KviI`!@sWRIIsKYgLlB}@1JP_B zos3(q?vh%*PF4$ya0&~!IJW9C;ijKy2U|Ff!bLoSs=*5W6zkZ@0`7i9J$pPYn{V!> z4EEScP4(e4=ssPO<2!IZ?LaHH@(PWQ@4%aYo8#k>?@q^sF(?UpZdx|qg2$D#Y#j@s zuuP_K()z4I&t72cHZ08_&bzsN2Sr-qya4TsK9&`H26B5u2luFxp>f*C^3FM_1v; zB~+MxmaDV*$O>5C(?bJmnFJ*c#~bpM;+D(NKw`iIB!_S3zYxApN5j|70=szI<;&>~ z0v)00#BN#w0}be)#(+4M<|gD5*TnD%GS-aqrhUf>=J0m;AnoetdK=e$>?DkvNf`f% zaPV_22;5^i-Q{jpKaC82bw!f^orp|7szLy~gXqfJdJ;KoN%K)A&=d3|Ba#O?0wQus zLr`%%7W&s{+AXw-bUd~x2&&hl&f{w_jRl(Z^(4P=B%tH`E?lnwEbKDrdK3kJ{V4}Z})(pyX z*=H|^0a&|Y3ic($@oox!3cm}Xu1Bz8u#tqnCR)L#kU=*zYz;n3xe#%mRDXjs*6|q@ z@Mv>$7BYHzYvVZteUc*^H-gzO(9?3Bj}6h_5c%;*zFz!+7M7l~@B)PRvpm6$cormU zmVHEVX*U|_`IfVKa0^AcpyXNZ6e-cb2VKg7aab)SBi||5Vt^;^LAJJN}*4kPes6vrgM;%gEMIOcR2^gS4p!`N_$gF z&*i)cT`A|dW?1|s#iUFda+&1s7A9X?DMiX-4rp}kntqcq`f(~U@fMjBcC z-$N8EPBGphwdApmlPu7}7Xb?gzp1@T`}-_k$Y(fM@OVweDbO@mVb4Y+AGfH(I2Plr zV{`%GGv1!o(iBTa>xr~6%tF6+oP|sXOAGh&`{qy)wkJ`%WAt>rBIE^;w%?4jfhEn6 zE@DgaZo48aK1(gqi5HM|1k551;|HcdtA=1fLNG#2F(QN%0WM`lM(V;etSGHSy0R)m zxej-TSkqHWp2Rb!WDljzTb|KM z@G)3+3qTRD5?sY!Kj6OF=ze~QX&mb+-STuSSiE%9xpC<7)CUE4a7aYU_OfpP0DcQu z|69-PQ3FxzQ$%^iQo`E=8P*aY(K~b%o_n(f-s@Gx)UhPq;mFSdI%Bezm{KdLG~E6ff9GEG-5PFm0XOL6P!~n!AT)?a>3~^7aXogJ!gH+YdXU{Q>wDTtYNE2& zmL`UdD<>dHYq!_H4>{HaKS&ycnzYD`b++kx(QGhDz9OE+To#FNvfUJ5)E^3 zc_OvPdhS6hDek*IUMm(<=O8G}X%`n2q~Yo;O&b9Vb#Vnw-27*|y?=giXW(_{rnS+- z_(bu`0>>afCW_9Mqt_^0RYIqP(>OxK+yiKvfn?a}vUsfIhmx;3-RI^@_z-U{o4E)d zJ%=BI#LEC2y*JaldG~qHEPR^}O>kg|_t;v6}8S_O@GSZ)G^2D_rE4AdmX- zQ>3r^k-OYax}ASD#jG}dv_z2z(ZU_CM-zE4ZoYwOD^s@pd zyI%qQI{0U*ab9Z4Kll%Ht`V`c$tiB!Rf(m=X+WYlCRd(#NrT6T7W7%5?C2K4JuxX= z2_{9Ldq-SJbzsTp4Yu{8tsl|a@OgciMXfQ!1!&G6op5FJjA?z7^`y7HnbMQm`ld;r zd)PmR<6JG`8W0HaTcj#)@>V!5Oy*IAHtjHYpDni!K5knWlK9|anV-h1ci2+V(};z2eDF=y2wkVncGftB%uTGzv6GW5@CWOrFlM$I|VXQ zZ8#cOV1D9<$U=*p65+%q!~_pSQpCirP%P8!ICy!xU0Iro=z87@hq^}?%E`-s|MM$e zV39{70Cs~`g+5Af(;k%oA1?vAlU1KzZ2kj!RPH6rBwJ`A}zsT`m(kt=G`xdHQo3*ZRC-oA0$A#^Kjg>+@dg zPZV2ms`W5j89MBU!orQSKOBtz7gW zj8Bjl8BU6O47h3k#evAs_{_W~Y2sY9IyvKmL@-`Ie=#5WEJy3#5~L&+u8b+$lTVut zbIY;uHg@0zhSPEoh7fF!uU66HD28As)07(dO+3&B#4Cok$k~8p;o@AZmmw zPfi~M=~52D?ef`!&GhD_w{JQEd%B^M?mqZqDu=j^I}g52t>7+{_YeL>YPpL$fp;@L zl3Cg!XzUgq5rL)3HKa;@fqp+d;x$;3P7<&{w8Vk9M}R0ITq<6`{1j!7e-dL^PNvs( za-{60gKv9i`u5Q2_$Lu?nHp&0;-P%B1dFAqr+b8Yy4!1g2GXI=x<^nx97bt74hR*i z&@QlZvPee|UyqGa1+Xaa7%mr77r;FlFpAJ%K(-YT!v6Zx`_Rjh}3F--|ZfE)$+?XkY7w|pz^ zU)tkq3eXHZL|XGt^xD4C7k%Z2X_ZJC(i+e+Nqy3;(y;s5?0^ajEg&Zi`AOa>9ZR&f z_rLDpuMe!|e09eCvYGQRKU=AU0cr{^7hjU<-HI#@#=d+m1 zE6}E9XGjO&^xuJ(c|>~wMda{v?J(*8t`|B&QA z(Ltb=;AQ+usUHU6y>yvfs~?supMNpzK_pZ??%zmnH6(4}_NCYT9_W7l7ht^b{};(! zkj1h)uSShd*gSmXVcpva-^lXLMpVNkb@J<-`%$Cw8Oigd&O_MExN!w^3$V=V{2hLJ zn1@>7@DDceau}d>D=ZB9$|+%P6Z#5wb`uriC5d}W#)Pkd?zHer5+qH0&^Y^8b|y}@(KL#>vO`NobQ6lA#hQOnIN@HmGU zcUeTDg#yjEUO+^_fWdxB?;PE_S}sQn^khhPkHJbud{%T?s=5E5lG50i+KAsXz&gWP z0$MnVI=_ZoXJoSHwsit+C*7tE3;Avq*u!C=^F?(337>$M$lk->VAkK6k-9U}m$p5p zH*$*<)X}LDEHs{z|AK_Z2-?E%zJXcJvDEOQR7|@SO0niN4 zYXB<$OvSA^796aQ-%+>F8{*U6VI3n_zzc~om7j9@Yy@PuV3ZMxrC0}+-e2-~JR0;uyw)DGp2oLP+$MG`S)F^Gs$Fqki_aVV3wQq*-+p4Q$Uu#=bQ^ zK`!NZ*!beA>bfn?EPF-6#)|4JDR1_yT(&2jc|OcAF$);j6ugc~P<>YI285sOnSku{ zm~4?gj;C*|o@J}8-`1cN&ZNY$9S!)|;aQ~>t}R+J;D{ubFLvyxtI{i6KcIE(CvCNw zHw?(Ris+8oh_>2DA{}zZe}xq5a5^UW*SzLi*xuo0m8nTv}(5 zL6=t5xb?D;HWt-aV5T?f>657EmCl-qI_Ip?s)h!@tY`5F(X#NTvi4Cuhc~9pU^Jl7 zQ&?18(a;cSYbnaC+(4*KoY+dmJ$AUEeShujO zEEWGoWM?ldTU}Z0-yabb3Wr2eTB2eWv{B=ScPBAHcg{yGcAKuusIv6 zDi_pORrJ?vurQO{VPai(vpJ%-ypK^Cb@Nbn7Q0Q1E`N}^Vw`2J9n~1xTAGgAMTNOT zRuFpzooq!XxhzN2xig764a{^s!F8*sGACgb9jXc!ppyT9K+BnIx~PzN5~3=ggKJsW zE$oKD=2}}?H0&9{x^h@HZ+On#2_eycMGmE%X-Ur~-*Fs{o7i;;;aE5O&b+(tLaVQ0 za9KkYDqtoX>l(71yV1_GL${Nko6AhrA=}B%osD+p3>y-j{MWKzQU!O!<4+!NUwHoEGwA95a_P#JcWb z1s|)WqAe9Qo2#gW?aY+Px>hsC$Ev@ep~1PiMhx^$LEkMJ9k)0@R2sl!I9<7)b(J#P zU~Ox-9yM!sflFC*?qzWlxf@)y+q7>i+a!%>47nLB*&8awDqwI9)>X{zH2}a?TjLT_ zQpv|OJ6{`E@YCfJK1c5-WOH5Nzd1Q4SHPMKqD@5)z(+>0b`y(Ywl+G2K9h|e2mUdS1qootZJk_ zoy;_qbuD3wjO#AMPqc2XuXWe(PUpo_)WGZFc%7DI zN${I~%rqv+p%zzds&H4kN~)Zjx45*S-od)8%wh~1%`a7HteagxD6I=q76;`p)78n~ z6Q(<+b9oBzX*pYB-2Gw_DH{X;b2tEy{Hn1j^)1B!Ycey9|IR!O%ccR2?7U>;2G{ItXKpqgahi&`y`6PE#8w-RlqwcG>p`QSB=Gf) z8)9T9#FS(vH|w&q`wcoMs;DWet9H6vRrM>X8nElBhx*3ZckAR)>|1*+L`i1C-f*yG z22IjnNt~mW@{$t!ZcPeVsLd!64vL1oQo!;Iz`+Pt);nEsL==Lz@s<$1Md-cLNfiGL zpvn-!P=4NA6oP@EYbCQAfL2_EY2W6osnVAqLUIRP0l7M5NLfJRQ^mClHlb}zZ-~LW zxvZ;*eaZ+jQD0PC>8x~ZanP@CJ8L%6ssek_%BR@f$+aRjcyt3;JD^e(TW{FF!m3SR zVFSmJw6=5~>uO<5N%k|?c<;=OAu)5bFI^rIpX;>Gb?atF(U)AJFBaCdi#?ho5Cf;{ zO1r;1nQdEK`*CPNJ+?wI>-sX=XE>^;NiE(`T~zJd_;5d|aSnp%S32Z1tn1qh-XCYZ z%Uw|&t5WMKl?6lQ6nP+mUs?C{3CZUmBW0#4*0q!^HiQ*Iq`kJzT}P%Iz6%F!@FCXK z&K@^xa3G>32K>qB11uvtb>l) zA(^h+1U_fXb`xoOEZ8t%lxSsJiUYnkud>qYkVW zL#V?V<3b$}2U1(gs;Y&|Bf5{UMzl0G8JP<$m2Giuf-C|fjs`a9@B^&O0250pYAUO% z1U#j+?uM$;+U-Itnv*X~pcdUaYH_SA04HuBT4bc?+3Tz7AO{ofa_AC^i!QR}%!jyQtU0cz zs;JbkjKei2p2Iq*L=`m08LVp_n`e@$bwHw`Br-g($D;c{| z(pD<@n8DERf>{yfz@et_F2avY%<~5JpwVcA!9Hq@YLk>$ouqaXCM_P^bg0qF#ZaAT zR}8TS(H7jNUFB@40l<1&x+X%dfT7#%M7s;wJ%;T<4XLT91|J<@S8Bvi!4&Rg^OA2Q zcHexG7Gf1~t%dc5n)u?H?G@Ed+Pa)V<;7a-wS`V#jjAP`aV6H%nCvg9t*Ers*KR9= zxLUD|x@kAX|7)(R6(tedBQ?#a9e1BDl9j4|#WjM+7gx9{bjIO!viU~P z);PCm^wjNiJ9pI70a~ZCZhLlZ-F8-JP`gMM@gkU`Pd_Fq&LJ}Za+o>(o>2=E9E zcnBi(n)5bhe+lB(@h)y2+Do2p>y-zeDqfDJ9|N<@#K=EP}~ z1LqJ9F_+|QaBd(AwzIkt4I2$H-Ul?WQ3wgHD&RxKHZC&PV=KpcR-+2U2Yw9F+DhTK z+a9l>4k!-C8O+vp(3Yxtms9wm>LTtw-7X3bdNq_b)_!$rqruEaOyR|7vlP=x^D~Hn ztA5Vx7%t~1ak@BZon4?AW;HY?g1{a~w&xOOWo1>3zCN~Uq1+CIhVhDWf@#tW`~%yD zzC+lP6 zXu3-8hGdtuXQjqDVFt&ZvW>*1B2GY|Av@WZjli#6P`SMVB&<^CiC|ri@&$}jMI!>V zC>9Y_8t3W=#KmrSnWL@g(I4y9&FL4n=R(?!bGQr~n&w}E(8RkFtD76^M+cMMUePer z1|;u+zUWT|nNYJ%#=jn#SA(s|xV_NESh6^cLB98sW(a7DDhm^Cq%v%l7#tx z2K&GqHL4#QbWk;p(F?-O46x9V7>Ex}4aKZqy@dzacfwVxMTx$tfWZ8-q`L zkb7|j)f`+?H)MiwR_26Iqcah~Hq$B zK(v(vB>ge@qUrP@YrF{5qan(btF8jpf&Z)8ys0jkvM_IL+TqEU!?aLtug)F4Dq z^S&C{;1CMID-xL#=BA-GiIWu@4uF{aW({pY^AiS?#YM3u7!I{!ZLEV!R!qQ3!+s+o z#-kBU!e~XDFv^`UY?weYj z3VdGT;{qD-w_7{(f<4b_1D83A)kcd4#Uf7Zxk?i|%d@A0Tkf=qD^hvM~ zV|FC=Pcj_D$VgXQ%RRJF5d=dR#++=0;ga+>mjrkqoX#Z3FmF?rsfMCrwMi_44gQa0 z+KP{ZOR%C8NdujNjyk{sQ(93E>S#!r;#75*F9x(hif`SVgt{{}#)u?jLxL>gW~h(2 z@*~7ZWX1=LCLW@&Aw)G-cUQ^cqGe0htz#Pu(E)&zz?V{{#UT&1c+SMiS4}c~aPv|m zplGOG2B5ct*QGt|tS4WNC+Yqiupt>O_mJJq(S2CuHO`Gt8Q^KerZKdH7uRA|i4{hI zvg_epiSxoHUQ0rJS`QxYGgKlENMu$T8!J={5#$-l=lS=i_E$TMZR@7J0 zDeDTC@J!_C2gqVgv$N;8DmL4ettegKusVvSvE_!HXvBp=h-qQ!8wc`PQAG0y7&>r^ zYeo5J4aK|dv$^%+@1Qp zUUVHtH{ftVPEXQ z>PRPIcDOx>dyhW3wfl2P!pB2Ji)$a{Y=6#O@pd|%z0mD))z(~@01Yw8$eFAoeoTPt z75DnY$C-$7u7CW`PJ^*Ux};V+7ivW5;pb)T_oO-vhq$&!>*Jb;CL==%_Y$?6J zYKJgR6EyYihWaZ*P`1f%_!NRiY>d=gqbV3XHqV&%mmP0e4QEi`M3;!h3$d7?PJg4YxOFa(HwA32UE|YOG&TJg6AW!U_?hTbl$9 zKoqKXMa;o8rh!^)B%51{jg4Adyy`d0);G?LsN6Vtkv(MSPG0|f%+1f9206Fdcu`#S z?u{-;F#UZFg?&Rt85_mw7S}qQ+p6mO(Pn!Ryqces(&+P?SOpL^RP7uUoeYe>KOn&d zleB{$QPHe%=YIxPf(noLHQmYLb|&prfm~;}+-O{HIq7x>Gb5ls!-ygD7ZD&BMYLLo+w1R)q$QeE4)NOO)4sfRonHeGc>Q!rFRp z=weWung+U+kzm@++6}M{6?uFp_YR0*3?3H~PwvGPE4+R(v7`7E%=YD}MsW}X5f7=4 zI<{>Tb$~khjYzQi6YCM9PogiefFG|faxI9ls>(g8`vSv3S4h%8xVj9h&C(Tz{Z^kzs00D5Uf;|jX;3Z4##q+*$k zjQps!6dvyFRaau-_-=G^nK6BlgsoR3q5LxqMg* zxe83}gD4rDauSsi8U@`AhJkCMwlExPm#0v_^$9Kn(5@nY`(hBQgppY)x-ZR0Jybt~ zbyI18&QaY66Yx--3_2JiWG$&^NRAlC)9EM=uc6xuom{ytYGh*jI7VJWa~L(IYvDsN zaO{UAsYZh6V(r}Dg37pa%Y!rVA^S8kcd=L1m znF~035_ei|tE;VnyK!JvM2*LGnw)BMqIFrl6Cptr)hkPv7WId@&n+n~Lh#IO1~V9` zu$^sArf9sln$Q6OUXqwRl9*I$$hvZ!dW^!)-kf|3!TMIz9UEGxxx%zM8$ZEGwynv)5YgV~ zTqJG>7FRS5GE0NZge%WbT(wv5gX==F#}Uzug9$A94Mwc}-c%z;t#;)Q1;2^r2H<7? z1Ui|K2(p6i8zQ|^pvi#ShQuWf&LJ_y$&92@>ZPA6e3DKKLt<7`Ar1|z*{}IVj{Og^ zu7=i(PeR_y4bV^6+vpS}&4+&3gP8pIg^Y5<7|8=4-*T~i;+8>V7!vWh2ei3K7Ee@1Sb1YVQ4`-0=UvglQ4RmqN{ z8@phGH@ag5H&-MhY8Y7!#JzST8SteXRF55sAREdByc0tTH)Iz^mMR>i=Ki-(7l!H z9fEflkl=+?(B8yeqrq%9w8bQQs@QP4i^zH*`ZPieQhyz_5d*HMWMtom+B~36+*OqW z))i;RaJ#vn`BhvD)`+9gq2K?@-n+m@RbBhzI}cz;0uv+%C@M-&6jUIfU{DA01QQs^ zWCMyqAPE@^nV3vMkcV@UP)QH8+Dg?{+O(Ys-qyC%THD&!R4DkmEk16G)!W*8&&+_> zHm&?1SFQPf*WTxxd1M{|@9+LU|NrNP4|~qpkG0ocd+)W@UVH6*h?#~bM`G>Jner4D z4rN7lN2=YA@a~v_wWJQ)`*KDq8(nD|9qf^(eyLQ_$(w3Q5U}u>$E2WA$w?T-;R^47 zWw6F*nFS5AC0jNJSoJ{10lxJ?_(zz;ia0 zdL6#ZaX2F_B?mDsZlvpusCkJTdQ*8x8F1wCQxYn{vN7fMRD_4A^fMHQC0GPg z=v%SyCi{j&IDsK4o>?VEr`WLsGW$A)41W4ls%u}FBs8KJJ&3wwvKW3jLu4ycs2ld70& zE-y?+N@cL3TUzCKitYZeeZV=atJqV!WKmtl_#H0HRI02LJmokd_yC*|Y8r)2ff|(B zP%Vp^eq};zx?;a3H>87cNoN*a(y45aw{$*{mTDCeUciS}?#`5VKE^i8#FHB5N-2|R zB`w^!D0}LwFd;Wg8@jx<#n>fCWg9wwFS}IJ`+U!MUu+{bM0}^!wzwG|*^#>lW=A>c zYpQJTuka2Mq2R0L2jAWwUDksj| z_}|u)a|ASsxENQDGaEd&s(aUnP@Q~_F0PPaM zjq$%H8U_$`OLD7Oe5)*8oDt*O7%nI|FL6pE?)<(PgA>!Schnew;!xEl{RAL`jl-UC z4+JDVKL#^J%uRI3DinKt^6t?Bo!`K@~lT{yHt2C#o zKGx!v3~a=T-tY-Fk&gI|V~n&&4$=JhV$8}hSt%^^X^OYU?r9_~Gt7@ro(J*~Yvq`& z^kj(0H1aPpZr`M~B+y^U9{);O%!fbEA3ALCF;=f+FiP&ncaEgdD)vT~SW|v7FhZw} z@Wzz8&+(0bPPQw0X>dM(<6l@2zoG;=(p* ze~bEpT+CislEUY8$!?4rS`x63#BdI2Zmik1yk?77v<)$>B`+iP%v=9yA^EjtXR8blA`7u`l6ge1sO5+!^CAu-Foga#N)Tt&m@bs&^}+qd{AmuV<#kNsj|G9(->EE^+=4tsjE(z+q~hRGzt z${G8anPfPT5-*&}!@FI$JIy^e*8GqH2AnzCo%phraV5F{sgrrwgIbIWaw&)hCC0>) z7#|xLlSyRf@H^QiFXA_xfTH$UhRxxzE)*k;u7r>%Cg~_B6)YphpD}s6B2^VA6MXms zSfW*q1CTAL@j5RCQc+hg>^kkSHwY$fise_iN25VPsd zFBLGFniHu#_TrsnWQ{c_CPoWWAi>(2PH{Qud<53#xI0GQ;5Mbsx24pX!KO#gf%wI2 zX1S+jxu2%RxThs37ok2R{mOmJZM_MgQFPKzLk`)4PXGw2FQpJ~X^-8hNl+x_3x7kl z4VF0FTWXpU=;iLY!#{QrqXr~EC(eggu9jAkn4=E*Q7ZW@5&C|;*_F@8uAC0zlrRP* z?I6RJ`U6?WPA5a64Q0>|Tw8R*8Ds&BYtp}hh}^8Z?82ZJRR}qZ5{p~6HEpcgh(nNV z`j%01fpMLRxjLuMFo}$qV)#lu38%#H$ZJK8mgc!bl;MbVelj3J7{TBIUuHwmi@WVc z-%U-334LP1uqX;@;G9TQW<4=JWE>xl${ay6Xf=^48T5`hY-ZBzVOA+JW2_~FExZQM zjdc6Mlwkoh#9T69r25gnxLt{66+~mMBU(@Pk(n8@5~BehESRCUg-O&-pBzd=1wD5x zS=(uOlB$ex^zFeUSzYwKYAlyG))nJXPLE_%X~ri<64dLSGiPCRnxF|f<%YxvkkT#Y zq&y!WM_Cf;nfZ-5EbjeDfRS8W39G+23MlVP6V7N!N)$tX-v}!(&i}|uEnAvzDqpp{ z80Qphi5o)x(Bh&3Nj+ni*W`DUj?V=pnbRxl?r+1`5JZ!7NHQ*j)y3dM*oG-?c2XI! z;7QarD2Gz9mnbIe$YkTugb{tCi;GsSDeEnEwc#UCOWMTOr-Z?jX3L`)i<6YaEfjh) zCD!rzExRu<2&viXK#XH3k`RUu9=`4rjlG^b{@1yajb~YzW2v3Sg8bZTJ9bQ>p-1q2 z(U0gqyM3wbza(?TIKGVg!tv!PL5`$+AnXW2dE4o0i;21jJb=W!%$H=0qWdFO$8-)} z?(Z2R5#Njs>jB6toTw#KXs3y44{r{%(?=eovgwd%DogKGeWbTzCo5r2*}K@1!?(!X zW`S7J8la`*a=6-X3N7+lc{p>$MTIV%ZKeA`s9`vTE1W{Y6wnjJSq1V5!p6{{B#_*C ze*IvufH~i|P+mHT%#FrFxQNu?!zS%@dfD(LC%EIJ4L%6J|oWvK8=#5OpT2Ykc(R5e^aN60u+UFI>&0$t9xT z3QMh_siCl1EOB(Gk3*s6G-Cf9(93oBEsl$jHB~!@)m~DrS2GLx%ys#HX zbYp8I*mft<$?O`pw0XsT$9!XTBNUAIute^#UV_W$SnSvm%|Txo%`tqyVj7Gb0x{W{ z(oJk=78AR6HnOq}-;-=-Ia03#%dK^7WtOs9XK5okjb#HOJj}#ujb%|6)-;nk2c_p< z*w~fu?T2m3h#P~<5(2F_*@&vfy1Hg&vBTY4L|9N>`24XP*&Wyu==1+*NTdBx>eI?9&_wi4Gf$lw&6m&loWHyEzorShsjyGz&S zp`%|Con$PXp}sreb|)-`$*gL+!Lai(SykA|s8?v}2GYk!D843sm&-y^*=4dKW9XQL zq(8Ws{|cG^1l&Vn!G{ffHo1#D9Ft&qGBD@mgm85-ND+sEOcy`1i4lmK?VKjtc@4f` z65CEpetr8NTWI>t5iP0Rrj{lHBu=atSGuP3z^X_F3Zt1eo{d?X8nIc)pba@#bbXTC zR4?&dafuc211G+6lbC!)_85F|vlN(%!eFef9Uu9e3dct&o?Ii(WRBD@A& z38;qs@eN6;HBLR^G`sGPgX0(pu}oYC+xuMlk|gzrV~5G?Zz4krF3C$+xlMdsK`gGt z2f^;YzZF-Ft}ClvT8#5~bCML)zsg32U7U$F&*l{+DNitg;!6_roX5$ILMRUxl!i87E5f~BN)?g{c`Rc0;C9EqZe1iV7F9WD@?(L+$26Uy2eKO5GajOnRby;ptPk%+yd3HDFTRNW%cqRnK4}L;tfEp zW}~~L1)tZGiKa}pRf8u~EN&Gx@B~MH6R1b*VgnTcq^QxDiKfCN;x7z#p$WSJ2twji z)HQ8#i_iYFnCiy##kfxx$soHUAG==8+4<9*IIv|7sA@;)f=I5{VLF5iDSFfV`33V~ zEx0MFVu#BPyPjxRN78B|h(QR4iE)Jq5B<2frKM(DRAbt42vHwDHY0<1oB|>U?9W`8 zP-{w>=re`XiHUk8x&a`Y$eg<2thbT*VXdWMn=#H&wFNio(}qMT&bq?|NZ4`Hkw$-G zTDR1|AP%XEuRFJ9<88VA9qfznj1L~j6uHAQKH_*)>kaZ81OOe#W*T$&)3m7wSnqYr zk4UOKEsws)X*iF#p)78MwOiC*i)1P~QWQ;Ry}mSm24kxUr-(m-LRm+OLX1tp@05u2 z3x6Cbi&6!nO3kKFqy_)SmEz2uGy&>Z>jWE(#9T@ghW_ZZELqUm6t7^mEa+^CQ!p24 zqf1vbNy9VMl+Na%rN!*b;AZ?T!+Qps2x1D4>BTK|HDRi@-h}+1kYwJVA^t2Zomzpv zOx)H`pDx-2AR`yA*oxbG(($O5l3IJRQfX{tm8%UsN2S<-Z;KAg&~hFAOfAph&(J1O zfUeKbX7J8w+AQ8#tj*?~CE7gRS*k7Ion_i0-dV2Mc;`~Bn0GGI%6aDwMcV4Njclal1TO9su&ExQ=YX>O!2?f8S;2*s6@<0}93tU9OO%DHr zz#YIPum&XpjVK#X==Jlw^D@@EAkB{+7M+}ke8!N=WbuYK6%|8bkbd=1NRRl-jU6Kt zi2-6}rR?kQKaFg5Q6q+OVz`(&_0a_i=T^pyAIgfsV`i0og_#YY(W7YuOV34>7%R7; z41OBq&llv+&&7{X6UN*i{nwl1L851H&|(Zic7ThH&eSfWp1l&()NUpEmpJ?vYYp_& zOiy2+r!Ujf&*|yc^z6ixS~HsH4+inr_5?_8FyZ1H@L+7LiARYMmb2djbb+w zxyVhE&KaW;dK7zQt$-@NlY&F9WsTdK8(K#E;e4H8vvM+_}C%bc%Rs^1{#Q^N6GiiO(@h9rVOs@ZY1+I z3>C@gKT^Bg0m}DY#r&fr{YU1;(7zR9L5c#|Wuvr-qR4+TKUkRPvK(`apvOgj5vQfU zoQ76`w(|jyD665B`UBStZNgxoM|&MUMBKD#bVDn-cMB@E%617RY<#L~a>I~Vy<~1f ztE8imH7_bc&xMKwon=+Jp8!x;URPJEza=M=MZ462*7jb({9qr{=8rRg%**c<>c8c_fJVa!dN|%O+=m`Rx?n9$zos77VrGho-F@#NSY1nlQ)w>{0 z#ottfV@vh%h}9_ktM!z8q-2$vb4ib#f!J)Q66Ehh+LsnJas`Yx(FFYl}aY0N7)dSE2E?)LL2jXS~0<^H_lDbtw}I_w9txN z&rpsG*`;)NKdLnqh#o7V%`^Ra0XtH%N@lT5NymxjPV@S6Yk> zu}##y(4%G5r~?`_`zIz?1)IzYFi0!e;NV^OT>{nZ3H*}k_FM6L39AOHTgEPBd&KV) zb_~CiZ#HDpGIkkj!Y{qQr~gg@&s@eXXAbdu1)B=o2ZbIm+GL_o;|~058mZq|q>^P5 zXUHOzEK8e00rax03)agV{!60uvQ=8G!+$A>|0x<&tvRFv%+=fu|2(b3;h(SVaQJT` zt?g#5+u>iJ?Q!@QYI`02TeOEA{zC0hhkudwxWj*|_N2pqo7U^_->!Kbew+4`!@pR2 z+TmZK9dh`KwPzjvW!jG&{^i=w9RA6g-{HSYJL2%)t-a{*uhm|4_-nM|4!=`-)8U`3 z{h2F2&_3XbJ1~lOE(u)1m4U!a-dPk_;_#0OaESjv16NuD-CTJl@Kvsy3p`6tuTbVc zQ*aJJa2(W=;9Rc!Jy^n(e*|kNt`qr!PjICz_yewdJNOdC{Ru&6l*2zdbP4agBm_10 zl2921_oB)W6wjNLHN7Mnt2iS8Oq~8l4S5JC0q<>M1#FjJQKcOlF{~M_X97h{hQo5t+^k z1Zc}rtkKAwIzhupXkfCX*=fxqsg-9GF?b-Y!iG~E9~H-6R@j$T7Rq6n(QOuQSZqmR zn=KwI@49oi>Vj2mTWU|joSw~F?}HFv9M95s7+@#NX&Kq9#wgq(MysGs$b;6xM8=Fge#SG z%A!E*Bwt%6?farn8`gjuh((<`wTVr#23O!I8* zm$Y7bofsFUbhh?SVn|k4M*`P^fdsG;UJ_UhjuzNN2`*#_>;xAHs9Z_`lPM?wBMjP+5CrqNB6z>UzbUwznAARs`wBgMhgjGV3f|(% z*5F?p{)Q0N&e{EE;jaMDl@1WW3g(bnN3xMpyWy(h;`X7O_XAB!{DzucRJOH*N%2Ws`6STo7d??P! z#>8CtM20UmNdJuri8to5JcIQ{s-FY`dioHsl)_+gBY@#&mHZ)=h_|oGpQG|8Ab;|J8q(&=A1;5k%b%~wpTqJeAb*JTC~$-P zsggh2<*I|>y{`;Eh& zPYm!1EkKr^JmPp$9RBHn`B1U~uq0g_;D8sn%i+H)aIeFEO<+4^>2mmI1inU32Oa)v z1BXe6d&S|uE)XE52>vxQm<1g!cmwe?@UL0HDpJtycKELk5`f^hpzsBsarkcx{wr}o zsEFCYw<-7!3QkebPr*3~K0y!~K|u}$<0-g=f-5PQLBWj_2u2B_9HlE(YB+wCQrK5KqFy03Jq2j1L>%Qm7S5xBd$EC)$-1Kt$}w=*BZIgxzedU$(3%cmn*wj?;XrPRu{)f zng&*&|HU&jQIcRTG$0tYT{Jr%bC>mgpZOty!vG{48X)wySNcYU{rE-d$4Ab)A3tyOqtc_T=gPzAN9D8VM`a)Sv2zCX2X{Qrak2VTu2{ymNu-$lgu#u4KiM~rVnoPE~< z?7J$(wC_6C*mqT4kJ@)tmVogo&tc!y(?yK0gc#osiSbP&0d@%(pYkFYU*}9>e6zs# zIA^) zSN@Bw$rVj|pDV9u{aguZXSfp52Doyo_Ayr$1{haP1~R$wTEN1UQ-K_= zyn&rZJ^KPDx#x3%_qpfufqw3JIBXKY$zVSB90-D1Uk%RU zp05Q#t=`}~?)iFf0rz|(xQKhc8MJYaH(1O)zF;}`^aU%p=U~vzJx>K4-19HN)!g%~ z;9Bnac5prSJRPj%p6>)3xaYgUM(+7uu$g-f1>M~9{a^?8JQLi(JwFI~xaZkmH~0K7 zxQBb53-0Bf9|a%go*xGv<({7eALpLugHLkLPlLVO^Ru9rdww2#ihF($e42ZH89c;2 z{@}CR^Q+*Gx#xx8&$#Ds(9bd!w1oN@y-OxPl z`EzIi_xwj_5%>IO$i_Vyj53};sGNI(p$hH^h3wpOGUVW%Q=!$|^Im8z_nZ!`=braN zwcPWUPy_e;HPpyGe+xBpPk+eGJ%7g{#Pg5P4(|CNFQ^KcPL`b2hY>d(MR( z=AMDjquldfp~t!BLo8N2ABB3k=i`umnSlOf7gFR_I(AuVr#2 zPqT1kf|kRTi?v*?Ow#hWGFh9%l}oiLT)9k}%9YEte6CzUqGBpUg>ofyWaTPt9#^Kp zMMudeQ868&Lb)2ELb*mO=gJI-3gueO&Xw!nqNB_tQ85dmLb;wq#SJ7XZX{7Ln?wb+ zg1CaMAg&ZZR4DTxDwO#oDsFQ`DA)?(3bul{f~_E~+(x3}b`ljf5*0-x zDoROIl#!??CsDDKM8z@^6?c%Rs31{MNupvoiHa2@D(oaG?j%vMl0-!ni3$gaifR%S zoJ7Sc5*4dSRIDLUaTke-yGc~6B~ft?iHdtkRIDRWv7SUl4eKpue%LmJN}s43429mB zLZP>cCCf$Rk1`J4G+S8j*O(tROQXCo0H`whGve#n&k^m_Muds_xg_maZfHCn>l3cl8hIK*8(kG4Xn|Z1lRGt(oev?zi1m5mWnlv5@LI zlA|75ckkVY$%x|3=AG4EZ1_36Xf|iPoy>nRV_om1wPd-kTIOE3%w2VQ%Pr*=cf+1? zOZQ9dmRriRS~K>PX91um z?up2X#L?lz(K3-8n83XG4*x{&Z4|%&Hqi^ZPGr548A4W7-_>yub7!e1)Z=PV-e6TO z+R|%hehj86uKok@M^*B9U;s}j+8=sKK2}RUmQ{OxjMksEjm@-nzXmdNzir+95`GK(JtYIkq}n7(>WMpczrEwC zqJpzBQvIDQZl9-?42VbMFTJDf&yiixQ*x%jU)}Hq;MES!uI-tsmYk8rY&Ny}V|8V& zY8kxKV#Gdg!61?8)dR16xGkgWb(T>*knsXQ7x?d5v!=Eux8V-}W)wJl3NP$*KcQsL zi9kfHCfo(6d+$~Q_Z&WynGR2lL1CW1lL{TUlX=I1?Rgh+> zi7Ov#+u&0{eg04H0R-AN==C61KGBZS({G5@Z}QHyUPv{U_aCsNv);>@|1xwurgD*_ z+HU`@5o*zlyAOB0I7qB(B=e3!eXMsP^Ism5)A}`kRF)j(zXF2dIa}VmH1y##=AX(~ zp?#`*QjdM=S=)p(w-udJ@Y3_2pk1P=vVzH6`I2BdS5o#V?y)`gDVtfrO9ih-WWpxG zde(ap^IydnrvYjaJ#jB8n#cUpX!Ht}R5ZJ=e`h)}6wNB^-z6S$3XA5ojW&5sPwPA} zFwwN>f(REgKctx;g6v+f0Y<#;$~4djfLB#x7^)uvs2t(~t!{tY*q&WE^~&dB$WS>fcK%zTy4R(~y^tH(XtEzJ6uUtK<1 zMD!HrTU0(v&CEB`vt;@#BRx}fAdeJFc+8X;2Rz%eSbL88d$TBFEUY~nF?wACg}ZXx zv(OkFzq7U}X>DW9mfn-zhN(;weZ8K7=Px{q)dz>`jU@8`<>Ua+HP?;|wszCll5M=tO4l<+D&{X0GV6(xKY1j4=d>TriHD(rVx z?)JOKt5rG^s>)Q07RlBf?I~K6C`}3n*%&#VBBjn1%sYWA|HnI(ci!Zk!=~a9I(9b#I9`P1-z&5-WhH?GDecr z7D+{DO~$Lv(X8ohRwIhf(Ay_}bh3+adjs>1CM03gebhUff+h62f&y%VZT4atf3tTR zS2lV*cw)VmF#ilxRzAD%HO!3HdhD~ndjwoL|Dc?K6-;IRYZ=@9IvVx5T0X1rHB27l zogJO`Vv+Yn=D$u%j5~Ys@dRohsT5khAZKa@_f*wl!HpYh+TZAAk{E}xfc8fjF zJyCsmPkCPVOYX6%JyTujR&A1-fRWtD{IdkTBAAF6N+Wu9m7gACeQtRLX5&$H(qNTc zq6Ovzoe1zBVE*e-^iJXqnfbTaE8KPX-D9uN+g5DMbAvJHgK11ISYBrSS=+=kcL5#* zjk|vQGtwN|uG*ZYngzirtj{g0AdaD~EC&u>JOmovMXVhP;^oxTtEuH;X28t@NP*+R z;RcM6_~JkIEz0-#&e|@vS@*xN2P|k`1=!G=)R+a<=fJkWEJUMvXE8sv$H8cCshaG* zY){o>eLlvSqEvgf&RVdQ3f2$ZV7AZ=EX+TLu|p(cUSs~bVKre@Pvt-lg!Bo>RXe1q zT`ig|)tH{5+2OcZM%=7$T#k%`qAQt(XL}xNyV6W=&DDpRnN1%{y3|zrm+wBiwTE{3j`6ssjxP|JX)rfSX5iT z=yKKL1yr@#lCk7W2J{X~Ve`N)v@C9hnTL_FaLK@q%mV-Ed56+YgKWqM>=FC^t||B> zNG)dykM6X}7j@;CnQs)n@F0~)Ka_Uw-S^0z%cCJPUyPRf)BW@GUV0Z}1_Ng2fX~-c z(tot%G&G}rLu1?Rw|?$gj8BSMZ8218UBxeXAJK2yir>C|J+l1qCDFK^5)HB9vHlmo zmO4eZ-`Z`3T4p3j0-QSm)s6t0+M$syN*A8kc|}wN4I(r1%oAz%L>4Xwrh6Ym9k!m5 zcOe3hv)Q7S&QtRqH`vv{*|G&5_x9?uWed}-`~7Nlzq+h=W~HXWojG6VCB2k-Rq%hoqCfZbvmh!6R2|nX`L1sCwiiq2kF)I5wq?+Ad4kvTSsKuRu<2! z*3`;y+Z=J)W;>s@%^cFU)46EawIyeO+@N*$Ml=lb?6jOYhZ+0w6Y}qq+ky6hJv56j zMQ-8BW7<-97eiz2yoVOKo4wd!zt`)gr_XZbaqpw_^aNMl_xkARTlDlJN;pOUPGOGu zz={ieS8(MCAC{+^eHGAQe6T)j_I(NYe0|90`!(eYQofInuaAa*-wfnqy|*y`e98Q2 z$t;I+eblsJ~I)P#@Zh*uls4Ae3?dJdoso_fiA`6uh;?9Et=rcT_x3 z^AJ1u*ldLHK!JtdOrRp+ydY4TsN)E^{)YOQBZSbG{0N2e0enUgK9dAKE0}+Qz~|0A zIr*f1A1%&LOAEL08v@`Jj(x@XBkTrPy`^3P{216e*dYeqZy+m}$%L3g?4Mh8` zKSHIT!K;MomtI78mqCOa0tZVRz%6C|LV;x0kpW#8f)7W4y-Q#^g`038ATtT>TEg>7 zs}ZgVBPtjt30&4&!~AdtG*-c@)V~YHDd=2sE`rS0i3+Z^{s>}cA7tOYl5?>nnMGAS zHUl9h&AAA11A^1o@-7rLZf1TQ{1Y|y(=cnZ?Av+9=d16j{6tp!K}5AnM77tU+E46i zNe-$#ga04IR$VNrw#lkL(5tp!j-qN<12+rxmGy39ez+eCouVHtC7nVp=<<7L>0{2D zm7yyD^sU)YR~ElD!aNI%vh9pBUu?C^3?w>%~bXz^dy|rc+`6x1;QM152>o% zWEgpbo}Qqw^&AB+i+<=#^Dhx3!fO5tgM5+N z-}T8SZVMs{o6FmDWoJ)u{v_yqx64sjk)KC8pKfNV$j{9{%xnJzqtI)tup-}L{o1dP z4wGobEib#X!6_%DdH#_GP2RI3e-d*$)a@3vth{SraMu;;$zx^0wusI+rv95mi~2#% z0_vyf5*0V~SGd-29z^yumrKz1t3}cnAk|m2b&6VK-)BcCsAG0~!=3 zgL{fVXmc7?5k;B%_8g(5>~WCkOS7R`P{V+d-TGWb{v`EHk+thczHTm9qVsnM=f8M2 za^=h3A5w?;sl(o)Cyk!|iz|Qi4)V@y-#Fy+;Z)6Ed{BjReD`zZyR7$G<}X4|nmkt2 z0p)45PM^CE*DH6xDnk<yIu2hi02|1q$`M{W~-3yIx7# zJTB@zbMr+}@9FAcphkaW`_6Ru%V>XKjQ3IsaPnvj>s^@PFEWgZbMYYesfW^wsMr!z5KuS-I zP|w|$s9T$<88iE330D))}Z-}bT z+7?W8-z^Iv6mf;kQ+AcdMory0KU_++<;^^5kIr&ehCOd(ILloE)ag8RokgvhVTc$x z!wrHqfEFOA%{@Czh&Lq>$}uL#s;hD`XlX5}WR#jKOTmUMGMD!6G9PQlN9a&05&K!*Al0s!lMj`^1g zg^aW@T8n=SLwdS<0-pETu_mP$qRncy_aF_z822Sr~ZgQd9N37*6utUK3Zn&P6uA6j*9qi ziuijmQ4rq(Jqkah)8l6;B2V>Ft`F1j_~24&_e&VG%7=8w_rc3i4}8~^_ns0EzKl4O znLuS41?2IFv35U#8kP6{BJ#`tf~Qu9#B}7dcAt|Ky-O{6Z^U;g%7dI;@6EGz(+d04 zRme>HLAK1Zp5LIzRO@c5j1qe$Hz4h-ZEafHbdf2_eyZC5^^K!bCxENSeNVb|_bc*6 z^ioW|Lo&(Jkq2bHDPK^%YIln&q}lS+XN?5OP=$@ly4Q`^QnGo_Mq8NwTHF^!MTu&Q zmbd->R1pg7tAy<0kX`zA;l7erv10gGWZGAGMxXB07|&ubW3*bkvqkl1C?7UwGo(pR zlz^lWVlX#ZyFZ7~re3amMAN}-Q9dGU+jgJ? z##yGjl>pc*T^%z9cWpAjNa_>A&`F7E7opk(lsS@TsJ2Pryb_Yc7xCXueLjAz!S;p(3cBE zun#NMvcB~c2W{{#eTV4v6up9D{^ei}o({t9SauN0f$0Yu9sZ>Uz4ZF8fOjx}K<7c3 zAka(@C`@rN>?YA%zKuu&H3!e?$m{!vUZcBCX0%y;kbZjP57JI!->3WSHbHWm1pq`- zg+Ba>Y&!u(b7!4;37!XEM|q~|`^0+xz~r6V20N|Zt0$f(@ue#mKe539;rVg|7>^4E zAH$(U_B;?W)9n%Im;sco+(zuHZC;##C6L3IH1H%0t6%N0uUD&&_tD6Y@RT4 zWpOVe73yYLAj~z>-T8>3`ZA5O@#-_u2ri7t^U|&=30b?FEJUGXoC+Ix27_FT%(4Bv zF9m}fld=NLYomg9A)`g}G6j~qkK;XZ;Gb{h52HNsWdV~`;`)r*Q}RLbMoXNCJ)f$G z!2z(WKbo)_v!}k$zLp&bF)vKHU(=wYIBB06yQbhW-iIiFhE_+nQZ3WIhJaS7b=vdv z`b&DnwGWqiaTIW~7w&+6^G(33Zx&vCus8q3XQ!tJ9sV19p9lN#k!tCC6FkWGcX}Em zhIJL5`mVt;x9@u1Iku0`=;IVes%767=;<*^JwU17rPtq4F`NRw^x#_FTbk9b# zT2(9n=!%AIQV;F+kbv<#SOGrVMZ5rcq{eA=SsN^z=1D^4a2ML=z<~(e}#sNfgkn6sIqa zh$p6u_-4fG?r71vp2@Qy$G(BKx?8Sa4~koNAJO^iYD?Gl{J~vQiF!lELG`O-a}tsa zbwg6#Nx zg_2DD+zo<;#i>wO)}RuKk~S(x)I6ZSd(gVqhJ?cHQ?1=MOEgZ>Z4~h4>a=?EiG9rm zS0QJzznN|if2B>bTyG6mzURdW)Nx)M%(;j>cJ_N;px{jk-lqTtx)vWCp7;B}jCy@n z(o+EifY<9Qp(mX1-tW7Qf}Ip}QNkDL3EO4+eNWTtv%tXjDg}R|r7%2WFAc(%Wm(@k zuH4txh1b4E!P5GE0hV?!1H}$v7iaxJT9F^bYJPpF4w(;rhFBkQ%!6Mc{`Vcq{2aah zi7Q{nk?9QovOy?v(4jyF$^9_k=5Eday+z-Oz~p8S*B)#4AP9|(jWhKbJ<>wJS5jy8GAEMG(t!*yvTD|ItT!)T?4E`nc|{h`|w7 zrJx^8kfYD~xg7u``j762GH?yI;aHI{&A_Ww7;3^x5p&8%bAnT|2+Z#cK#-%SE|GvE z=k@MI;10t@KSF8K+bJ}k3(@~{t#1!W109z2mk;j&g(Jz_Q*sh|-}T5r?j}M37c>>s zUaaSQo^G)b(a3)K63@;cbLSapv^i{~85L3Kw(-W>zUq@?R$Bnt=!JB5q{5Pu*8L?P zcy@jOk@P3(`;wEgmyeZ_L6n^$n}q%>)?d%#z3VoMJG&l3<5KJHZ;3)e@VajkSfElv zZsM`NYyH}PiYREgy8WNrAPv;ao;|%0Q5chJ>!*!a4}N3z;Bogt11@+|u6vw>hV>gg zKqn@BGn#9eKn2JNwI=(*Mxnl2cNlonAI%^0Pxva!x3l=M<9|QB9{3H;^ke#Yv!F7G zePZurWX_+)JIB)@5GT2Ld(E0u!RM#!0d+YzcbHw z3sQ-z{KW_Vqoux$^okjOgYSEI3jCBSTYO+pO};zo>j2BDW z7=3n0wySR~jzkUK$pFN97pMCxbUW9JWaq-(Z9na3$}QXhm{RpF*0q{_l_&O9_v@Vp zXO(`nDI!jMcF)1UmZup=?yD+~`F3faGh;(6oW|Ug;z687? zM=g0*wdWy%ggRDKdV;O|dGxzz0Bmd$6;O!eqFxbEwqPS|-&eea4CwffzRC3&b`z81W`MlT>x zl39*}ENiG8<#7Ns_)vk_MEXN zk2^*dkSFd!aS6im$k`_P8gg;AKHSL@bQY{74L8HEN}8kmr>BbClEySA;p8jkq_O)4 z?_@NBw4T3uZ|9vCv)%ynuP_blZI-U1AHx#eVZpf7R~sj2<=%kE2y-Xwv`~Thpgj#RWW`Hvg-=53Qy%kvmFR?_^}ZL{ z*JkfwJh9&E)3ASPx`#v@%784I0$D_Nt3wt|#ZP_VaqI37`ey?bnFxXW;)AoBd2wcB%oV7bN4tOTDsw z?I1~O@$iv6wSlXsAQuO}$!Pw#%!+I?Hpd;92PnZmYydK4az#NNWj1I`AQTp>)szC zPjZ0P-95+?uJJ9~>J!+m1+bj2ymb;y%AqE0ofXcZHsk^S9MMtdX60v`tPZD1ffIi3 z@~^fdb1qz~-(Hme6;WpOTa&&*1UOMEs>%FHm~vY!r7NtvmtzfJba+H3^i(;zj^v3A zQx6dlH?n(kXar(O_9&gl*iR;|m2{8lO7F`Q2ZQ^5?KH)~R<+Vci!L9A!G7Ny-nrhl zn4ZdLxARVVs^gvOe2)Mg>pjN&RpBG2sERedyA{tUgK_%k(n&pA-^6~jys;%fnDOr zYLx~$RnfUs2VG-TMTb|b=}HT<|bH)wO|$;LZ3LOONcuC1omdfr*2rel*zAFWET7xW7AAvP=`Jj*J{>;QCfkNDE@b1jJ#Q`@rOn4hd)^xy{wwKF z?N#JvneWB=X}Y%tcjwW$mdog<&^5FQyUzO=T#QFs^s~HQ$4z+Nr*Z$C7j6l5?=Qqz zTpROa0Kzepysk>%#bz6vmDV!ylNa5iPG4~-4O{-gDPSNu3$KwO+YMWIfhkdz7O#g3}W2lcG7B#u?gB30s9F%Ais z=pYdwe)p3y-qUf8xfh`}oXmiG{UMRQ1}hA+gjxjzo>@b7wjJPHFl9z!bh`9B%8EMB z7>S|oe`#=f(M*|wp)yq}Xsj1#vI97dbP0&0O>_9yXjp!(@#4JuW-`gudliR&wfA%M z3hUWH@6YL#TpPVG^zWyQhyy}HyGx(yI5@j%PvzjA>T_gix(6pWa`Y7(oEfn z?vkVV#2u%xl^lK#>klUi|7hJymJK9}yrv2?aMad;4YBHmN$4TtxU9?r|Mo9KJ!kGI zMk#e*U*#b7@zjT&M8e=>k0YG<#~ovfs&&0<4bI|XYsW1A1yLTpCNc)Y4u!iOrNUo) z7@^5ruAose1UK0%{oEXqfh*Jb((9l&owl(^fp{s$5yb0B_}xkY#M}L(J1z+hpa$<HolULyA8H<*8SpNpU^z7p-vr9_ZlKpq^y;GKL3m8U z)*dwv?wgjc-9>tv4SN%TZ3}*i9v!(E+%7cyIA#o<>1S~G^%g$Gi zMH*8k!PbQJsA0$mXA_qM6sZqA1yu$X(Oh?)`ViGe`)3c4E)EOQ>1olxvdt`z9!93f zhJah_D|yFo$J6mCc^91Wop8+h}-fmf=OuATjC*NQlJ>8yX)DUnnF&`j&+{9;x{^>Sh&65+@xWmas3di?AJ z@$g0g#TUe+!pMGRPbqByMUEO7J34M`R@~TZQygB0QH!eTv6pa;s+1-aDMY(o9wa96 zE~cg~3;)e5w*uubS!Mr3>*^=voIMOO(cgg)X=Kr;(XfM9Ltpc%@vh>XHh6g=fZxPZ zUN1d;kJL|Sk2dd1q=TNIr~f4N6Lv|PF9S-a?;?7-9I7TZ5Ak#>@5~_Mq|L{1#+8-zxS)>jTaw~x^61Fy05Jw$|agLD+_U_Rt|sDNGz zvGh1t#5)@fE)_C<3G;7|E46WBZZYpiy5W{N#4hL$Bo%+-6W=_fU#Po5!>uSwG>GIS zthbu^HwqpG3!mo!+yiqJ9tK;^sK?jLd`+#is7nXboJWLm*%3`RinbTzy$RvHPW6*oU%2!RRL#Z5FkCU}oND)L)6= zr@nX^_V?lBL#~;t!P7{GPs@2+{gw26rzOg@t2Onoc^nz~-TmZ{>|(tqnZI6_5LgYb zI$AOa??j@(SsVOjyMKxv!#fejy8F9$DHuFfGC<6oa>>e?b7p?9<4#%-#E@?S`srB=>hMdN}LB zk-(Bck|U{dU}m{&ale|m=cn*5#N8qvi5mN_&HOA_#~`MwTw7zQD^5niwu|2QLWX~n z-tglj8sF}@ryn)w};%y8g86B8jS2|9CBgncSuCijo@QvE0ef`6OZc(PznHLAHOwweXgs57W1 zCsykMjZSW7m6>k<1D{XXTI>+-$~GNt-SE>1$Bzb5G{D+a_-1t92}-EgXfDyjfEQ%a zo;WwyQ*s<}cu+s=+IgJqDm77Clo++kOk{vsZZ+IGt>3GD(?Ayp{#w1~1Zw>pU6&M1 z8?3U829`uZSrC;Ct9{2UC4=s(0NaE+b08*9_23@6BjcVog;F}A@SdCpv+1#o7v6Jb z=bdIliW4^k9_|r6Nwv;Pi|)+3Pz4(FbJ?KJ--ub+wNqo(&(Fo3LMScv5z@@M?n+!& zVN_klXj1bYnQ6~Ut9~a@s^~6Ya8Oi+9e__v0jgk@?8Cloczk2XN4)707ogwKbzlo* z>GO;}aJY#a5u3gDBG5V={tdXssnbbj*$rL@C#Tm>EBv=9c%K3&!p**G@Z`fe){VYp z4u7o=E1i1Z!(el?6~5p1drE*Ix2})ebo!uYZ^RZIo{A{0ik?&od=&hIf^!HCj-!C` z9mF2_0s1<@rVt=+IJk*-ZVolmQyZQRZWjU&f~tX*h~zaT^*4EUz>axz{6Bmf0vU6# z8;-e8A!gH9?0d-qf}nJyzwK4B&h;W)Y}*0rM}>(_yV~Jz(6DN4pl!STbWzK{z^fr29aJVYzh9{NYCm?^iId%pHId|G$3_Y6{rgA_>)0qVFQ`#*xSU4U($&(bJ)>e1 zrwonC85%VqHp=>3>EzfrwKP}w0G)xe;gB@_#2;j@|ExMLvF)dgZ zd-qX*z4OK1XDPs5{9@91nfD|;eZV_M(Pex0`9|{2Mj!UO7yB+JtrmOdi)nMkf<-Kz zp#6^at)nL>zKeZOsTcd6px`?c_$hdUf*=L(2437Zj)LhFEJP421(JQsp{Vw8D5`zf zYk)Bd_KnRL?c!)_4)epVKRALW8g#W0OiOoUu--Au-v}vxJw@Yc9Wy?QQBGX_hCezh zo9;lq-YiDbCo1+n_KmTm@MdD+Pja3l90;4>&vO~OG)gHJU)m{mx$bMdp}D4&?ZG{O z-<+5r2ir4|dD!^ky2j=P=Tv)5>&BYKsk~s$4YS!E3-f#gZ!8;Qd#2(sD*=cbTpK`< zJyVdy5|u@M)J;#GFeF(P%cFStrS=;tv6G}{pA->|)M~f0u{9EsUV^@$9u?0wHr{}* zal2bwX8wyr&4@2`wQMmmk2fM64Ru?Ff+%aL0X;Svxj`cn$?Bz%l}4tB54V`GM9L~> zQ%$q;hUyMcO=R2jl*+o+)|yRm3n53XYpa1mu8zXeCZ}76V~xN{5c5mlU~I%XN6BhO zX3>h`k`e2Q7dsYbieTN6s;Uh9AC;fKq-srN)yU!!`nBNIvBq8^-tj>1%NJM5cRbMh z$`z}sO7#3$^1XCvg%LBdxNPN$YJ287M@3Zz{*PR@boo-pItztV^T>52tLdFWdS{Q1 zV*i5eMGS9x!pFZbDdf{goH3B84Oa) z&8us*i9z2$gWl%AAP}!r8!-$Tm(;Yh(rmCfHRpXBV(J=ZzMM!aw!Ut?VX&$h@1+6A_d@D5@@y<>w_X?~r7vYiW04Gq<#@X(LO! znH^dR8YC*jqs!#qe}7B!+&Q4I&6s93hyKLUE@p?WW>+MHwQkN$1qvO_jjcmaskN<`7tWy9#Sh4|`y%53vy&t~cC z*`Wtm>v|Ub z;6;oV2dliG%FA6X517;y)z2?4E?Tz^U?0OYzEH5dt|i=Q(O~}#U`v?oys@oxwYAn& z)YY`t>0@98JG7DAd){!C7YRsqSF6K?kJHyRiNW8}F=wGpUm*}}4)Kv@mUcIB=f&q; ziyfcd)+wN)Tf+`*VE3FqAbfnyy|jt0QVQFC0>)2fvP*Hi9HZ61K$Eii8v$O(s$i+6FcMV0AvJ0YR{RYVTl;j)F4&BSv zoOg|@D(|ScT3hZXOQ!3z-+IDl3+>x!ta&WNlbgDdsv$LmM`* zyDy*(dL@b5VOF8Q$Mpg$5lB|LBxh@hGgrcujEA;Z!`5CvOGG8B8tUpAQ+CPa?9heH z$K^#vr)+2}YP>H|MP_qGQbp@7pfTZ!l8j)p8s&1xoZ1UuzN;$DiiR@+R$&BOXf@|C z20lPF7dGXW7YVtPs9#K$j0)~%`~v!=%K3m0&+!Ja2_3`h!k{OcA|~|fE_8xGBhWwF zosvEnw-+Wy_<6_?C#ya09*ABPM(E}9b>&81)synLrP1xFC*PHnXraRNBbuK6Rx@XV9ujkqo6~D(29Q?7D>2mBN``)g_k5PG{`EA4>Szc4w=uK9{uX3lvwfR?fXC8dJ%~R@T+Dx|-On zsSs^!bZx{}x|cS&>sl}1=M#cQv=i zuF;3Il}r(BjVN!99E1EIRE+g0PT?jwNXalp2(+_#DX=Z7X{u^)Z8bl5E=#e!S7wx? zD%nuev`I8GSx+!`N-%XB@3Ysmpj(R?@F+fzPFN&^S};4hfA#ghS)R>JRcm8iO^Zol zs?IgMGLE_}%|$gE8$`p~V~q?$d&Je)wxwxlYsr>o_qJ56a;EMr2&p!A-AYHriu(H2 zI>F~%u?DI*NVGM%E9%rZi#+3c$p2MnA(a;63+}Y~f(1^+DY^(*~R83?R zseodlx$7_YYt>bm|4>8fTqA0OrtG zRiV_`Sl5E@b&t#FNC^-uWD8&x17;=IJkckCv7!ZbS7(d6t)@|$yd99`g0;?Bpd+9g zVu&HcF4~9md2yXybGfs&wyw$OpVG#fO)2}RY`Co&3VU&5BS|wsxpw9nJ|H)=O^9<> zQ-x!tAW{NqIGuBcsBViJotv7rkm`0J6=E`7c!ggWZVM|g2$pWaUeMyETFH0BgiO^J zE%S`dj~4NBqqhOzQb!fg0GZCV4Hu{7bsaH0H&$he;Hv~XCEI0tr)JzX-0G$3U+ip= z8j=_@5OPnXD(_lc+g<|}Rx9m)WHJ^VSP;Fn0&6f>FU-O+o2jaF%r^$m^eS-BxeKCZ zUF6HUb#vWw@}0BuDV(1_o$XAGT0N#6DNUj}*}F03Ye?Mon$~2}%?S=+bbI8S-!Z)E zm%8h=2qWJGR{!a1|1^zHP-q^g?DPDy_7m5p_cCACg{^D?Ef<3b4|d$OiEp&K_3r~0bq zMyDJ6Z)II8%wD7i2)-PDCn;5pZ0{8rsi`Lyc9PM>u1=*gMQE`5Qln362W)?B6h_)) z$fXp5G5gO$RWp}Z=+uclnq)P_?wcfng!ObP3bdredRb}`VNqGr+&T=rhcWadtYKiD zTBmbKNpE(Z^Cr_QLIS>EuFg3OQ=fcYr@yXSHzzFQ=gfwbZy63Hpjs4@O(yetk#cHLZ<8eXV@8adOuP{y)F81;b7FdKi!kYSz!o%|uG-R!<$0X zgq`~6uc_P63bA@4i^_@(n@vB9n}=Ht61K9Y30kx;9kr*l!5I}2o0I0IWb@4#>nIba z0VL!%>BAO0KkgoRYFF;2avim}yPNu=r#C&tK&DJe^+tZ0`6ZJskv zpDPa8J)dqSnJYC@W>aOtz>|PCzigSoi8{ zU(un)z&)w(@`TqlUtmL8jT5laYl^6JkH24M0k5jYe=!Hq^UNX z-JX)7$i4!m5Yr|ft88(HnS2M_8QId*5IdS{nreq3LQ;z(Ie&&DL1H|C;?S_vwXsd# z>`FPS*vgcx41*g6dSIN+VX$EV)6l7saWU60x}g-_GsA(!9!=TSvN|`dDafl07MRMK z7I4RuG>loQ!+bFW1}%J>=f+(-V`7XNhHglhncEC4a}71XWJWTPrLzdk9wkCn-WgCY>x$0I}^ctPb8(cLlwRFg!#w~Xy3U1Ps zrD#QA&RloRrqUHFD;GOT97WUFohd8PnTxTOXz3U#XJr0if||PLsLsv9U0P`>uOiDiHS!x&bZ2RstrrCZ3Ipiue26gnDO1=;~(F#Shvg; zMEebxc4S>OCC^hChb96-ZI6lkbPsWy|1!4c&=_NiWfPY+ZEO+4ucomId*>Td%8-r(l0EgZgHAXY z8oNGl-g1XKVxZ} zCTv>TT3bHd0A=+ln_uCosUL!q(;o~lsMc#>}f3lRX4T3Q%FZJ zE=P#3NI@_y&)A-_3q>`CVs)WxC$1(pjL9P^C%RF^$s8nyVJ7(W>lsH%Vy&#(g27VS z;@T4B{FZEP4#VEHvJO^Jq2?$gV@RE}oFuCOCJ^wj++I|4gX(uHsJ8ZmU@uZGS;d7l?nLkjYzH^*C9Io?SNSfl!nz11)o2EnNhX0yvWa$qS zY*hhYryOnow;>k{*G-Lp@{}Y75?vyj?k?#w4r~LQyPAC^+EV^bgd?Jr=(KZnq#p7lAme-bWi0njA~ghd7&_a zOr@7<=pmtl7GYr{u}2_dyH*x5W4yX3Q&UgDOCj9&18l8gs3acLZRzjXDivRnjM(Of z45UO1@Z}Rby*^|&Q-(>bO_gP>!2b;(;q}*kM&JsBaF;?ZN2UrHxJV8lf z`jJy~A*Ctl006dh46Y;T6P6CJq;i;5N`9&bEZhVBlTq@kmRl6EwwK)7`%;iXB|;$+ zDVvE~PdX@2qY7RgF{dVaRdrhpi+r~q8bcuh6MPUQ)h;KQDW_7fzfbA7CUZ;0af%mJ zu3fU$re&BfT(lA@lT>P1>dnz^yD&2Mo@Om4Da9zaM!Ppgg{-SP`o#34QOe7jW;#Pk zIv^~(O0yRVIeA%BrD8iCC9hH)M!;+-JYTn9J~Vq(jA)a!?{7s{mk^YN_EUDa+ajK~ zOqIghE>AGx1-0T5#cJX}%-ZEPp!Dc)@&SQfE>$_bqg78tkp{;uEXJG zYh8aBs_O03r50nSQj4q!h69@e*yNhrnQWQu#PTGADOvmB9WOrTl_SPuI-Tath&q); z7}tgZu+w&Q)An>BEQqOOMO8Xq(SuHg0d`{{Ni*417j>fx=R#skRn*Cl6pDV3636;7lOrAy2@ zKm5=znn}sm+4k=;u1amjiIDep#T1bGHrho={b2`tke#2&e*q*PPrzlAgi|yv~3OaDyNOo4_Ii~MbIjX9#O^ItcNPJXbT5> zGhq!VWjA;O{MD4?4=H9?TR&XJ`og3ZZb0M$8J9Cl0d{i!6E3|MP|gou@J1{)&hn!| z5m+EpUIOhseIi~=finaXq)4eTlSHZ>Tk>w~u^lE+~|nLgDQa}x5XLEpUP~L!)kJ|0*UP`G^(V> zv6WrW!K-4dAaX?^tcFT!AWm%+oPiihR~Mgr1X;-!h(eSUE^iSkO_d!DGDRCcfJv~} zn-SnRi>enE_)8^>RNml3%T?`kf$l&oODduOpLLaBxI%;!faXxpT}xmaP@1<^^wEr$ z<=a+Mhs$sEhx!72v|)o?q`=;len@X}uE`0l+LT-9u41O@?mE(5$TC%kLE^87UMsz{ zWhRw=A7XP>9+fEtd&dx1Mbdn~V1q5P#56fM+_}Smau*jiWu*>>YR_fTI%d~e z_72)zn1YLEvsy+fd zSPmqo=WJYn31hEfZjgc=+&@)9)jfehxMWZyqhuUwVyts+_X)Xp&Z^k!CIl9dHvK{{ zO-VsWdqoOR3U@@!b93lbIjPxjq#LR1C>OC&X`V{)FqM9WqGB#WVO|{jkz#CxZh`b0 zvcfraF1fh~7lv}F-Dc@noJj(Fwjxp#ER97;7nWs}bD>;GL!Z{bpub!0N5LBSiy{@0 z-PM z^CFc>VbvB4DTUNPO*(2wvV=HBl-Y@vq;|=o%FxPW?O?rL&Tql`(IvT{##z_(f5B=|CHqXP(S+c2+4? z9`Z&>&vCxYaR)oQ*3yU5p*6K~4Rsnjcq9E|Gt$zu$ZfBKfx!UkXywLFKb>t;OB-$> zq^t=DJs-Hf(?p2U>Kyxroa$dFE3?YufJ@nBRExF8S*iw-_033d_&m;g7R^Zhc>95+hbs#nth48oDuS$R4ipA6^EvHki z3w6a9(>X7Qint+k0?|2t2_0 zt@X0E#@msVe4#zJxc9YZHJW!EGXl zViOclp|FfKLQQu&3kV&^_LO+_f`HnQ9rFPU1;UHyUrr1{C)A|Z-m2_o;YjY*uH%a4 zxxl?C(4FDHvF*u08K6+H?xl*A)+>2VsmV6zW~Ndi|Pu1ZRLT%dM!to(=p0@(*?^MzHe|8^kbKhHsqr$D#A(v zl85n;Rdio;^ObAENZ%q9B!5n2x%I(*L>*q)FTxoMSBW4UO5~akTNqsfgfP!x>p56kdBm+>GI44plZ^H0srj) zJI@`cZT*u_=ej4910<7WF2WX$5Qi?!F4l}1AYE%(&!(i5n1lc`G^?BpncZt=kj#QF z*bn!q)OcvnLgp-hqNViF#ulnE+((5r!vY4$(t0WEyQIuQz{in1kiu4a%cE7mTw(`v zklKw!>Gcr0jSfWQ9PnFtld27D&Iv6|3&~p}6lKZkW#`URj{pYC94))AFbv7<*4ZeP zlB}Vp14PAgyvoFqlyr^VL3mEdpo=2He$GBexpqQYsoMhBYSZ1(XH90dr?lv7mp)E) z8*@V$Yj)9uL`^8Hw*}a(-azeCRy#;1&gHB(64LGwasr}}bzFqf_Lc}2q0HH7cM%|@ zbY+l1tKe4BnvQLGb&Hv@LrM{H#@PN!RMos~N=@jr$yrnFw`7#aSy!6O=oMjMw0b&| zPTF-NXVd^Bh*YebnE^H_mnPK}VJVoN$!1GY)@6;d<=B3ie4_`(k@PAHe%`K)3;&U4 z?5vRfvPUy1P)fxmbBl=Y;V-jM8ME%tzltpUDZvww5?S&Rd0a(WjE$8<*za|aG+#=`X-I*KN(qeZe>V7qm| zANAOv!+K`+uuP3kkWs`S6;b*D+WwOUHKoUr{gCa1nLMw^Iqe2uNFPk9LUA-M2!l{z z(DArVYjlY=Z+hg&x@M>)?U>v)B5Y)bhWRV$D? zoqu$(1z6qeUVQ;dl+U(KFYnPQqbC6a*%{L9}veFFQr5Gq*m+Nd!-X?vZ8WfQf8#sMwwX) z=o-=bVp)j&{>o={aO;|W$(jABMrRrEp7vy?5YFaQ+NG_lDj*&zkwqJGTC zb%mlV@--LrqlJQIWS*TxAnogl;HZnr%hTd<6e}IAD~hyMQHP8ogt5I*N^IX2vpqA` zEcrYY22B*ykc?y_X-omtXhO77oVb%*Pn~5)PCgq%vf?W`JN><3>r_CuF0l^{l)Q8{ z5bO*}T@vIR&6!N%6I_KV*xD;*9V>VU;7w`n|gSPHo; z+R&0Hi?h(WEjgzUFF3g@+F(+;rlAKeOeuE3$K{wDAX9bj&a`r@gYOfJ<{_s_q7$m^ z5O+ATIou&c2&hccDM)td?XYVV4sDhc0W!P}G+DZmT>Pzy_c9yTpEG6Fgwp}tiaV_e!_68VA}}O0;OsJX4WOPT`(&ad6s0_Wapm?@xy9vjw+#l?1YwB;TTqPq?Xz%iH{+1 zEM3sWxhK2`fs5RWwOw1WWx*2U;U3|d+`WvIdCCxerTO&XB0&g?u4}=yB7CM;YDC2M zn{NhvTC!v~mc+5pjP=4G7NYc{UDDrK*LMV7jh^ z(Rvb6`Ta0W%SE1*VZRO*3Mr5?0cJbM<~ESWP@uDqji-o0*4r|O%kmNeFe}5C&6BKW z6P*f((kCLRtdF=IFFa8NpTH&{rG!#n$HuLLQzq={xGaTpq)ahUO!*J3M7(ByM~_2^ zek|wrM~j^KZJl&b+i){Z-V<{tYkvXghlN^pViXTnXiJ@?BO73+XQmLoM2?Q|+|1W5 zPQ6KQ33>iPE%v+ic7&~9n*-SuS>wM2Hd}0alP5Y3BwbJJs*>kPdXi(g9fq= zmZ_I2k)1rn&BYoW>RY+C&T`6@%M^mZfCLEAO4=p6V(Zqnuc?oXHkNVySz|fZUomRA{%xb4>pwMC zbNy(3Yu_+kr;2f6(`o7xPF~EgX`Cuv$=k;Ifv_)n3Y`rusM(GmzxW?UTaoyeTBK0 z>nqJ=T(2{ibG^Z=<$9x8&-GR2YOb#~o4J04*~;}brjP4anj5&@W?sW}pLspk+s!Vn zb8`#V*PGp3-(+t0#fr^Oa{b5V7cfxfw|udM=HL5b7nVUsie5N##aYAT?q{5N(dDeR$-cJ%F zMKVQJPA_`~ZEnV_|2=+N*y!kK$WX`L9i7f5v!Vl2Kzi_3R8;f`8)A>LT^PslMN>Uf zJX4DvV`D&zR#EAHwZG{ubZnU@p&-gty24Vs5$!rpP@-l%Y^gfkBdf3=rDSuiWT*Sup$_*-hdrV4MNv}|&NGdugsHm5yXfaVy zFHuo1QBf~ZQ7=)^-8rb}&mAiI3x|rnmP$o^u}MTlA0jIHFj3KcL`6SORPH3 zxGy%rc)}Mu)fn-`CL16lMaC~My^KHL%A^*V)EKjpzPK+o+q}vbn`vI>i=A%X;ESDM zZuZ5_G;i|7=9oRcSfv^A#m+Sce6hLat-jcK=AFLSS>{K5v9ryO`C{|UkNaY?%uo1Y zrw>PiVOWwjxa!f%D>2D@}U$Q2keDcBf2R-mX z5B#s_fqnNC6F)YV7P(Sd-^S9?HkRngc{!FgAs~#)DUp1MN+$Y}!8_ zr+mQmlK)Qmpa}AT_Lmcq4;pRpU{!(A!9XF>K`+<-$qe%vbC54KZ|6%)jW0EK@(-E! z@MY#6ezAEkzr=iiUur(cmzxjs%giDEVKd4vHy`J<<`aB{Il@<(Pw_hQ0IxTn@oONKTpORluZz#%o8q(i_3=5pBVNgGh|lAl@ddmqUd8?K z#e8#o8NV^UoNtLkAri0WH^o=;t?_2w9dG5^;y&I3ULY91hWEy==Qqc@cqqPw_r<$; zINr>b4`m6aZQg$ zx%Scc<6PSje}Zc}<0D+#6@Q9rAB!L0+THPIxpq%H#;xBP+Py8^~ z_Qqf3+9%?#aqW}wH@S9i{4K8C7dN zzZjKVGeGY(6ZBq-Lm{Lkpb*mDhC)dD4MZ93|A5|WM{VPNn?YB?}Y5xX= zkaiUGUi%$H8SVEV_}agN;A`(fl+pf!2>yE@_}U*J%4o-+5Yqk#QAYa{L>cYR5M{LY zA=a^+&uOx-gTv7;~%Z8d+4E(SqozNb?6jh2f47PNVPtRhp`1{HQ zH(yH9!&pcUi`dX^78}P{d*q!R2M<2qaBw;BXK%y7)|>tuX()}Rs8! zcQ?Er!T$}uU2lXZM5HLRGZvmo4||(;y%)YTa!8cCGuB^)hdTy+3<+@&vpWpWn(b$H zE#(lji<}#+pX;vPNc~mGqSLrO7@bdFbzEN%T~A+|e6fj9zb`f}+U<*tXG0rVtc5hX(W$S+#Ia>3IRUzx=P~+;EhZ>uT4mH-69BQnZcBrv>{GrBqlOl(9z7Z}L z*}6U{B()s}%OZ!ayBbxP%A(`B{(5ve*Y`xvpn_L${g-U0gT*FNnUU8bb)_P!g+wnr z>3M``l&r_i`+fmyyGf z=Qc)`jjr#uP&V?~Gn6j!9FYG?pZ=H8(a|&eSK5O988tB}>OTYcqp~*-N9yXu)sea4 zC+Dm2NEG?G%urWPnnhWa@~0a{y|KN;wR@-hfXe+AAs7muG1}IOeCK7jPsMMLgqm3Umfd*VHColRd(PhWx2#h3HoPTa9VYsKuCC8| zvJ7uT8kw-g_`~aw_P74amWf)9K6wrX_p=Alses>l z6H9|B=j7g&qgP2tbNZj~LLBo*Es1hA#>Y-@@p< zm=4iLF$tny;QH61PjmgYXpHNhh#uzpC!=rB40w;;{uytFJ-8hPvEDyCi>?-P{odh~ zcsmS@=+}l@=ot#yKYSxyVd6YC`~W}=6Zl~QKm28ShBEdKKTlV`qAzeWpWI)BB>TrA z$^MCWwtpIu?8p54`2KnH0<`gK`#~FT+uuaDYv?w}_3y=RM`QNyT)q9BjR;(O5TS198>anJXTLhloM&ahDH<< zH1Bk@N8Q_C>~46+LG242)KgBr)Cv6N?gk_E{?!wNN%ha1(5q7>NwSPWCbR>~nb1us zuu>-U7^l_8MKH4A*XD*l?nL1K*H7pl6Y9B5=rJC*2|bx6bVN?*&trx}AHxiZK1p-= z2bfOLx9FKk&yLV7X59YhU$}nXF!-MPhfkwfh`Id4@Wn_nd^z2&ra1~bz+=N9u77G+ zqqn>0ZG@g-R_`AkrmJt$*DC~y${40HhN%pi)%*94!PR~u75hPi?%!XFB>NkXWd9ZP z%tuMCL&f)RrWaf31=?}j{=4aR58Z-Zd@ueC%t8CVPm}xwG0A-_2E$>Sv)XF6t)A6i ze5h{qOzpO+>2y^!O}nkhOIJ-r+HJLC>8iFwyRCWxT~&|QZktz5S71z^BkXP_KD2~I zOSt|38@iswV2S|a`8yO*w7Vp-vUqo6F}K!AiwPC^im}XHBtewoa&qL*w=jV08Tt|v z_{b@EM2t$3Td{fGDMXvW`F)?*-Kr=$qlyh}X0d6qX?2pJ1k|(T6uh4pt}Pz0GGf87 z-dnY)lf0%(O)GF_YWZHKrpBJhqM){e(esGY1$ligx`b|F{@G5;#B*%u3>KTt7}%OW zMUF;(5os)qXpa+if{b-!cic@2MOS*?wi$b3 zoAv>cL&7z>dDZ4+EMWmzD7@1a(4+bV7k5#y2u=txE8lhn#SLiO9)tlbe{z+D_H`Su5#j-IemEKw!X^@pZ=Kah1NQ12LQBL(s&3>dD zPkxZn7dsBlxfZ+waAOCH`ni5EiWPP+x|aycr@4N6^ov}tj((jG4AK(d1zfWE6RR^KsmXkuRZ-^l$XQr1%KA}I^>``>c#^tiP+d1(C5Ceqivsl*k@6rKJ)3Ac9EPTo{;4K< zEj{bx`t!zZT)!g`j3%A^H-JP zk;wMz7hkJeyyM`zZ!E8T=#5p-5s9naVVS|Y6|i2vdig_W2I(VTuUjr2?OrwIrmWW7 z#GVn2vzC`fz!&w`6@q zA2uhSO1i4--agEl5UG7gtJL!8)Q8RlK_6BxFAJA1pO8jB@p#_y3Op`P zqoR1+w7eLPz4X|j(Rf_DycCbe(&Hqhw#lbpTyRhVThE`;f0uZ z!@YC`mh*+-`?>y^;fH83dYpvXui?tX1ifPzV(?x2F|RM!e-YsA|1fgwuLr#SZGg9b z6X5NKt|+=c1bF-36N|(-EOvG}c@>;dO82Hw;|VNwj)mBgNPFPD#D>l(!ndHi2Z;{s zZLX|Lqr8}-m64Y$By_kRBD`+_|E&eHeCI!6Ae}|CeCJ`@W)OhLt71}tUm65S^E_W) zSte)<7$KrE#9OpxP#O?{*{@%{_#yeboajYm2ECZKyKWgKAn1=+gY0@9PEAG&&Tkf> zwyre-?X4|6a)#@B(Br0JYnd^QhwtUUJ%{r#foIlkD`=mJ`Fz`cJe3V~`dO@(L_i2Z``C7Kye z(#-e~o*A#u?d!A<{VT+>=rJT7o=7s>47yrH6JQ-(f#-=1-w6QbXK5;Y9sq{FiD%|_ z=oW0+ox?we@@Dv-C^2yV6Z@ecTFi#7@x-saQp0<(8> zA z428 z9H>d!ovUZ|m%@HD>+7B)6|irW;mIs8U$7{B-6ML@C0}g`<*OZ=%m;xV`D#n@=W8mW zd`;eDKBx?kuc_$kp2(d75steJRnrJ&)$}AxC?td>EPq|*O`?4B%9Ht^pg_KPlk(@Q z9#8qICnWPh#esa)`M%29s2Zg1ZzJQ!$4ReooAC>J@h_AP6!fL&zd_|_`~}a*?y-Hig6sbro7Cp;n(BpB?4DA@TgR-Z#h%&OxE!A&`@-N2H{tah zye>_9?d<&Nvy#lpO43g8(5<&qv))R;sNA1RvEi+wH?UCdd$V(hH*z=S!uBQ+$UF^J zXE$0X;>X%KCW|ugTn2{7Ol^llu%4&Fw{}`vZz>25N~Y*;)}wOk5w<$ZC!cwgu`!!` z4I6yLb!+Pz#%!uz;agEGzBa9FYxCkWwz6_%+s2l*lKKYvEyb;GV{3!B#|7Q5S_LGwORoGS9TD-~E+~&onWK+|cCf}w~`lgagHZ^RZd-|q( z_TX6dMK~G(qvTaF@Q|*GSqXgq<+I69{^P2tgiVF6TwYCwCi-(F?9|bsQ7;==lzC2# zM66ouU9he?txdlc#ue4Gk!9FxPmvfFFS4*Ej19DXwOLthAPqhJ+n7iHtcQ)9n;FDE zD-#la0}1CNgoKFpaBLY~*YE^vPhEaE z7gqz$=a~Y|BIdc2ja-p=Doa)~omd$N2|!UlY;>>n$(SS7J@c$)BbQPv-PHZXS-BmJyb(LWR#(5~}w^$!lax3-KsQ8xu3=Go3hS~8DC z$*OzW?jagbx5&iIQ(g$p6&gXp)1DW}7gqtvG3n;Bb8~i_*fbxw)j5?K!#tLaTu9NG za-bodN)tfz)3o%ZY|lPcpPpXyM$S$3dAkPj&r(Yj>sD=3-|E1Pi2Hyg2FK~7L^-9% zH95#mrcW+b&2d;Y*mP2~vOQ&91UOACB{O}kh}nwOS;QF#vNfvYU0wD$w07i?tV5Y) zd4$wK<#|hpT9W~9a7mSgnse*7BkBuckc2`V+bi>(i!&EzY59~jr;{o~@D4f-*^UsG z?|5y*C`V|?{M{$czp&0qm4P>D&=Dten=pwBN)8gvf64d!blPHIT2i_D8??R~)> zcA2U;D~zfg4~-6rT!FJCsJ1w^cDX8hPhcBD&dSvNf$crLqLsY^l|e+BQ614<*U{bC z-;Ml|;sJB)?ng(dPQ6UkJ7u3z zDfvc1d3I>s7JuhfE95p^ri2$tKYYn&4Jpl*%+uVr_kwrv2~Z+GL$Le&&87nloi3(E z1PjaK^Ka{|>*(Af#&>|VsLBa;_iyWI>TB568x|o*^7U09a}~`ZK@=scKkQ%UYfhdO z6lCqnO5i}|X8(Y{yFMVI^jjfn^3?@l)Y=LGYegs)ArKKSU5O5e&6a;)Fww$(;6ukY z5dkQa_s|s;$*vR7usvT+2E?k&TR7NcM5jZ5yIzVG;+G+aob}%c_e7`gg)m1>@eV2h zB4ph*z@%f7?J(Xh$0oTQnV9n&7IsykC9e)3+F6f1J`|l?IYz5a95zBw)BF@YH^730 z0}9(m(=_|mQ3c(4BM``rY9&f1xdG1`>#fb3R+_$Pl`9?C3IiH~svO&^nl)JBUFF4;Qu+INA*oWmHrxcz=v7F+qaE$6H zE4!NVolIBf?nyXkOiLc#&2WOgWrojflVfj7=j zujDSit+zW62E!xI_#-|*(B;9LXD+EqWP8r^s(Y_MA!Mt%_el0Y5Y5i5swu2o1XmYU zm|rlZ`MQDKR2U>Hwq-t0A!V%*fH{q$$P^TTVAk>D>UtM|dJ zI%*MU{VHoRPi>QgWRu0tZ`y=2C@TY1mGoU*g;hRONKc@^`Ww3cYGtbARnhw>GkJU_ ztt7wp;=~g5s3D&a(*l^D+vSWAV>k$1P$5ILtrrK(sisA^I*_U2&P64JKt>uB8MjlC zDg^0QwL<2j!;n%B*;47?&}||kkvSQFJ>Mh=WU4E3G=LN(2w^T3deD6Ps4a_A7RTup zK&uIgjBh48V7;;f92VmiN0LyDNB~N?7nFdhDr*P)@>ICYK&~e@lN?FS(VSCZLP6uw zb&JxiQ9fI2MoWHIP)jI-=$Fiv4AvwCDPd|&hM=ylZb~BL+(9Sqce=#Y-#7Q5& z3Ns$^Hf_0_8ka`p1(|D+QDGDcP#C-5cyCR5?>l;At9N`@JLNMp!DcEg{-12M!C+0g z^*ISZ^0OVGt>Vnq&mTv9#i=RQu};!1mO-1GLS*jvF^pQ;Qws*J&FpRi%(eMx=ns2gLfTf)=h0EwvTR^7QOX zJDa4Cw3bsXg)Ey?A1#IUNUbTo!oNLvnoy{}FLZJU$~G&TPp4SZGDc#qstbA@M%l4< zgC+BKY-8%RzXc99IN6+Z>9UyPHhqEjtWq{7^QguGbEF)Qsn&YY2-gLB0|jVl*&gmy zE21ob+Y6Ei3$PmqcquGHb^79MvkGcCngC@m3NEm|svH@Dwb~E2ys!;1sj$q;Z=T!~ zaN{MudmI%OXN(pwI47xH4&~%}+!|2W=+Q!_R?4gSG#Wj?DxedvYIG?3R`4K6m*zYa zAvYt1g=vlVw!(@Lib60}c?!vge=ZeJcfV*O^&1tO&q1;PHNnZtGiteDoqrpUr7;xT zmgfAHuWy)I--R;=VN71z++Z&-=+87MC?sy%e5+a->YLhIf|;)MIkVwjog(?g__0vkMg=I>*@NL*AP$#!EbR5k4;v^eVQ#m*H>8@s)vX>BakL~jm}HsxurIHdvC3DsJ}6GDPNK~~0XlDS7Ntd>vF9XBT<1P{ zQl;oLR-{W7@fQ2m+fxazrAuhlcgB&Rg>CFZ7ysMQi=@RJ(-6}&H`bSCD;a685gg-~u zYEpMfDwVCubuGcp@)PHT7-p-#2x{z=u(VV7iDVdDR?Oa zX%DcqYPi6Ix-I=ZTcs|Bdd^j<0zj1wU+kF@wkgTvWtukWjQcc7kmWsqoQ&rYD$HDy5Q?~iSdlEY4n@l$!E-gEx z2^5wgq)%8nz>>-#Qz`kW z9q!R%YE;3?Bj(gZuc~hHm|SQKg$PXW zL6lUxoMfh)O2Pg2!*PmlaUR$;+ZD72EMBd6nug0%l9$ z`8amWhi0#e5p9AeLkMR^W5cBol!f+_riJ?=p0~_a1?Jf02}Zo2R$Njj#H?Lz14@q$ zr#8W!9$2#qsZY4NH*@jNU_r8%C4##m9L7#MVGGOH-k!CF+qJ?W?45;IEEo(bH%i%% zPee0vfnpp;?{2fBDlWFABhVv_cvASZRxg#f4u_kqb^X}e-n*T;)MANLYLPX;a9}fz z!wx5RCR?^_N9j*n4TC+f_c&gB&MQZZ$8KAHOzTwMYnE`a1G%UD4CIMLI+d z6i|;oIL*tDEn6tFau8G|COK7|bt)eshO0>-aE8*b3O%rJ6{rhhkGrbt;VYo>v9I7yZq`E;TC|0Oy_v8El*=Ee zNPlPeucIV?NHN3O`r&ET7bdlE10ol6$`Q^~fc=*kj|0m2;S1h~#l~5FR44)qgvv{x zy{FILZ86ivCz8++s~4|PxwnTK`?ti-hHvY1L0I>}s8Wo8F0 z8Bi)GWiH`Io9f>-p!D^U8RdE`j1nmkOa@f~Np6ELm_L=-CWqDJVg(Z0S!ni?9>-R8 zK?kpjv4Y4Ig|Hebfqs1`5Qa?)8fgowTvqLT=%(ZgL?KEFm$wL&rV2sU1frx#NNmqy zZ$_A@Eb%cVQk%W`-{FcJK}RW!RNl}P76#D);>Jm z1hxUCd8^PsVHYW|ccmZFo1AOZm;t3OrqEr*%I>Zs?S(8;g%~9M`ht!ES7uV#5+F8b ziHrOJLs!hK{Op}wtojVLDcX444SL$%6ED}lUm|UrBeFWR{L$69l z*Vs=Rd%C+jdhK8m?iE)~2?Cbf^O%| zB&X+WT!0B?fKuvC$W06qyjs_v z7pYVVtF~ZBDWnE!(osW_C0N2JvlA^z?UF^wZTu}1rizu73(sn75xf<|tD{@!6lj7J zDpNRqWn$S3W=0au8ejzJ98WE=5?oKDu)t=^ z@SwJ%zvDE6o(>|en^j`DEO9P7TPDvN;@8epAv2xnl#vk^m`HxG+bQD|nY@*3wC(|f zYXwP=l^4{=Ql4Yxre#73E97wPioWeVoo$_PN9wnTcW;f|UWfA~BaKLiY1H2xW_9`C zk^JbhmO0xC&w>2m>43Cc^7AK{XKy%P15A2Vo~q%VzaD zTB8>P(2n<)4;aMOMf5M{P@-8i=~b{Qx|uYG^Suk)s{w5Yrs4Kv7YS#ewC1GDB`dX@ zf2nz3yi(<{olx$|lQh`5b)CPryQ9-zk4rf{^7TqDg8#Xc8?LUtIQ^aI6Aa2i_X>cE zm4!-6oy4*{z2~I2=kNgS*>R~+Ev1%JPdMesy|Y3slqW~Me<09_{Wq}04~hjd)Vp-` z`Ze{i7}mNMMV1NS{VIFU=C>RwMeh-~GtZn}=fAlhxFI@C_5*}Sw|cfwENH83ePG1u zdjpgwz*gqT<40UVWd0M~&e=QP)HZ5Zi$D}p5EDD%_@`)}xmHBrQY6*gv?Ef08C zM}%QIJON5YPtF2sNp-7!Fl3^&+Gk&My~P_%Woq@V#<9|U3y&mir&ZD#H=4>)k8EN30E6<#Eu|b?M9d&;cQRyltqoeP zNWNe{{6SI!JVC9Jvj7VJ&__lHY8d?Cgm`8F1FLMk6b37j&Jn?GBoCypmELmE6EK(9 zyVIq(wkW+GLRjnQB)35;Z&FsnZiCQLTg@nQ3&}rOz3kkX@*ZGdS{^IP95K7UFcir> zq}gbdk}NGSekP`*q-*RB!W~2gwi4WY&VEO^c0yXL+DNb5?Ret4p@TJRh+v`wia~76 zO1Js|ZBtpXU>4Q4D~^QZ>P{XDG-{4NgTvF?Z?k4z3WSs{3Nna5Ia`jB)^uzWrMM|V zGI`6CBIInaohzuSdE1nl&}oyij@fUs2#;MLO;q%Xa5izFe|A#xoC|=%OvR?mNt(h7 zN%jX(o`Y~6vQ>$h^dcoaYLdz^ohwAC8nJ-LLxAkFQiNW=%^#w5zt546J|g9#)gI`W zFe-Vn5OI>W0sE8>gw;yPl6=QLUI;A979~VNi^inE6IJQ5Qq}vz9XLu!@?~}A_SyE2TC!E=0>KfUvufYJ7=>U`zfSipQV9wzRrDS=u@_`*A1-Oc$Mg;lWSTpf}S60p&L30U}2z zNn>$fRA%d-jKTRWqEgVIWWLRsm7*4l^3C;$f*gEkB_dY&J9?Zcah;qJQI9hvd@>Yg zZNts|9o+)mvYN-{*fp{f3&M_VPS(OZz)sIhA+)IuL$NILb;14WPz1YZ7lUY4I+wvd zBeN#(DemnETLDez@F1)6?ecQ+{8=5c#(xWRByd2)vh{QX?S;0~YFDjDE2RN};sdvKCR<&bvyrf@*>0@Xkg-b~cumO-H`(cWtLrbdh7Ob!+?SirX2T?GT9&@X~LcL;*S|UX*ppZGz%vTqng4qC#e86_S$d zj2ysHUQbJ@amwS~bO<)G|#fpr%_+mrPcw!jM4c1uLKQwq<_}phbpo5-Ut?QHfK@I4S3Mgq@ zdkg%ttS7)NHnvgJypJ9gv+v=%p1nKz2mF476|oC}HY;&IVEua8I2)<@mLd%x%2|X4 z;jfex5ls9Qqvtml;Q{`NSjh^j*dk`-l1yK|Tpm`^7VHmo`ilUdiQupq#C^jxq7yO! znd6*e{#@XQOdd2*q9FmgTs)hY#{9bE7IpWq(iN5vL)qEBgJLLgzN{}+Y)qkZUT4tP z9QvBa^>uXO>m~!|%U*9R=6Z**jO#ZT%emfZ;IQW|qn_)2V>Q<|8_it5(P-uR7Q@H& zD~%0YUu#^$^;YA0U+ffP8`mq0PZQ96^fgRhU#73;e6exHYgBlglKc^g%}Ka5XLEgx zd66$R$y~$ra+A}uF0Nl^4*Fsh<{qwp#C(9BJ<9b{*wC2b81R8mVr+6mZInEPXQ!ZFN=1J}tvqaE8r6|+M5*?2ZPA4$y2hAbl61a^9kY)M*~nGellv^bTq zQ=NPc(acOtry>)6Ok@+&7Ld$zw0O~KH;GM3r@EO5=|E)+C6>?WPC};|g4$)~7;ojM zV5ev2NEym0tmylI3`+2h{F#QqEMne^kRLzo^5+iuGc138B7ZPRL`q;eQu^i3UitI1 z{CQpeQ~<$|WVQV1mp}XD&msA9O#I+0?GM@MPovN1XgTTaSB1#4HfNhf%6)fxXk{uk z%89m21_?1P0`)Lze6b0}hkUVeqn&Oy((Sc$dk5X>bi0dghw1i=E4uwB z+{SUbcX=GtcS0Q0w>%DlS{`qv+g7^mr`zpxdk5X_quWR57G$(Meu!>=NVh+x+hcV5 zK5i4}i1GyLSDd(nzE;rJYWiABUqnz7=!4=!7kzD|uMmBqpyGr^Uw6~jC+G`JC{8>~ zUysq(-_h6C=<5J|eIG?84*6o^6YtQ~QD3Z#4b?GFWTFi;c5=cy>l>arf9azM_b zoCb~|FE?iTViVbrr#Ob$Ws`Lq1QkMmQ)uSlZzg_U604oO|KR(B9{8XK{+IN?zJX$* z@MDN!Eg@cY4DqUCh*vGk!K+>kURC=o@v2ujylPb%ud2O6yy_J2suvp9bM4;@FqxB$ zEnNGa(ap7kMlaXTHNsqPGX}ZtGj8X4yPz5eMa{W>x#`Uv}gIqty zc!BG4jhDE-+&Ikj%ZyjK{$b-auAgVT$@R;Px42$w7+haryv_BM2B_`%#!;@<8}D+x z!8pdXgz-Mt{>5ZmZ#0X!evw(q^;KpW*Q?D6t}imDaJ|Ny!SyA?UQadWaDAFt$@Q7$ zJU-uC!1Y;X6<=U3=K5@N8P`uYmvj9LvzF^;n)O_tW3J}>0si| zFz@AOnh)?f=7an!^I?9rImFK~qrB35oX<6%;OCkn{5VB&o`gt7nm`=&^*X5 zGGE|T=1aWVJj@rFuksr6HNMzY2*T-k`hWH%b7_a23;`8|G_yXP(ui_txFXmUo zm+|KKa^4cJgd@Yvf)_60>zG9#eKXjzJdGV*YNiE^_<7M`1<%3z9HVtH^zJU zRq-&tIzGs+iQmqzjcfe6_)fklehd5OySy& z#0;+OOw8umuEZR!eJoMQwYwAZxOPus0oQgXs<`&?#A2>R63e)@CxLmjH&M&APbBKO z_Q}L*uHBnx=GuLUR<7Nj@Nw-^i49zPAaM=XKAphC`b?sWYoASEVm+9^#QK{AP~&rn zFxMVR407$^#O++$m(aNO`NU4H{cYkNt_>yjaP5)Ay9OT+p6EARWB=Hj0 zzLq%5wI>s=a_#Gh*SPjn;!Uo7Bk>m3o=zBC`)1;8t{ngyqkSuJlxxo<-sRf26UVsr zEE~Gi6C3kBiBuo_x%9wAp6QvcsC6N5?fwwkw+W0n*Z$rp=Gu3RQm%d1DC1hpsNmZ7 zjVWAv&X~cq9~iT__PjBNYcCj;Tze5jUHc)3y7nU|AhegDfYAN{3JC4TMAUx*qOKhV zQP*CE0z!KQL|yx-(aN<~p@7hS2BNP097J9F1yS|aK-IO^iK_pSsQMeA>e`#2>e{bB z)wN%Ps%!rUs;<2Ss;>PLsJiygpz4|ds;-$t)#IS*+S{P&+HXMBwf_UEuKg>hx^@&) zUHcuVy7qfeb?x6l)wOp))wTZsRoC7FRoDIis;(UaRoDIqs;>PBR9*WssJiw(sJix_ zpz7LRK-INTqUwyOx`(KG5m9w7sJdPZs;-YAs$N1=eJoM+QljePh^mh#sy=}Yt!6Pu zio^_$bsGkob1u)R5TQ{OGT6Ryp%f}HPN$XiEIyqLJ;q|tj@bF_@wYi|`}t zu3rzs@##V}2W5hzl9NjPc>+=?3DZ2_zeP^LQs%K(d`mTln(yl#JbtBwO*~JNTS}w@vswjL*N} za~ae7bAGJ|slXMCw0QN>%B~u8KEzRZ?5G zFke-u12bADKt6!Zb?>k@@Yc1`G%gt+iz72eKb3zB?nwpt3~AWx5jfJD0j)&U=Kc@6 zRqjZo>znb(j*b*p?l@Q>XU;#f=tP?GKPF!(;Gb_-wjbNLK(~F$_IS#c&^z z4DY0?d+EiabOp6h#V~X|&BM?|RSf?Nz4#AY?LURSKtY=KUqDxw=q>x}=n7S<*ncCv z2-B5DFQCn-*#9|Pv7wJLsA(`qX^Q5f2c9H7sG>oUltWCjqDa-~XHv07zOxbW$TXW; zXpdvIMQSS|wKIUzva1D+n#rQ$iGVCdy`ru31$m-5+99aO?F>qvzd{XDkual%6?P44 z!^c;*kFXf<6_gYNxXJ?Zp7vujvo?IHMP&NNSuc*ve1=rW*4@?5Jwv)*=Z@_=&FY02 zd|LDzXu6`+gi9c$Sk~`kG2{g)B{~Xpy|npbrD6YWd`Ipqes9%mPydF<^Hzc37VXHG z@L26gQTWUwOD%#}^Wu{p@gCGCQZah}Qz-jL<(3Z6)|>DTPsQ6-@qJ`MN-8-SFhp~> zzB&pe=c!RFmJ_4balI)DL2nWpn!;je%dS`Yy^+@9$oB;dtP5k%AU>51UBzNBvsjgn zJQtaYvYAMP)uoE>y|Z+0U+K5V67%oF*ID)5ao}0>BI}=N-8*$27x}66$gY?%0czeT zc%Yl3!2MIB&~u&|ZQ=U0Y-k+AIs}v}evS>5u^1`Z#rh~JXB7FoY-l2jO<`=ug;Xa` z%JcDzQl3GN*}f8n#7H+Zo{=6t?dfFp=p@q27h}BwNu^JOs-FI*oyX>;u>vbP;jGse zxIr8&+t`7#k3!oQ_IC8Kd*(9s*jdW7?4IdNW0R%V)EsP3goEfDUQ@lWirrI+ZC_;` zRtsl=3vfAJ32b5ThMSOhDiW8bC6>>p(Q|kvcJu}ol1EHur_LGwORqNKaw-#^mHMe>3 zDcRJtrpdRdl)kCtl1&X8=$^jmo;^60eG%*s23TGd11;#PjFo^Fmd_>wgK*VU!lq8M zucm_q{#*$=b+iax4kL9qDzS2~w{l7P{=_sSfY%!#L2VQF+0VPl^=}|RJ1;yN*vLwj z5irh9Cy>a_55_s|xrJP{%%jgQVj~T32o_aIkAa?+MFEO=u4E%0$&A2pJP>M~s1gIr zvzCqA!g|y96dxBbX{~Rv^Vm_$($6Bx6iU$@7kN&;8BSOWPc<89WmlX;Gswxw8Z7As z<(a@nzRtdK5+J2xqd%1F8XKXggu!L@KA$Y_%M(V_xy*AG8~Fyqo`KZB+I9sB!qj%0 z)lCkW|L^O4O=gg*KAaQ45|i7<&NGMSBBni+~A zXI|QG9>~mH%J!TG-#nW2ieRHy1jd0(vx!3H+B21%DsthNXS*~U*=ePjB-11zTYhNP zLQpGr<)cvubJ!QQb6H!1&&KxUyy>Ib1;6S!Uy&h&q^Odngh$DCC3f5csoHW6 zo)sT$0NaJ~R~i^V6DznSET>%>I)h7aWg95CK z0(c^jXGk2f{RNY4v4?nu#H1N)s!VD3hwYMRVZv!L*=Ig*zjfSe|J&SeD{O4Bt74C3 zr5q$yl*7NZ(+&ak*Y;U;wqn#`o@{nzcj!20R-fG{2?l|Y2;Gkp%HVMp>UcxfT6EvP zA{6QnCHD@5s}Xl`mQyXcA4sI@7AXc;(pA`UBPK3_sy)DRjDW+fEOKDxbY;%=_k}O9 z3@Zk(O^CdErCfF220+L5`xE9d8k1Zy zcCbp;Tc9o*x>oow(@pu*+|0PFL;AN`Wb?35_yMwgkHk{^5zr}4uO?*+;U-(cevH@TCi^aaTTszn`@3 z-?V-io9-fNoC^q9PPAE?`twApu@)M+D)dPc40Uf7k8fx2mWl*v&bb~oW(8Rair8v; zS;RaZa&$dkq@3=;rr7|Iutg-C>CT1PX)`>7$>k0TfN?8qE}KeSD}c?;$J8DVIJWP* z0)L9gz~Lot-6FDb6p@vq2v&|t7&&IbR5lBive__{oen$M8L)4h3G>Dr7|G6pjqGgL zHqJ48WXtH_8G;RQM$Y1dkFZiDCt|8DBsnA0 z6N@?+lL%8w+N``}vnqnk>NGawBM&rEKBd`Hei=CiC8pGvJ?VLb@(P6%3@x`5+bXB$ zcfOHrR(W2kouI6nz|W1&;B$>Lc}296pAxO&r$#U3=dq#1un{xX9(ji}YxY0XV0X(g zQs33Y+P9pAIn*^;)wO$0q~Vw-dDk1|JHLl&?D+Ok>X-icwn}bS?9Maywj9N6%`38w z4M!t@^iyco+sb~kSLAtDzIc0N+-S?YJMMUw^}k5(`Y+hq@^)$s-cHuw$Rx`5DXM|US`)i! zH55sHiVdmCgC&;15Z*pXkMKO^R~(7avFgsCw)pU;cd^U2Y6 zJ|#Mc-<|k8gzInd^P|7O@4IjU@euxv=QD<<@C$}#^LfK(^Z9IOsW5)FaoIq!I9Y1V zn%M5f;wRrf+g2U!eB;is)+h@F^eHUrb9P)tRb>6Aq~h^o^i0(KxtbViehw)xz0j0;jZ^WN zh0k0}5mJ~I!@%J+7LzV?1|x&r=$KN1H$FKl000w<0ALOmMd6u0Rwz!fBJH@45-Sv^ za0JbvIGtcAPRl?sfhzRLp*Y10`qO5z8EKkHTiqyBrqH!LHYL~V0=1;4L*`A_lG@;< z0#xWZEd?lk5F+UR8$D<7ap^f1XNKT(lmT|wlyx1qtlW+;py3eqH`YQ4cLkJiA41F) z(oQU{Nn_~pXJ(#lY~(@;PLRq3X5~qiRF9IXz{OHYHA@T0#XOtY$nwlM%vnXGw<%>G zpJxFnZ7);QFg-hQCw;Qgl8yIFXCqfLeq2Co9e@+KZ0-siUMHdYDiPQvuyo0ink9&o zL7liF^T;Yi3jrP58SPUDp`L;|cnL!wqe2EEqkFQ{T3KaV1DM?}v)RZMtiG^P?GAAX z59-k$WNhS;%z(mfb;;_0TiM70iY1e)19GVI#LkC zr4dK8=a4O#TZ*(ZAWsTSV3noHN<%+8u@zl!CYvr2SKGU#TM^@xj(ByTo+?(KJ=56~ znb6*l+b1Tt4Q{eb^?~Z9*4CH&_wAf^Ro$E+2WeW-G)Fl=%7hmGHX=mWrvKlnP^fh( z)eMS2!F5{+51aixRJ)K96(Eb!RpChI&Hzr(kn4PuupMKqt0~O6u5qLm)*xLpkl{ zxez>OZ@VZ4)f0ut`sFNvv7uHEWH(n;#b+g>#=e1H*h5e7PfEf;Q6fDfwgBB+b% zfQxM-dH({DdVF$?urp<8K-j)}EL9Lh`63eKJtWF|NR;CpL_6mh#)*hUtgjxYziA=7*q(ng~rm( zwIXvWKgEQfNvXLUcWfxc@GUxitV?etMe^LdHaPgAkD&3?3F&{t-s4ZBuhG#`o4;ZE zPPbGUv#FAEpo*xaI(~$oVyuC#0pSE(ca>9R{s*Ak4^Rz4xp(kOs5a9Tlg-S3|HnPKH0<=mzhg& z$A-2Fr3@`MV>7#{5G1QZ#^Xk^V)C-}!ppYYxR@$e$0r-Bps->?)r|BtIn)L!jlo&a z7@P^oeok~QKbsBBmg)z1Q*9`XtS+AadgObNAKfqX0^}wOcc@eNIbx#0a|HUJ_A4WQ zg7QFWp`fg&D~lYS-&T6*?{8lcd3*lW;=LcEEySzmvZJG;-nS!lrSt2GcNbUgg|^{m z0xC2O-r~yLb*0{|#k;qb*^t^wBR>}JJ$sBR})Lf^M)oJ~E}! z`-=Db=zP-m;2m8%L-1qoi|9?6_K-K`sY@S-^P^W|Aa3IqMDM17xXW_FOdE)o|A>-b zW;^~^d{><{9xwj|kABXQ!}9Vd9{hquex6>nU!>FwuVl^YN;f~xCbAuMr9~JnT#pym z&WNp~5rdN)oGo)X$-&QYMzSx>lGDy)r(5C>Den6gt`CN=WyH4yF`T=k zU)4*;$&Nw1T9%NBY5acEev^#2mMkJu^}~*dRQ$hY`3g)pbq^R14|I zcKf-z=E%+2?*C(-y5rL|Rb@)hawNk7WpMa)3IdUy=|=dz)gbn*(Eg;mhPh{Cp3B+D zJjA0)6V=nwZ>nBev+P4CV9^*ha;`e*GUjP2DB;DdsF;n^X8Qc46`tNod0vgQlv!us zdS>NNYvchzVrfvGSQBKPmC{|%z47VggXNVIcAbB-AF*pYEkm^DG&a(dsj10RiFF8v zgBV^pV4+S6*~qfYlMM2}O7RNvbRTb4$6W4N!bW;nAP*X(_r&^+0M0@TSwR62B+BOx zZG&5v9XC&P#v1d$iB6477X$A$7L*WJcTFLfeDTG=6NU^mq1$ZS>I}#N54_`3mlSLPX0ALfz9kHsy?LqH_~c)p2<$Cq1DGEdVI1 z52A!>M6Gl$A-9r{=#_gOH%;LOM`HN5)rv*9)<+@IJ~$G?KJ=qjt_`VSh1X=nZ2rIO zy$yVvMYTUZY5EeN&;kXD6e!s8PHCGK%A0PRY)Ld}L$jH-QYhPG+brE?m(6ZSf#UO| zEwm;TRMdK<8Z~XXiW<47C89>*LRGvXa=oZrFIS)4Ewgk#M^ z5D$qwBHBq0N5#4}5F*0{Ly0IgH za?xm_YepQt7zHPRiz-Llh1PTokC_%zM|?v9 z_O)f9hNWxQcFIGGahxnvbQbsSrWzt$kyve11i>>cyyeCX@e`Z{VcY0;FqBpe>9EOGg zIU+6*Hv@-w1k3gcQz%9x;H``8Q+W(l)SOhy|I=R#m#^Tk)O5s%-9pJWRS@RrLvWt;K{VrAbsLM^^-Dyd@bN`*VR!ky)KZxkHD8g{om{CVQIoLTv# zvNICms0pcXKJ$`E(dDkLJlG(cyiKVTwMbY#TF#U@U0h0f zd+?&y+GY`0n`Ts7mnB)Jf}}#V+I(HTdX6LH=itx*OZveCREyd*u@hFOYP|{q73z$f zS~LJ8Nq$-RZl7%x%QSILTh17H)eLMwKDY$doQU798uZj0Iy|AIA+fw8i;6VW5!9~2 zgLGoX_pz;y)dfa=w_0=Qp>Z$3j>b_>JY#8`dk>f+m)=F~d`IkVf|J;+~ir8{Si4jUd_PDZ^tjR}5scx>8Cwr3`bVu7#1m?8e--^{qJNdmhUX6IL`NLN%W+l=45Fu)DBtiL+ zASxqWZCpf5^QTKh*)nA>9dJIH2gVuo!G;A)OQ$Nw#o|7OfzAi|fyEwG_7$7Fw5Fgv zUu@tYZYQ{-k~0-7)#=XoLKvhgCgzr0J7Z!@@Sz)0%j|82QwkIvO$MLS(?L8_Q7m5 z-}O->y-`)?anNy|pB9kKOGMA47SKu8w4YK$_;nZ$RkNeXVloXOv~(((po$kkv%qe} zMqF*BGy}&$LR76;{KVDF`o>kNR|#hA!R}NtjDz5*c4Y&+W?sZEg8wg6(-cpn6OECM zw6ZrIqL;P$=}k)~EPAnMVof}pY=d(+HuvPtM74FeK3`FoGdC4pw|MEY#)Y9^sAeX+ zRauG2T&%?>JT>#rIeS_@rY`6Zu-whYD|G@hu=$G&YxHpK*OX3KBrf~8ct%(U@% z;Uy)OA)1GTK8w)SWPD?v8bNz+O-GU&gE8SGVN#zzYj^Vat=%8;*}ld&;%e8%?-63( zJ|%C9S&_Ubol3==dsVr z`fjc)I3iPG?%O^(;aGDdoNQIfkZupju3mP~<@45trHZ$cM@Fx5do) z!%Y~E3isLM@|0i^OtF@A%tzP4dZf0LzBjT_8mFmm$#iFObOg%QD!bnh54VvRqh+oN zp$S`2w(Mmg9E6XAH!!vAZ-j$QIvF8bx~!(8`d)7F0^M`AvO8jS2eIBTJ3(M!%j3DUl`5K+-KtdCqU;aO1D$4E88};>}v+oAHD2TIhxhRr8f(a0rb?5DhC3}g>>Otu~Lvu>8*qjDDU$e zHKZChXO&u+U_#BURIjhdA5`{cQCLV=Y!j(%p5oFc+Z;@NDqh9f$--K!Hfjm=%F$z! z-Sh1wHz{XjU3aP-e9WIWV|-wmC0q*W-99ky zxl8A`MB0mP+SL<;;Ef;Ul!jkjZ5;j_ZFOYslw7LVax5euS*4=jdn%V{ z;q?PWq%5z`AR#RFNURYkxOugx7{;rO3e|V`7KLE#QMObuRFV#A+S48DrKO56=eeo^ zK-C`pVr;EQh9s4|!mUl^_BqVIi{eR0=vr_b+%6jLF#U7;6kS0<;TPj;ySb3vkI;8C z4(W7jv66Q3qI3>(>DZLKR3+uopto|f=WH}#o$pIW$U9`0z58u1WJ*4he{`O5d zU9xx*+S@mUZa!S@?9luaJ44D-MnIMT=4Z32Ogo@5sOAZ364P%(cwNL#ZAv}>fQ{kq zWwiRlP6t#{Im{{}KeYowwJS0jBY(Aee3vHZ7MV^LQK*(sXc-xsNn3Xu6sS|a#Utj_ zsg$&+-j7>Xq@!3R@PopO?eN*V=mW$RrEbjZ@uaP0D;!pTj{9E7V3G$P0*=mPH2 z%Ns>DY+ra-B~)f9cU`8Dx?DY8)wKEVk~9IAtHiT2Dq^h*clu+-g)Ydh9bV&dc+bALF%DHh7Y`bpEmTZoriC9oz1JmE19R=bSXVL?AF9PI&cY(pZ28YsBUX27E1(oVJd}_bYd^8;1;JMWzA|8 z?m27q->`T*t~@AZK|UVK$Q6olAiKMzj;eN78^clDFF{9ET)Lk6{3aZ3*4Ly{Fja3P zl$wg2$}F-Zo{FxGw%U$epf+>YLx?AFm=bQE@ea836eroscuc2rD^(|2<$W^Y*z(3D zHIAG6{JPp2IM3kHj68?GLXR=KKo?u*Cu^a)T5s5o zG1;3+o~ez+*-k&5qJfngsFDkn>{L01;c=h{XX&hvjrOoFiq=NOjO_8#GEz@yAt$$_ zOQ6+R9d;kbNj8-lA)wp*jSfoVh`1F-xFnW5Fp;WOs;f<5ad2H~_J|YKDhIz2G-bSY=2_M#F4-)x>493|vR_X&Ix2CIX zy26&ejIK{u*wNZQah7aFG*cB>$vhhBc?*Y1Ww{vv3eM7qrR|E;N9R&VRxKdr{>)4aL_% z*x2GnMIzXNP~h}61yhX zD~Q-CLbIRjIF_*sdlafWR+e|P`B61gR)giqXbO8;ut=L!0Ks4*#|g zGy__(FO&}D(YV*0F@AQ4uyY_aJ^SDSwlH=msDTXh(Eh0rD)dAVntcKISHQUlgcR$N z$9<9>p0g_Tx*ivc#G>#J@OQRLwLwnSD>8sCj>;vqmqV}0ZNY*gm!A?*9s)?YJ>~K+ zm41c<4${8lV!ten{Xj9c!nQ#64cXw56_nN@JQT`ntv<`kKnrJ5$TCEVisfdJ^C(aR|80zl5j5fAMzNPvz$R6bZCM144Zi)c<4P|U3C|BNj58Hi z6$@KGpp>?gX1IxA1+TXCmnABd!Kyi)R4!7Z)%m0$*%EfbsIYxolG-JUvRm})7p6*; zm4c@ZM+EN$^9sj=O@Ste+E6Wv+J0q1Y{uDzidl#YL{CQa9;UxX-qN{~bty_n-HUv zE7D~F^Z=a2%s?c99d)rQymH$4aLbq=wJyY>C{%f68v-7h$Jc-OJ0 zOdVqNB`DrQz={k>_zHQqQyemLuzdI3o$$C^eM?JyEtV;B$`mv&r+_|2rXhQYQd6c# zb9gO04@o0zjR?mG(dEv~!^>Y5@@=0qT)9~j0` zJKT>Lq&Q-f7b43nV9zqi`*2qgo92<1W;)%2EZNu{a^`$;j2CE;-CnMCv5P&x2JAfX za(V1XNS<(-W08nBvKD1C3+s}X1A>3HC*vLF>GdcCya!g6fVw4upqA7|*1&jY`Knqs z^5jLog*oX4Y(Yd9wd@4D9S1_VNO$e6b!epSt4Do*kqPsb|srDGPZQkWz<~dbJs_SUtuZvKCa7n5gB&kkjX8Ix=eUF4Y?DP(q9=JGmJ4^b*T6VN?df9Y7v>OZnMp`pPM~EAfNxG>PdTfk2ivj< z&C2)xF6Q^?Vrb^fmJVty7$O_}-4gw;v2zRYMXXGs(&kA4L1*$dTsi9Zw&I|jIbljG z0$8wGyTc|Ta6__Hq9p2yOQ05;I%FHnpgYuv;_Mcwi3{Rfr1*gB>r{4@#7AMfERub{ z%(s%AV>MKY7t9CqsGvQo7Pj7<#vY?Be%#klO_jA=XLh#U7Kt|Udt^lyV*&~-&u);N zwavVW-fG%rSz=j*9bjB8_(-2I^9IVwUx#`N>j&l=kzCO)T_KxTH2EIYG*v1@F8 zY{18p7gPK|vCmCk$Fq|W7rYkn!Dr(y#S34KzcqN@ZvK7Q{5t{J>3bzRH7}y%C4d4r zlJi!Imnv2iP~K;Z@wuY>8JO)-Td>1*+JHp{M1zN^cP&NQt`{H9E*+TDDrNTHDkX|k zu#*;I0k?=$A_6pDe4Rj5L%MjBtfEX6Y%FD=LU)QnCrf3+T~3y_+i8{?myiWTsWI&Q zM7e03B98d3mqZ-;3S%;bpPxd(=%>y5@^T;E`P zh3nnM_o<4P>F4M4^DFxK4gLHcKbeULP@cI6jmpfWSn753v@$eYmFY#0^~|@p-jR7F zG<;^}2ye-r6dInAosEZg<1_28Wy2T{qU#Cyq~t_cFB44}#l&##nk+Cg#yFGObPm@W zSbxRXVIbZCBk(M0qNgc{rB46efxj<`5s~k2|GtfZw=wX)1OvMs7;CW5a3wL!vBWDY zjp_6QUiqF9yz+g-E3YD6xzgg5^WChH>sNqPo(oR7-nf(NR~v17s?pB1V@8bYV~hm9 z(nxWAnbFPlX5)UYhYXF^8C$sCVr=6&H~P4~+<1iRD~w0Eez~!e>$ic$-e~mmn~VW| zzVSGpW<0?!Fb4U}##8)V#yhWVw&bNn)6Kfl6wkuNk};){$|c#ZKYzsh)x z*BY<$pkeUE#sPkdafr_{4)c2BP5vI^2-jyCN4b7chVl1i#`1ZYD&CNp$gj;z=GSGW z@CBKv{D#al{*KIaeqLqoWF{O-)FJehfocV=Gasf@wXnFG8lbBN!QIn28= zZ}N?hIQM3b^82!k-=7`JH)X5%1KElE{n^P})3Q^zre~*ettUH;Yn!vvxwa)cgKJx} zmvQaE>{VQQ2!cx6mYv77?b!uf`#|<4uJvXETaCk&nCFGGn?YtC$imK`-klPT>E5JFgt1>(4&Qwa;XCa_zI($GG;nY(LiqvIAV(oqe2Zd$Lb(ZEtpv zYmaB2;@UrE_i^p>*=M-+h3qibp2$AOwJ&D(bL~sn7rFN3>`Po5%)Y|4uVi24+LPJW zxb{!k*SYpo*5KM#vj@2Lwd^6TeLZ`aYx}Zqa_#Bt5v~nok8`g=^o*P378mbJMu?Y;HQ&p3BYP+CS$m$lTzfgUifcc~-O06A za&27u*IYZ-ewvGM?cZ_aCxfi+i-?^8#b~yJ6*M66Km21Dxy~edamAPf6N`= z+MjZVxb{}=FxQUc-sIY!b4R%LKdk@8is8zmmj3s@gsYy|Zd_AwPT}#F8oHY%l4QG9 zR~U?Ie=){#?Wj@3wZ9q@x%M|>GS`_gh3im%xn5~ZNX-3IV(zCAbDu2Cu(^IQ)6U;inOYKOY=kzkpc$g~Z~g6N|r?So}MQ#m^uXe+g;Pmx9OZmx0Oa zSAxsyv%u!{YryCA*2H)2MVEp<*;{1z<^Vfj$>$PD0dJw!{Ukv82-vaKh z*Ma@(^{jsh8;0&J)Nik0ur%mN%#R{INhsoDjq^#)Uc_5i|4D4v)U{dvbT&MmvCTIN z5=x)`WW{Ib=>YwFj((nHyUt?9n9%SzV%WB!PhhOM_vgJo(SGkL z=Rgooy@7@%0Z+VDzr6h{g1hAxsd3`#ZK&aDW?&IAZs0Vouc8IRxPhr$znxkA3U6Zl)ol1A(PFc| zsp$fSU2^Mi`ZU>Q*83B?b>}i;95vunf^#;%)wqxzE(@Xe{dcqBlL3)0-|}**YV&i4 zMIm71h#QgHZ>hbD$^T^f;qCV{Z2ePOBB1(v)i3wHAQGOeFduqf0DdNkZ*32YvD*CH z#NHQH-$B^AlntE1^?Qx8xqipMd0byTa3xd1C2o9zzVM4$NFzz!>3T| ztX3U<^614s>3v?lE;fgycX6%wu;mvIOfb_#HRrN{O0M5Oa0b`IsHS<~oxuITEUs^2 z{g<%ei3GU!_r0$kJbhmU22{YP9u|LY>R3Xi(P-oW=E4)_(^ZK9vdDo^(N; zm10(~JJO1{ax<=*F?$ACzlcNv^yB{LP3;x1j9}Th>l8ujrxM%gVf|q?d>Yo0n}-j* zL0~r5&KT?c^dcyOor(X?$N$tJ`oHksW%w|~{c%?{8vt>fJ}`;vTL#YM`c^{Ig9fPM zG~+7znN2_Q=%;~xR)m0p{;#v)NpdB;zy*yaJOEudS5g49ijHS*@4h>HxtH&hL*>59e?^r#y6SIfagX-TnuFalw98py1$dH*Y$`CT;m55~;4w*-d>qfqo2iE%2{uI(~NjfEj0zjqqYn^uVQD?;|1kL8BJm2I{%qW^j7Ci-!h!xgIe- zN^hU%`r3i7aedvueu&rC=u?jC?E}B%`oozDypjR&j0}YR8N0#M-kSkGJA+J#XJl@o zpEdLoqn|GNd60fSf}h==Lgw8AfU^6mTz@3iN)a z=tUpOo`%#tkYpd;GYzF!|7R+O!Gtl}AY6XF^%v3z0$_OXvKkdsL54VV+Q;0DJ13Px5il8^cQekV0|e9c`*J{QSVa{F=?V!R?i zNoT6lOapv74Oh351HYMaR(?vvDshqP`z;Ziq(dyvS?0A;Rvv0lRbGSSVK@`?`9;a5 zYp%bg%KP0}>{XVyu}LDS4*n zH$v7Yk6+XNqE(-0w!CdKc1ZhwjiI*nabEdjAv&Y8-xAq!HeOfU=nqT@E4F% zIIw!owbcvevkHCfI5s%FT&8PT#d{T5u45H*6;&-jRj)#hmTk}V$n?lqHn@X5ShCS( zn4(a;F4QQ(J|_xeyws#|>V1RRVax(Jo)Otp7u-I@n?rQ;r#YTZwu-pR9&Qn6&nst_ zZ}jzZkZDSVBGX)CnySiFjZDTEHkf7wmmHa6W_N#CXfgSNoUq=b-kqnEALHvmE8T4H zW9;GbBa`PMdO{oNJwz)&GUe#%deEn?VKQuD1?LY65u10o#xHu)ol5dT{aBX`cC&(; zLW}AbS&)mOqDx)dJ}E=$pS?Yo4!Sj7Z3cr^|)l*&zKiJ~~AmY2VZ4PFZe zb8@AdXZh033@uD1!y6HvbH-fjAb@?nDP&hXIg?cc*kB`0!kSJ>mS_vOL|b5HDA}3_Mh&UneYANWG>5@d_0la`wzK^w>DDZUcJ6%5F0B^$m5ro8R zEyv?gVT4t@e+1}V6N{+Odnwr0YFx@A9ybyc^{@)g2Agqn1L5^Lix55V zN-kz)kq{=5j;{@|ird-XdqKHkw5kgcV(FEEne}u;1~&wiC0$nWJ~nteRt&-|EHK&z z1JW~))D5_MLJ*(DZ;!K)0QWaEjHs`Wiu1?DBo@VR2SRbd_Ukt!#En={!0!coqJR?u z-*tyW#SCiqiW37Dyxy4(4l`Pbi>XdXyG^{9kj0E#f$O$?!B$DW0zop~54x4aOTsUl z2)9O4rZ;oNStkT8&T6^gfNCiOmuf0XNyku!%dWAXtl_c`(~4HcT~kBHh33e zaE)?AiF9xtH7yQZc$3fNY;YwDjkdDVIvV)PW9;Ljh3yQPS1*op9M55cOPw>0C8^Sf zrePn?gxY@(8~h^MGujT@5WBCpPE!^gNx8O2K^mbdi8Yn}Wysr|>_aE6y$PS|5ruAm zrhOhwJ0XK(pNl2S)LPtaHOgrfOd@VFO;!&Z19<0kC$=Kod1AIXgj+DAp$aYns3^q- z+t}*S_6W`jvOiuDW-=R$v2`b)8v7E7gx5^QwyH96>%awL?rDd&@g&sDwJK1fcMuKDc50KegUPIwXn>FnA6In%x zjb)hBPH;bJ#Ld7oroENb;gfkMp7tM>?O_w^Psixx@OTDK0`i@TzlqMh-Ma=W=$96?Sf?Mne` zFLpI8-d4}8p0ipWVexe42&|sHdQrShY!<-?&nnJ8o@%N`Tn$*j^XxlCL*QOLN>7x) zOd;Alc3fr>>JuJ`xXkkJX|s(+X8>P!u)%}O@YRt#vFFid*h#q;AOa9&a8pry4 zpRg=ES=3t>J=3~z9qz(L1Q#Fk(q&!$J5Qe**kFpqPh87gJ4Ie&Lfc0$0sfFZUsP== zI=?j?ZC(HWZDQBI2Lfos{m{aPVf1W`RjexOy*TfB_+Q@12J6_uA{4r4&KIeHd{@Fv zWIbCuYEVAM9)4$joQ-@*FjjFZ8@$b*vk^R%rAL$_1!k|N^QE3rfK|j4;{Z=g3>Ep< zlvQ1iLjYl1_JFE<_HE=9u!>gS(2Fov4e>ZC%*~$-CU9JsQ_ECf?i-`PoU#Iexm!zY z%DSfB$k;tvyclzkRzSj4%i{T^6r&MACSSb1%blBpm!)E@;^awSb@RgHI(VXXq?V>r zOV*xD7I-l3tE?Z2P^1djErp<_LPn#HSYnmHORuH4mnBpJ}P zqWP_M7vS*Sw@Yl6B`(9=&E-ohS-N!5dz$TXTl~~SXC&+rTV)Bl=$dAXhxTV@e^65B z&Flt|{rG$_EWA7(Zb!v*}7oY`z~#Id5K!F9E!4$x4gi4E9JXxtLv7Z zmhAfR4WWO0S+!yZ-0HQ$+trP2tF+u6l`W=aq zFR;|pqV>3aLAbJU^8Q?Ss@V?AGeI`u{s+f`028!5I5i{2iEArQ zqBhF>1F4O>HtZHVJ;62T#|gyM82~BoSzU_Otlc!LFaFVKQ7hS)j=!|nf)d5M*v-BN zqc(1CS-5a`JjGquWAE}^yVY}ZFT-qo@uEsKONlMJ+>4KgKe~*}beBHs`*?*NYy$pN zX*&KIs9zgukBBgOUcoG~c8St*9d|BXsF;EQwk>qK*TwShu(D>ULU$leg4?^D1vFLYI@Tcqv+Whv{lflsR@A&?{^ z$(!DG5y^OHjysd-YjQH(;76qArA($%o~_f|)VplS3#GTY9p>`Q8(Rl-(u} zNoL`gM1;j|J$b+!%j#zO4vQd7<>*;f7OHO|T1cDpc7=E2#5iL7nwsk6_?4L}rmb3X z4zawz3a$(va6%(Xa>7*>UT6jnfP6^Fd}U}9L%E2?Ty!joe+a$g2^5(Z$6GBVz*y^5 zyF{9-95-m>Wi!W(G$!yhh+mV^yC{geS57&)rcaVxpb)hdXep6G@pNl@W4I%XcyO{Q z2*g{m0`f)bOd(($WlMHfi!jM1FsM&vqPSy#HVl$+oCffQlrr<1H#`ZL^DVNIVUy?P zmloy(IB`e2B4$}9X7aolGn2N*i!3c7a68Q_E?pRR889#{FQ_aWF$-T93i!3y5ibkL z%9SnHLHjU*Lr{>e{BF4NEsn);m5Q_xh^V?H@s4_}j3lg@qp65suX41!$ibX7G|^DQ z;#hc{IV-)|1G06iV(}ubZ#5hlYm?!1jb;=-d&|f^mTaZcB{C&@1PCq^#bGM<%tvu1 zsiPhy6hy7^EGlQiqG)Z@YHHa&xii{*vgMeSTEuuPtk6USR_ZrkB6;vXD=S?#7KO-k zQ&TX%A2PDBRjH7T))rH8u4QAk8)v5@R&Z%_pmkE;utVN=F}eP+u3)2!bmGZSeAz9F z2;ZK2K7Dg5ri)7Kf*}fSPmIbMt1d)-q-GT7umlgqIxU^UiS*uuD4(VCG^aMo1y+|S zew~W2p6CiAx*M@z$=4He1S4>=Py(wh+(I+g&z6vui_n05Fw#1UlyCPdxFpFQj{+#R zmFkFG#=gbo`>r0L^BS;%k#oHu?7LTe7>b4FDN$LDE2>)-zGuV7L94Z2N_k8}e)SDk$v;OK>S-I7XMq(STqE=U2)@!rCCV*-M)y z+4hlCM@m?<)vm6)OnqgTi5JUV<+CXZh3P@`tR@nRSu2-vy@w+IL7&iT!m-wL3|!Rd ztj*hCP0i;Vk}V{P@F1yo2_zYO981sfZ6j?Dq5bdDk`})$5k3+wlYQBK<6@b7V)VEa zk-V9~h!C|Ho9(HnlyQy|tUdP^M`N@Zj~&MXuH!jFYU5H94YoybdksyH@(tePx50EN z8;xz9sp6Lfn=HH>l7~Wwf}Dybi2p8vNoc*0J2;Ez6JJ*KCKzSMe77Ju(iXr9kPN$M zvXK3_rh&)QVSd@2IIuuSA6<2Rd5oyeW^_eE*o;+4ikJ^&5i1ky-EpNPL z5z@TEG3Vq^IXd!UBdqV-%68x5203SSs$6BBOlz6%ngn~M@lpw_l)Y_J=BHUrbqT<{BlL4!iO@t0Q?D080F z<+uyVC6c+wj`^&g%#k(i=?-yiL1J!^xe~Mz32%s&#X*#|O5A#{XJeN|I$-=<+7PS_ zr@~@_b*%nMT;ykphQ1vj{ zU2>gVdK1W_X@2!n$3APxowC&ASC%b{(0#*xK>D9%AP{)b;Bb*`_bb=bIPSakNBO3U zUNXMHH}Jy6c46GWfH2zdE! zL|Jfs9Cd`8xw1&k6jd2R7<8;Eg}E3$n8djkLMBkn;G+c=i{e4(h;A@9!_v`obYn*X z<)YC<*NixrNTHN0tlU}CS|n#Jnt`kWU;3h|t*!J{Y>kQgRE0%34;#hxbwEeD$F^Hi zC&`UAABRPy1WqY$xshNgC74L-dUMC39gnVEL~l-vO^3W5K_Mj14ZA$;;pJs0YJ4mb zPUcOzxW6)?$c98s7~UNcN%K_oNI8IDBum$>?TiR95XXJsiq?@hZ@|?LvD&EEE$nC& z^_4?3Hku2U@I{>_u-178QX8-vsvJMocV=awBrgJtnURWM5aHB70F!0C9EE7KzWor}3y>|(Fc0T6oqCzf~6mUgsys+DL&2`bXwn&H2 zA%IfaWKp8^7QekJhP^O`%S+p=-G#V+Kq<@dqan0R4pV7tM6vKHRK%RUh2yCZ^nd&n ze%Nmh$wDJGUKVz=$+<1&kZLTF^Q<*hzQBX8M;;I1a0u@Zfj?-2@Ew&K{kDs4?00w* zHD~Vi`Ji3=i^x^U-rnW6YB;??4A^&miP;Gu@(ES>_`eJk&+2RxyADrWI@! z(K)PudG^aTUw6;92uN(g&Rvk#UH8lC)pMQgTiQ%0H0u>jy7k-sHC#h?@`wUX&p{<`e?Tg(vz@!v>bPnD3Kr}y*+r*Yi%O| zY>+dm3oWQpeOztY@o>A4Bjo4IhLlhGtgv=X?1a_H`o3GKS{GsyQgqbu6sKcm{*WV9 zR#8mNjV_yXH!LH5hDzL5qncx>IZ<+TQBl5FKas^^>6*J>vXZO{Ucdgzkx!S~53s;T zrpVxcBf}^H?`0vBJ-Cn$-bzs1_hBwqHrQ4Ngl~+{{8bo?!nI((5->GoDK1_V(42F2 z5}l)?e0yb;=O!nN9v-NhJ2!vetYt2G@;hHWp#mj)(p~K=d~kTaJu0kSbOFSSIdkeA z0-D)vJHq9!kkXwqM~4lME+?a2oraJ+yAs}Q3R1R2ZkN(riX+AyZBF(kHRz7ErEq<) zW8W34e|GYFqaAIy##Y%_aS5ysmai5ca{lIbD3Fl>hvOkKe8dbe?!1-t%7+ur{ z+Y~S@{imF0j{6vf#W08afrShIRed$R4am_GJ_C*6q)NCgO0nk1k5nfD2sWy1M$BrAu&9WR0)#@W-({LCa#M z^kS*QPaQ%i*k=vP)l#%^8WLq`4nsKyvn_P^Db{a+Tv5i4&qLYjJa*Pp#%AFKLpCbg zGnNjyNBrQaDV|6t8Y3NPwE!)>%s{9KA0&f|fs)YYF7O%G5+-oA_)cF3P^-zY)SP=)si|)%n=B?S*C+ox7OOd`lT|dA7vk)%iPl z4uFu*gTl%6WMpBV8bNz+O-GU&gE8UHCATXISg&1)#Eh^Th@&jWZ`J&e&-OLO)15er zc#qhZnQMilvDvdoUX)Iy;vJ(?ydYjvBWaR#-~Xs(mplRO_)xXB?kYl1~E#lpl{ zyI+f?yV^wc-pEGhOiMD|nH(L};L=PLM{J0P+eo%p4p!wx$kUn0hoqE_a}hFR%5fX< zz5^5q)^}3nY-N|kR1eaPWK@$l=c`!sh!PEvu#C*%$2du&ToX@3eXKo}C>v}7+vrQZ z_hGaZ@LY1d^j;-yh*S{=0V2 z&S;xj(hBX>0oHdzrN0p*2_(Ec%c0qa@BCc&2-Whc8BBWg@{08ZDpk%j zwQ=bJKy(A8@1sg;gE?QiDy>*aWjrSC2sGh&j`~nNV-oc<+3db$wLt%7>yGB1wm2x_ro2aeslyPv;h#qvRoZyMaI&9W_?yFxC}bE*HDo zRE4m7hQp6#-8XEn^*HKOvKiE8D}H6*D0N7{Sqx|BPFzJr zRO7=KjuI{f@@^lP_uM9Ne8}w8D_JZAA$Twq;6^#6;fPcl$L4oi9T~bLhbp$E*-Li1 zqeJBuiDsEFxnG#^!lTnv0EKMM$kNi7>hd5- z&Yg!z5Kcq`jR;>yH?#yo%Ns>DnDaf10;>HJcU`98wp=}4)wC%lqpKQe5-nGWXJ=G2 zR~PQ|$5RUxP}go5=EUMk9~F&$X{mCo8Ht@$X_4}4Vr43QQg~V5Oc9P=(X^8ym!^fM zWwPu+RHePys!!HKpTf=kjvfhFElprc1dyZkLUBbig^uPkT~x5`{9n zKyNCRFa&pDDut6eVt=UcsBS@dJM=?sw6oQJ!{YJcvwmKXkH^Tt`YusAyO8PI(k@kd zeegtBUz3KilGsT2F;x|nxnfB?6EQ}rs8F4NO88) zPhB*qa(7Tp@sgb?x0ayQW(oTY4g}3Jtv2?!T9=Zs9zQK39fKBEa=*0%TAdYJ_i-J^ zsju`k8uqG+PE=w)Pac!?u-JNFBUPpm%3dHh;Ve|Zb8wJEM53R#BSd!2iG)#u@5DQRZ0PTte?G#6E>3|g|J zaKCdq76WxA9p~_+*?xs;PpK|O|X|ceqfnp3@#?3CJ#aA9nwZd}S_XDe}{R=zk zT4y=mtc@gF$#MmExJEHBiz09`mSjZ0x|Hi#oMp58-pe5?N>|;MB?%FeO4iBxsw!2R zJhb`<#ci9GET}SjV%1VqMl~5_=rPICB0}FW7d)!NV}urmzzQ`jXPq2Xo-V-I0{4)p zjKa>Qfz@h>9hIG9ahe>(<;gKBQEohyp6~?BIfqjglu87^xCl4f5QHAtdKXGZ)o9%7 z76#?nCTU2Oii~}+AgsEoIYw(_GPTg9l!PCT+trkNofaG)CL4&0*+n<~71{<|?qtq| z$6mMLl^uy6DCVeG9nd07l1IsfVQDeqp;26Gts0~kVc{eRS!Nzlap4u~3ues3!aDD2 z=%#3QBqlw=m1NKq#wB4io&`)qMUL`nD8G)EafBK#&-ld3!4IQgD&uZ5{6VmwN#W$4 z=~!r?*A@jbu`Cg&R=^^@yMjGLtMi9aPBz%dsjz*IgxdFlvRm}#7bffbB;5gDR@g{( zT89G}_hMxkj@gk`{i=1g6NwN8ak`g3uR^bTZ>_7Jb@S@yYgp-_A9wu*& zKC@EgCM;G*w)KqWZJ`>-Lw2$_YE~#-yt)E5>I%2e#lGbe1t=$-szlSE2_j$%wX3>d z=L9IABQVYJbh1_4mr_x>9!nB<*Xq ztHd?hLvVauPNTF8nZO+RQ4W#HMH>_ro2LAb+-a!30XL<|aO2C_DYwMpYp}IJ*Qc?Q z|I7n4g0j}poY%Z7) z^c54>B3w`%3EQFV zdd`->^fB)!Y>$1_rmMFB-0~-G_(NsuQnIs%qCCwiYp2&?16!a1spnd$7OtqL63cU} zB+&yQrrV{Qf`a>QWL92a1)MW}h}c8MnFSQuB#Q4F4)`*NB4JT_Ire#Z8mefi*%dg(1v+^~UONC|M-63aMlq=M2pU*1QRY)BR zB;kf|L_I4@isfo6#E?Dd9G*q+3;D?9Rx5A~9@XtJ0(8lS9qm`n8(1vL6o5C&wH9bW z1Qyp;M^3Sa+B#pC=*GM*gcpi8!4f6#r9kv-yB&mRlw0l;w5C*k?3%fGC1_~uA|7mF z4IC=1;S>aFbCq&qR=(ljF|6;Xi_((wk8T&+_CGj!vfBVkLxcnE-D zR+sW1mW{BLBc`n+-JVnhSP;B1WCiN$~NsDEZ9v!86&bfNN~-C#gk z>qx43Rt+;@kjuYh#3C4nZ_mU4zj#6>IS*Kk>RBTSxa%Fh?@ofyeje-9i7})j8u?7nbR~VB+ z!()u8^faA*F5~(!<0{^BnlYRAOfu&2p2@}nuKkm76W0wRz_llhTHbTIQOC7^Ga7i$ z8AcQDInxMn{XAm@*S=w_;`(^wPOfK+Hm(gB?OY!=VqE{Ok>J`7jTG0uV03fs3FCgQ zebLak_8Vgh*S=(I9H|+J73)aP3#dFxS3kJjb=~8~eHTqVXcver>$OHN$v?YZ>EJ zuKkPg8rQPM>s-qj2GdaMK`)+17*XL#C zaqZd60UdYsOZGWbLYd^>|aqa0$h-*K}tl-*@Gpo4va^_C1 z{Up=IwO2CjT>IBdjBCHhB)I-uCdKu4X1ck4QRaTG&&X(8ACuX_^_iJ%T)!mK$Ms7y zk8u664Dfe(W+&IL$UMgND>MCEzbZ4p^{X?FbA49k39et08RYu7%u`&So!Q6rIhkj; zJ~uPW_4%3SxPEPBKi99zyvX$hnU}bJedZOe-;jBg>o;az_hhGX z{k_>~TyMxu=Xzsy2G^HlFXQ^s>{VQE%FgEct=V~8UzT0K_2%qNTn}XfTyM$Na-C=E zxV}8w!1W2)Ca$l@hPb{myMpWQ%dX=3ZP`1yzAD?s_1m-UT)!h5s{H`xPDLeb*^`34X$s@9^m?^*+X2vH+z`t_hsMY`u*7>T;G&E%Jm0wjO*{u zjpe$QtKzzzo5=N^++?nA&Q0O^mfTdXZ_Q2P`h&UYTz@DxgX`OJmvMc2?kcW-AUB)q zlXCO8-kV#%^}gIqT;GukaQ%b1TCP8wtK<43xdyI(DA&aG59dN$|442H*B{NT;`&E( zcXIt>xi+qUJlD?kow*p-Kaopt{U35Eu75Ju&GpA}_j7$$PUHHga$C6m>D)H1_viY! z{+Zk(T>otDQLcY3x0CAwxyQJ^JJ-+kJ-Gp{@6A2V^~ZBhaQz>1gIxc7?kTQ+A-9j~ zPvoBA`WJJnz;Ij(;>x1Z}%axZdyF!vJIzmj`}>rduh<@!J6UgP>xx!1Y=)ttfg zujLMK{p-0yT;G>F%=M>pZ*qMocZBQTVEyrm;fkY`|2w$#|NCCtP;sGG7@-=vEFqF) zyFON7Fs?sijOF?_jVi8x%b3XZZyS@jewHzX>)$b^a{XCj8rT2Xn9lX*jTu~jfdtNe z2ps);5IFkxA#n5`K;YI_i z=XD4i{agqf{Z|k;`mZ5y^l1<{dKLml&q3hm2Ox0tHz08I-$3B#2O)6uLl8LnZy|8> z|AN5L{~H2FKMaAR{|*92|2+hbegOoIejx;o{s#yg{Y?lQ{f`hh`kx?h^tVXh93g=- z9Rf%HGX#$QKM*+jUm$SwqYyayUm!H}J#`9$bCLZM~V>pB)@Kh8@E{ zYS}v}W!C~`oDmuxYrGRb1DA2_t$}N}_Pc?Vyyv`ub@aN0o<58x<8jLJRo*jg;2XRL zLppBkFqUUP&C}xniLg_j{xc!y=e>t}H&tzZ;V2>M-Dr!6sOLqKwhpIH>zy#P3g5;1 z!He*AzxCETq4%faGd}iNA9n z>7&C>&qltX20L@`^}L3*+X*d(4dpdJdDVteVMEzzXAZua2W8MsXnDrh=-?9=&2h0mhr`ufeLO-kPZ*$4Vr`?1a2Qyx;nKa3YPD^=8xK zVjvj}8>#|<2v|FUht0o7#b1*?e__%mvRNcbMW(Y(mSP)Oj78YZ>|%Uz(uss;6};c) zdOs7-YgwFi0K(OH3bPF?3P>B-4S;(on+e#LvK#U2z&t&#OegZ+i<&9_OxDH{tb2s{ zQw8~Ho^+s1Nwg)5w$MMTzm$es-o*DPYt2iWj5nt*4o+mtI?Y-fSQrPk2KB_yYWio@ zk>9_2(6=aC$J$YAJ|rtY!J+ncAg3D+S!B}q+D6}Zy;H2)#tmG}d(JWzgoeir+)V6Z zF+IJPSO``YZyC4ID;5=R4Rmnr_eKxBKE$=p419>b?c~}Y2KI98O=BM=e3KGhpoCYD zU}WgqZ|K{b__n)}Yk$m~if-6*tWqBT1GC!o(mnriv!TT~yoOL~hFCjC2K_Z!CDH`D4{d>6`Gs;Nl=XaPeE8J_GM28^86T#-qf_ zf3~gVN4Y5$FN1Lb>+Dkx2Ag|tMY^W`d zqP!?NbOaxJgOY6x^_}XxSRY#mT2pG@ex!*aEAvvc$En**llK`#>7rR=w;z+91P(dG zPBS4KJe6LjS+B?^0RJgarKK8%7T~*8#n3AE%Td=$F9fIA?B13`*5lBONlxK|Gl%B6 zUcIu}W$$-qw%c^l*wmz=7@MCPyCXQ*H0f(CKibi_&q7OYaNqXeGu6Y}gTn`}?`^TaZy8Qa zbG>d4KGz$3?#AG7*LeIz=IZZ>)TfTM3~mqZ+q`KXOHH(3qU=|<{9@7;P0qNroA)h( z%EnStdeZ}68PnS`(EDI5J{9zRgSdn%0gaP7#zQr>fp zaRW0^z>Og?fyLP8I$=c zy?&G6eV3lTPftIhr+*{JzoMVt(zoB!xBtOYwgOLkPQufkDfD_Sye72rmZ>4+?A>;Ru=-Sgx37zzMA0_nA(+BA3VS4&FW%(@qe38CAN#BO(>D%-~ z;%v{4sG?Ws^)-4u0I{-%WLoym__ntKPkYh6Kj+Ti@85e4?|El#dT6+E?@T=Ht>&Bd zHgZkd+roRM=Gy3cl=sZo`+nY2x%cC|XWZUByl4F0pYWcsCRZOz+bBZ`H-)HSv!JX5TA#}LZ7qBPWoE#3xC_68jt@^KX}2= zWv--y(}tSducr=myIx)OY!5!MdDAfKsbWuaM0)8ZBHh4_rUpe%;D?zrPH!x*K%Qh3-HX8z);((JGceHGQl)Uw_-ryFId+YE6 z=UOS6Oli^V@>BE5)0kc1F=khZF}n=Ei;p$-Pulv9za2X^G&`@j940yR?-5{JOrwFy zf28?PgqMp+Z5d9Vna`Kr_yXk+gfvD#n+kr{%bO zs_H+zL1s1Z-pTnxdT_!E!6#^e>m>atut1x%Wk0ghNH?X#c#{Sq(H|UmUi(fJ@#o+Z zO?Qbr+k}21TG6}U*vDUy4R>*U5Xq#ib1*%KwLUG%(wsm|lP7KcNAdm~X3z&uLzeBq z{vplXg%+&f%_*=y@4LT*6t>Vi`}O|9-`>Jg9>>=HyeADNxaS!# z!R&L$vgbu)+4BlIxd+9*y7vOE{b_GAe<1e|68Aog#J&H3pWLVE6-$q|_CCpbChYwV z8oKvKlt65HFR|&|Z|UiecrqC)3_eX_Y*r2(a*d&wO6xFK2NMv@n5$8Y{tLnVB>p#V z+Fy~b9hw3)+8Rn1q01rfc7~iDt$af_xe7s3derrLEVv)FrauF!*7gZHPOTk!&XqwZ zKANEDra<+u+3KpZ%QPe9G=m=PX1FQu;G&_+%E3rDFzB%)49wAKl1?XmEl_7Oe!N_@ zE%^C^=b%rMzNRh1^b=`#3EYhYZPVvZxL^>X0P4D1w?@E}%AFA8q^s#VvH;A>)B!F8`e z4)Oe_z|=9Ri*{^nr#3ad=2|FyRD7~{P@k(ZH1k~aSSqeJ9-h?q=3j`DPTFz^e*qW* z<&09idy3%QlkuC+^M?jpz^%p#1r=;tHgn7Hq%G$I`p|G**8D{x8L@g>sw@b$b!h<0 zN@wk9C7ULtteuU~Do`L|2v3sl=dWn*?RvOk0Q_#OaWYuLz!_i- z#`W~H5KjZxlb$dDUiZ0yC?#}ILKi)4qNlC&1e3$~fjyLY2zswEOt1Ut73$lB0qCxu z8#qk)N^{qq3A-;LemW0XGF0zws(1H;;L(|0di@9`?4hSG(bHGyNodN%7|ZnmU|wnlD$9RJ(mzeuH5@G(u#it^*BdL@ter8_kS#N^msN=8WO*Y z3E~(#uE28whP5{QN5+?RFbI-Kajk9ma*TV0`H}@Dve__|S-IVH5RadZ|1ARxnaEmE zXTmn(NCT#oPWdl(GZ}3GqN|)?W=wSf=wET>YrP zGa3G@zMqfhQhm?wgB#X#fhjTxyA6ypY&e#2+YMnlyAL!-<3%P-ninz94E>S;xN|>j?BySm-uHfF7oNaje{z@ z+{CVNQ6q5)r_MzpowhyJfEpsGr4@g}$lob=Rz4pfUg*?CoFu=!(i~3Z6UZmGZVJEM zh*E_0b*RZQp5L6;CfX*Uc0|yU7~qghXcawQC)zZ-fM31C>_f7s^=?j8+;*zenul1B znoyl@q^;Z}uueSP*{qS0#K{NsNbnW6JRzPo9egf|`9k?==Ed-r{#kVr{ORbMH4ATc z%`JC6;w}042sct%5C^v4K^-f|XU!2Ok8GF3yu@sWXrH@2lAWbS$f}2EYC3ahvk)Ib z`1k*8%0hn%DaA-@8h=pg;S*Y%7 z1a;-}MvHek+)<{p{&Opaq0nH{uKIho;$JiGJxu#-e*!SKQ%dho$ws)M_tnjRIrcyU zJV$O^ab(h#9mplTNZuTpjAn}E;tp(yn0_lQN8oNjRxQ&)2B!-0aFKg%c-+3?$iYVO z>8ON%Q!iGcl*0|~uni8ohi|&UNe>n`ID4h`S5)eF2-v%PvW4b~)m(zTDjiywF}6Z@ zv=IMXwH80SI5SQU4Nou@;b-6$uKi^IM*PnXgn7@^197|>AEJbhQ9?f@3{t|kDB;JH zfc3=%1OLf;W(~YSPrsw5x5NVD{EA_WNDLuF zGQxh9#Qriu@)|;Nf^fbyG<>7+VM_fty<)rMvjZ@^T|F>NL+}$y_yr*zJ4mwz4${*f z=;_a52;LYojLB>dfuw!ma75&PK8T>e)o9WOzXoiZwD>sr#lRU$+jNYP90D$P*J^1?bK?57|ujjj39l*A|ds-iokVIo^mZl%eL-I2g z_&luIE?9YJeBH`x)8`C)L3;#f)N1~<9EtMTgvlOWvK)#m{vgOS9ka<6Ala&@L&oBL zjq@(@ukEz+6WXTZe+wS*?fX{Yl20tAEM8uPZ>z9krB(YXI2To81xx>|GNhNRO2=lq zzlnOHv+X?MeGRhg`gcZWB2I#>zUQm~SbF|wG}0@k(_i48hu0*%-b1es(Cf$P^)Y(= zJREEWo+M_5jmIkTqxpmJb4vXWO3jk14U9{l-OVuVcAtiCyU)V6%q7S|`?G)94d0cs z$b06b-7D~#xtE^aPy4tJQ;A*l4d72A!~9<|`|0V2^z=)5`ZYZrrl%vAyn9f`tUXoq zG?|{J(i3)rrTZWh8{v3zE-;esc(T25cw5U0y|3;F?ssjR(UC`r3D*E@^Sx$℘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⪼Y<|G~wt&<$p&x_jeaeN@De%GlfspCO^}um5EG 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

ymxjktckgAZRlHy<_scjw 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âx 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

gNE7f_N-q*yb)-N!pfJ&9ReD?!EC--F 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 literal 0 HcmV?d00001 diff --git a/Packages/RAD Studio 10.2/VirtualTreesR.dpk b/Packages/RAD Studio 10.2/VirtualTreesR.dpk index 205e582a5..8ac309128 100644 --- a/Packages/RAD Studio 10.2/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.2/VirtualTreesR.dpk @@ -46,7 +46,6 @@ contains VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', - VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', diff --git a/Packages/RAD Studio 10.2/VirtualTreesR.dproj b/Packages/RAD Studio 10.2/VirtualTreesR.dproj index 0747e143b..a38356722 100644 --- a/Packages/RAD Studio 10.2/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.2/VirtualTreesR.dproj @@ -91,7 +91,6 @@ - diff --git a/Packages/RAD Studio 10.2/VirtualTreesR.lib b/Packages/RAD Studio 10.2/VirtualTreesR.lib new file mode 100644 index 0000000000000000000000000000000000000000..c5124f08fad28c9f74cd424de75176fd6aa6c5cd GIT binary patch literal 1165312 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Ð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

1Z@-EnVZzL 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 Hk;fgm+h+lb!y9!oT3c{ijO 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

Y&DtMnqMYwa|+OnQ-#ILnZ?THpN3kCQ69{1V>K zm|Xc`6z~{D8%4S&aaIz#9nJ!GXH!42r6ME zj5KO14Y)S*BRg{kSR|>QXPGcQN^UzI_)&?UX)Hv!WT#YO|E$7dpkz;3#!SR1wE*cj zU}PokdgET?UWPK}L8$V2H^tq3yn2aUdDYG7_*opO!vm-u8S+#^u-nU#Ir?gZnQB33%DWsd_ZA5N+m=x zVgtLjLBKBq7vNM`C5^R3vCS2s1gp>*({oWOnNbTcdKU94{bXJxdv}3xA4Zar6U)K= zdXKPs-+`C~2vI5=3ZN*4%3_XZemxImTlEycPsI-b%$<_R=w9Pe)Txv;eO-MlEnVJ3 z`2U?GPed0g9uA{m#|X{_Z{lpQtS3fA6TYhA_GsMEGEoY+YuG8U=r{}MMv`jps#PsnY<<<>_s>{Tp%K)}Rc zu3dw~dLiYuSfvqag3yys(qlb*_w@aY1ZD;4$22J-9s;mBGXj2LBrcGE&{nct3Zn$D z0DSh~fed6BpuE|D;|%{i^K;-nu4wUmN-QD{=nnk<6#v0Lp(u7ph64~Tq{E7zC5M=Q zbf~=D2%JSLhk+MUr!DEq%m!ixV8uDIjKo+y(Za`)pGlPey)4pmgecEjmN*y-5u>ycP@XRQG#Bq< zPr+5>blgm}qm0C5Y6Kdk5MW~T2vogfhBhzjA5WZgZ8ZXfmom!IwH9b>D;&|*n)9(ZfU{i zlKbmh&>>~C>oPE+Tya;Z9{);?gY=4fLXLsQc?_Vp7aNHyIaj#RhLWoITbZn5xILmW zQ?+ox^Cy(WA|vsARs0eu9s_ELGfMG`;cjr;1gZur!DF{XI>h5?nUx`fj_;RD_rTS- zFf{8bHmgMmPLA)^D*cw9D6J}iYp za3g^`hTZK05UMSngAjw=GC(>|FdYb0vbG(<|ARIzHJ{yiEDx@Fm0K)sI2x||hBA9g znT0GfPY|@nmYXZ(y1JDM2!>snqPp~V-6g${lhUQVx=UD)r5WS;j-)+iT=z(h-Ei%3 zz1B!fMK&I#2Ov@o;b^`gu&Rl=enVE!P>}}iCK9|$7Sc|*EGVR{Mb)04mAGCHLA7-a zwy``SCfpmtB=d}h-~gnxD-SxbStm?v!_@!_kcZ)YriXl(6RwuOR9zju8aad{p`vzL zk-;r23zYv?kv&qn0a6{X8e^6z5}N7RP~)jLJji<@)DnEoI>TjG13xIZc$?X}{M>5nS^yYvUMg*U3y0;8RrWAUfK6S51O z=L`Rw9B^x+6MSJ49N87?iFgZwX8_N|U(?w{=UqC7;lxg*Q%dJfIwqa}r1LVJ6r8&L zD4=c_3N>nTj09eBI2&q=#B{wnj|0*OQ1PKG82`mMk;xb0zoNm9$!T1vW=P|LGf5dW zH~WF{&!AC}5_&**+Gcvw-9&!t7ZeX)ljnI8)1eX}F+B=K-qnx|3qyt@(!N zun$o$PlvVB;PRL&F%mPSE7NCg0|C0k2x7MmccEUOAp#A28u+1E47m5Y3@rEqRkDh>OpAx5Z;ePRZ$!e zBvWr&wB^r=Dh;G!_QAObY2zdmiSAcQ#H5RQPVyY#Yf}|<#0(rMdj2T{3kdE4vyiPy zsxw&L2`r6Ep{Z)5LJV+C6_dAkIvt%=Z(t=IV}3I9f?%B7mG>g9v&yoqquwi>o`aB9 zS%{;tvuYROgYHUVy38tt;B>)sLjp}VtDZ)sp%uBP{uzF&9w}Nq7450-t1FvU{;&jx z4b)~>!h;#4ZwyE2iNbMiEg3}WFpRL9CEn~7TvzZ+%f=f;0%iCRc)+eORDQtl=S=7- z-5YpR{CKMCSScv8efz^`F-OzC$W*ef8n+9J<=#jc)CExBb!qoFp2eOWxQBO06YyBd zED~GwVu5l%VWOR?%(xU-4nTKe3=X~S`7qp2f%Tr%p4s?@_OXP{k1q$LC8dpG>!?49 zmaj1{YKa0hOU^(+TIB`qH|OZAK)>!~p@lz!%*J+1Tcms|wY5-YV{7CWJ2A@|S&Z+- zok*ZR16Ox1LRBqe4=i|*ZX~?lT6ubvseM50?@~^rbaMpgJ2hOUXESaj(}NAv7AbpL zPea7>G?M6AK~|z<1#*x-qk;<`DxtBV_(_y6AfnE0$KFJYt^Hf~cj|41G{ZS`%E%H1 zTR?rH0XWGwNr-&MS6LA~2&+vnd=WSA`}n!i zS2Y*9UBMytv@iT2HBYt0{=(09!A=(I@Krommq!Qcr*i6W)$FgENFQiC`|C*Fsar%} z1$flz)`RV=Zl|~T;eFuSm{r#SjQYMK@U71VMt%K2M65r}S21b*C|G=1Uy2;-=c1+S zQNZ8UF9)~A`bT^f@5qUH0}eYJU&12`=I<|0ZNlQqT$GDKFD!FmT>SlKVg%nZtNi%t z`o%qPF?@<)6|~SCu#km6lK0k)EK)epEY8KymDI%w#D~#{cQ!!?%`>A(l=iS2tVwII zmH!0{f72R2fT$nbrERw&>B(5 zt6L%(f2A!Up5t3qpt^3&$?QA}Yh(&*f9E%>2Wm#F1lJcLdmCpgwDmYEmd9LMz!1qW9roh&~O)T*Q3d6x~cwmUetbyAa!2h%lKWp*9!d zQd5a4#oetc20}w=q~ z4bUNk(C+m&N&PdV{?os%{)4;Of^)wv+N4oW&k@knGTNXM7YTH!>=sz@OxET zDA`%W2O!$gmO5YB;(vFO(bG+8xx4ltm*|R7xxhP*RsY z5~(V%L-6I9QX%79vkKCc#}WH%jh; zXe}V1=-&ytec`JZhI0>=WB_Fii`_=wa{9n4( z6>rtOgNORQBnn**$)C&DpN`7cp9!dK{aD1VpXm!9SWgIz0M@qkeqZR*^Onc;7WxZ zpv2rH*bC-DNie1c7tCBBO)xc(OkbT`&e#=nWX^s84g}g*08buX{WmL+Z-xuSr$Ih1 zK7n6=OOS82k+?;X%iU)mM@ygh1K+%*cPxN%)(P|GFJAb6r<*l#OY&T;n>BH(=w@x- z&kj>J>(~3fvu@Vz_um);x>*zRkJrtbxE1Q0;9NlGVmedk+~7s`YyZPY%=>!XtoN|B z_}z>D=?Rpef5E}|!yoXx-ITBz-#+S=o>ee%f-hkn`4Vo0p4G%{+|To%XBAEXo&TSD zR-ZF!fi1sHt%jc8wOx1t>EAc$RYhYVTsSWYZCVS2*>Qf_2yaR5GY;|wac|KQ@7S!w z0u8W;&II8;7LBK8;7V2i%QKEAS*z@|6KOm>TJw|KF`eqs4hG|u82!)-L2fkYXRTTR zPX>C#oRF&uYKM92+#ZZQkyerkxy2h;#PXaP`HQsX8U|FpaqmTDHM50Qz79*+XI@)_ zr*cqgfoI}QC&ed_+724GGj%cnF(_=RDeG8b{L!J&3;@hJq3QfbsXOiT(zIev`>F+I zO9K({G{8{#1|(WCN{l#n;x+~*xn?XJV3lj8Ice|!N#2qI9MS?x4az4_M}ob{Gs_Q5 zLMml&%jK_0`vce76_8uWD-R?~x?-2PA=Qn98vf$5=qy zFxP3@Fn=}@cq@P{0J4dMslkKOQ8QO-_Mos9`R>+XwW-w_1pN~~f1#NJoF&OYvjBCF zN3B^psIDnMQZ+fLQQ+{xqEQh*xd30?z{j>EglZokxQ?jw_OL_|9ntD2kX%yg3#d&E zjZ#&1hCYLukRCs1FdS#V>|A0YE}85ourE1K*IV%d)D}X^{~ZX2mB^}T(VzG#=R^B4 zKG*Pi!QFmc^fz+HfN6OV?idgfb#p~pa5OdoF&p5*0MoKZWWa9EK=yL9HrLGEP77XO zU7~P4NJr+|*bdw$YVaPmx4M#3(cMy)vHr-EBkHg$?qGB?wl-fbZPR>_GJyJOU`)?; zEw!=}{Di0v$bRx40#4_o&9v(!Fcoke%mEY9{ej$Vo+Od)VgronNYv6ymAdc1MR#-t z-$ecb*tiY7mIs4q3?XJXC?N$YC&FEk(k3z1ZncMKIMcHgThdzF;OP?#CS+g=&@OQ7 z$(#aS)r2$6-k9G#@^VQ<&0|XMa|z(oLnk`PbUe5zLHFMQA>Q$@A+Aua!mC2zn_P&_ zZA3}aX2}SmEAEX!lHwqzwi#b~J#MxK69f^ACNNX^=&-dmQw4I@%V7--J!L8z2D7Wn z2ucxF%Ehyh4JCx?7o!p=FpZQx%>vi(QByV6&=0DPh0^|71iTRI(!fEfsQth3Bjlf{S7A#m0RFZzweIG|-Ig z;CkKNw5;Wrcdd1wa?2*$bo`zovM~bsxX4Zl{aL!FSAVOevxwKjx|uuUw6rP0LV z=oGBr=!00n(cfZGM&IGeyo`M49`N|ae$7u99y&AjBAxdjGZ*^=>z0dm;ywW2i}&@T zbmCx0&()nKi}Eugafe1Nx0$=Q?84*#f9fKF2w`8uuWDg(0eV7#|HuI1;RA zw|skfr8~g%=241YP2I%8Jjm7qgPRYuv$Hd#mgZnny{=)isj&w}MW;?(+8I&@bY=oq zMuL+vGG?X2Lt6*q;AlpZ@Wb!{F3bG6@J-pnUf#qVQScn!$amZw(TnhKOI`sVC9m#? z&hX(W4qC?$P{6|<(~9dPniBt*ro<;jpQo<{KHBNhP?d1*HR-*&aIu-;BrV#;b1sR>c`S9-yrtc=51iD0Y4`B)Kb9V#VR zunLX=?8eevHut;iOj@kbw+i?A^L-1^73fe3lv)*@d_GsDnXqt`M|X(``Y%jAR9 z(;*{2#G6%ALx2op<^N8z;g#JY|3 z$GVN3EbA87(>wLZSLnm^2?1H49SaidqNA3lXd$TL>J*KXJz?+KC+t0bKD#8_P+Ei=6J#VGl#C$SfoC@I4+y%_=f-WA-01kw)1oNMals>}ww2lw(qTn6| z-%1BDU5wzVM_m!sVPZ6SYJh14B~wJ}atU)+KmnG~DSoHF$SMmAj2hRMCv8SHtdJ2DrxnjxNsg){S^kmfBnRKs6=rIr zvoidX2(U-Y$DYEr%!b^4I7*K?9U^6qfN27nQwa9)s;~AONI6?P2Y?mTP|>0q&nMPZ z*JDwr^b?nL*U6WC*~wQPZ+fAWvVDmwQs~ ziU%>zcowCq<%Y5oxhr_^sS`pkm*1$JTC1+YkO+r$-G98?RV9$}zs4E~ZD9tG|9Bu< zz1u3iv-CMeO1DPJo>#o7gS~tg6@n#)V`>m;a6GvAaMZeacjfnNg_AbcIK!g5@XwU|P zfju+}hZ7WWWQ<_yJ`NCoBQ?WnfKs=~7+j;~;k%7N>b#5r=spjrfhU**3pDBrb)&r9 z?WM8}A8HLMy#mk6*h0<;^Ua4&W<&S?iaELJ|FyO*`uetBcY?O2E|EGefXxcv%y%39 zv%cGyV4*^CY^!b~jV*XS_7Al8=a?6*9g5eu26gek`b4C+cnsU1Evn1Id#k-0blWYd zhGu(eXjW#9QZWy>P>n6+f#p0PocSbuw|^$ynuer}OmZK} zovU?#w!l${?Q&Z5Dr~jrwb)V7Ill1yQIITNF=}@iiMw>`$Q!6M0>ZE_jPcH*)~5{? z2N_-xVC2NTSmJU)gSoUnLvU%Q+0*Jo>U%mCBW`yw9ec8hwfb&Hm}PkY0VJ4X0DvnU zfH5k~y$aeLR4%LcNYnH9-eWQ;*F21DHdpw-RCx`D;3oQRr_aw}3DReBSf1dp{GP+I znZuHhVOi)%+)YhswDQv)+HwG+ZQPgNunY_*jaS28zt`$p*Tal-Z$v^dX;^?UntgM# zEf_Ts3ICl0nJl&i$QBqL5d*s(LSGcAOS2win$hXbI51x`9w?@S`-E|T!evZVfd9vr zL{YN>gfBzv?ZU)B>l5dpcKj%Z2qDtDVI?4OceFrw1ST4Z9|#sLw25M~Mqof%qnV}O z!ioaDlDJ~)tz$(K-3GmS{i)*Z6mNWf6xMh8MlXZUsNHBJK*0b@5tP2F^_&OJLmsrc z6cAi1O?-wnRRbV00xN=>9um(FLYq3c4gyzotFf&DPR2&{ugG4ai(()Shi5mSf6V zf-kzjRSJgR@hkx5jxdE2;e~z$xFp}*XC(YGxq{58$p&x_3rXrqZEOeXQcjEC;mR=l zJ-YgIUFU?~3H1(3)HyOLdx=Yuj7n!aU}fvvnuUbts`%_B=UEA5c19V|0lXWA`oeG1 z=zBf24A%4L$%%rZ`!?UT_0h#*x9dztBA`fLymxxxKc!>o=l?$Xe;)qwe_#CXr~Ulq z9mW6o`hS1?9gy)2{bFLpkz>UF03i+TBkP-Mng3n-Kb}zrPipI%Pj~+Rw(?2)IJ?Qc@y+Zvvfqg0&xqvz z1;b!OoC)y%yFmKEpDwdvO$ingH;ssS@{GdqbXxLOU&WumY7pL;toLGR)b=qF_Zh~M zEI|kdNx;i0c*U{7peGbmIu(DQbY-8W z+yyZr5v2~CEk-PQg|<+}+VH#anH3li+A@@%&3NQreGl~J; z=IN03dAqI`*S%`PqZ2b4p$}T`GJR3>-o5yatI-!3!WOW9gC?!!6UyupD{!BTyo`%?8m^;GR*Bk>@c z0HHLP=IX?FOqF`w&uhB$qM9tPgg^yVwh&_iqk+M6)?)JEEZFbc7V9z!=TQF-GD>qf14BAX`ey|ON+8=S zS**hqJwc=MQF0y=pv{k?k+4XSXYe72X#YSNmUIF=BFHkdW(_N4YYUJXx$+JF_s#sm z%O4~fJ8%-b=Fg>0WCtQ^CB_Ypj{L%ENuUeP*EO@?RDUT2`dyucEkTw7j^eS{i6s~* z`H>kk&lhS>-tSF>V3!>eTzf-yqC!qFfFWiH6g2KYQL>PT07hVF1sE8Ba&iS%9HinF zdO8j3icva0-5Y}3!(Bnt^U;^wmAM!U#0G!-rQxo`+U5t;gF-`g&GZA?u97Oq z+4lz1scNWo9bPme&Hezo&-g?8|4St@r-5>$;?;(fSe@HlS%p+zSHhhv;lwSN_FY;~ z^qpEjeA?HvU~aP>jlhq;rX5_-)<_Ii{j``s)F`Z13G7iA^Wup{;Q^hh1iC>p;`iUd z@Dq=~)Qp-bGlLnhh6)Q+me$}`P03>?`zud5|@Zx2Pf=3;8a;1)*3i* z??XD(F}+XSeZp$-!q-)ve8O(S5)ek?n`#UUI$=go9@edr+o1$3d3dSdebN_()!};! zzT}tQ=HeJGqmyIbqwhxeTFYT_4V1LzJ~1HkM#SERul1m}`O-S1%dbPaOY3IA2koBC zL+gGC+v|1D)Ats9`@pwy;rf2w<^dE@xo|yRNCV_8P^lr51BysQs=)$`kfIta$%&MG zK4UQ~5`rx==F`_|Yn(XZymR zMPWL78LyYm$z=Le5~>3$i6OlbHhhU4f{(!+Au=?MhC^`9(mAOgWsqMv>;iNw z@_gVkB0-A>AQ0MD%Z4X4>Msa@)}$QhOzM>yL~6+)GEw8KC(_|7RE`64 zGD47*4H}*0XVwA2h9O|yIjW2iR9>a=Jpi-a-9NQBW8e@)4gCXxzc49NiX3w^`pPqh<}~GhjJ%?8U z7RYuKI1RQEb-ilO*&FH-hbKNouwVequYN5&l9W=E{VS5*?b$5-W#Rrsow$Ns~csEEDjtC}BspAnz>s(wf; z?)U;tQEx6>E?UEMys?dzbt{eyk~KyB-D zd{sBE?}wee9ty^>vwxVCsJ31J?dCsfh)L8s3IXU_oD)F{mVhvrQ$^?b3o@TE4PGq2 z#yQ-O$C(TnAg|t^4@D%Qi92}#{Q#EYHZXOGt9bEfD4uo}x}Ps`s>$6>17ch^ZAcn| z*-tP;swc;GWEt}=Er|Lr58dzTT6M8GD_xT0?ku@1%Rda}MuXU{d~#SXnUt$@a*o5F zWu6Yu{YOgSqW_0>9}D~oGaErsv|K=ePWKBldJ|H2zn}$S@kWA07gz=4GiYSjqdRoJ z)?$*)hwIrX`<0{WR;vhbHSEmagT|KtSc5vqYn-+%P8H`xTpo@%OMSfxz0zu8|DgXO zF|E3Qbqrbz7tl2be>P}6kb~8)dwzhWz#T2N)I{E*R4^9z48SI2l0{5*fol}qu{1zp z+~;7;;S)T;R)#Hw=Dh=x|7-NEvr47}=PXltH&%5@iV0+wiqYy)hBg+}yB z?tuy3L^a5AiE8k5f^%g%4Gz4|tJY0ctWhMgdA;%FV88+qV;<=Kv2t&sCbpJZ75{*2 ztq}04j=hP`ePA4@-gg?DI#B6y>L!aJqKAw`jau8&6<)@@@p`v=?gQZFN)sjjh6|UcePxn#2qP}qypKQLGBG1Rn&L30$(h}?0pNs({yaI;@x;B zsX%pKq`aAF%zJ<-k;j9I#Sv_;(cPiLM$2*oiUF+(Z%2&c<7jM1KkxK(ZdOH2XgUauUUM25F%v&p%w9 zOS+zh#mFXkdA?h)h3CjD^NUD5c}QdY-D2l=`A0D!%SLHDuc}e}630yU3)lB{zkt(5 z6;pwl;mxEyLC787&swx7e<8jzcE;C~F~KGGu^fgo0LPE+oY2D&fc0J6M%KpW+}fZ0 z^Zb{ZlB@VZZeaJLo{yynNj=`3Qm?td zaY-f79%L)dMui95&reo|HH@0vgAoP;MrWs|Gt1wn#j_vS?}eTJBsOzDppN|pw9lrS zpO+QLl`i&%>#{EvT7E^!@j>9pAR1K+;K!T*Gtzz(ZVp-j7iS$uG-FhTWPD;&aza0G zb=_~h{XJg*iP+KG^F_7i^Ne(EzU8Yu?e6D$tYo{3J)Hn9KMj_DZop)2w%DDpi})l+ zMF<2G?OGi`tfW5&(316V&lmo|ofDkS06@S`U=jC3-D`tF&SUabd)glOz5_!FB*p~X zXal9g!ORZZE%^+YWp2hnVTI+#+0X}_{NiFqZ%mVX9-u@Q$gx)JoBY!1ZPrYpxXt9h^;Er$K!Z2JR?5sWa~ zu{p3oCM(i}FmE3*jddYVc!857o1#gBb2n0k#{{(j(om~-3xx1VPIVJ@WMY?@p!!!5c-Z1yU9)tNIl(d*1UIu6VRA1%(^#F&y zTwh7w%eYI{e~D{EpGMAVHr?tAdcVkA&h1>Yp#$L-AIffO`okct$xMvykz>QNTjqe# zW!r1kw6VCd9W`qXz|+94Z;N`HxJ{8wFR@SGVug8zDaN5x(|7GZ1NdSVT*gxm8Tt6`CpBVl# zP9I*4!s%V*u6zzD)sAg*SFXng_nx+Tt9zm#3{}`8#}(#&zN~Xm z%^J28%dj_=XVHUC;gkkOzJ8kfh5IIPb%GOccs#!>iHa-^J2$vPrE;d;<$m!I9Be8qtZo;RA2P^ zli{9^+!ZMaLXwl@+~HeSPmsIfHN?sHPOOPMsFrwsESB3-7=`*05aXo%a{3hV9QBv+ z448%t8K6Y*1Xth+Ij4X!kt=QkI+3}*gXJlJiaN3$HOhTWT)!9RMQW;)r!FPfK)XoF za@%RT z@-K|QP)>=v;{5-Qy?23+s=V|6Pm%#61SViaM2Zm+6}%Q&FVPx-0AA22lTq+q(IU0h z)fu#vOLTG}Jw~Zm+U}~2yEDP=c3adgCg>teD5$k9wQ5mZMI96zv{7k@O3DBId7g95 zOd{I$_uu`$eEs%6ym)fXb34!d`QE=wdN6jHiyhJc=(}mvWt~eIt#O@8+j+2i>cend z1I5gs=<;W2AlQ#ZaSl4&>Uv<7PT)~6&usD<;NUV-LOGxX=ViW6&ATy|&;QZJe@ z#hR#DZV_2wlj*9lnMzD13tP-mZlUE~HMMP$Q7nj$_Oyg-k@l6WoP9uT?n*Vq2v%{B z0>x4p(CS1J$yGhh>@a}|-o}X!M@F;@hFsO3)!pE<%o=m8m|3>h*itrv**(;1t~(vz z$1n&K+&KV&v2*n$-!qcj5mv&<@J>Us&CDp82AMu9`x38ScJ#bs9LC+~s%$K` zFEqDm?C7cwVyCZZV-!577zmgX?Q?}9bmd7^xAoGCni5ygbQOuMR`YmA_fM0vj9p5Ejs#i{eXZG+kF z+YTT|&5Nu%CG`xVFw#llDs2jXl3kD^Jp_sF;Njo3iqakdld^AJgB$eN_=@lu!QXd;|XFg{=Gv1z17N}TlF z$ZgdwpiLc43l?OT8gkMFa;^*TeMZG&0?=b8bxj4ZKhd2k_B%8r`Prr37SaHj54K)r ztxX{A zFRAJwhgcP{Q0k2z&N#e4@OdPqYNBBB;imzQ< zS`fe9r)v$Wj9uwoG$Np!2E?x=ReSML5DOtuG_ZEzUWqA9EXWp4SW7>PPrYm}$guZz zLVinPqupsE!mFl-gNm}H{c( z5%O|s2Z$xgdwJ{z!b*e+G$;d6ar90yo2ix^?NLebk|)10b(>?SNnsad+UdpE^YQBr z9_Ug3p1U;lz8e}@JTP)E>zPGXErf(LAH^i$+AnX49Y%qHLFw#JGF0a1)-BW%a1n&^ ztMyk7=;_(p_fcDWT0k#UkYg7PcBFLD*2#-cOvT|sUv|VQz);7Q+JAM zz%A0aEla-CCx)W`U}5?mZ#l^lgQxF=%673`uJN+=fv0juhyu_yg{D||W$G|lru4l& z(;3NvZZv7U1bAOI>UlOX>v<>L3bHYr=M6o}jgIhb-T38|Fl;=o*FK%p_dA0P1~Q`O zZVh~PsM$(f3NjQ|(5N`J7B|_IynDTobXL`_Y=3v^Xz5RRrZ~gpu~Lc9gz(u-q8$_1qZS5w!KZojY5;@DSB_)?vi47S|*c{cQt1Nq}0m-pYZT z1V8l_yS|>(s{&ha*8*dG8~be5&YU1H*=;i-)l#xVG3Oq9x}@((9s{0~tdNiE=`g@# z?O}u*T@Ow!iL5&We~;_JU}dDP6nhK>zw=U)#C!i90e)2q((qj%Gs|11XVXzEXoST) z7Bn_t%i7P@iocJzwu#J`Sq4%9+p|wa`#nu3%(My6!o3O1G1Xy&Llnr@3-osIJ4R7M z`EH;ykIUr%l_UCK+e}YhmqIOfi#qJCo@(7r_!?I2~1n2GJ-SJDwN@qmqpU| zX25ZmW#Wp%<=~~LFNnp6Hs-zFf%Ho5K#~+6+sIgOKrhnha-8N1yN5+qMVHQ;28Sdb z7g?((K2%6msmHHrN+^S)Tx&k|PVK^XFxi=ST)3cdIR*K$DWNyq zSe;kjwczHd8>>1s8fphxnxUm`B`&Wdr|cdWB7{O+VVXSL%Tcz}Yi)&TZTw5-8>oT? z?~pm77eD%Svfq=P1NxLd&tK#Me**64U0!an@#M#mOp*Pz`}HHgZi%Uoe$An8J$`1 zRawT^S+v2mhGhYtN*-=H_l}STrp$grwzV0PnmrYvM{atK_#3hZO?7`;^mNhFUMZg- zP$O>pPIC~`=dzUmVqo!8XlD;+b5*DRndZnH#a`VrI-92~3Ar8?majC)4sOBwBN(+y(U1{Uu-1q5Djius~`UvyOZtF&<{ z8~;}RTSQtek>qws$Nu5YMCWAZB%WlP^BN9xIc17*ln9NU>lyacu{cR7Qcmctwln2>IpuN&-McaCl@-8tsI0Y%5A=MN0T-`yNbH?+^L}gQ(0$}~HnanJ19|U$zo?VFqU`(HD+v{KmnpgFv^#pt zE0;VBQkcC+S=%Qspd*fjr#rOxF`@vf9G0m)7Oc8~8K3s27>CuH2rRNx zLYoZI2;ANKcRyy__KS2}k{rccVlRPyPtwJ3J@b~ljd{b?3UU9&#URI2%VnN*xzqAe zLHdX0c3@y$X3rEOhjlRjYxX{64S4GBDn7MC?Qzc>)iilj)2vZV=ZtpG9MOb}%YvpN zck(C@xMCb$HeEb6Thw&%Xm_2Q6{}beA||6r-M!->Q+{;QN=3CT^JP1-sGM+mY9J}7xQrnkIfDwXqqJ%vB{$q(3}zcr6GJOHYr#1 zB=^j5wkY4F-Q)6$jxyybYT6IJ<5V^kWdXIDiXN2U!0t_lO&*|&FTQ)!i9|KQx3FgQ|$`TJf0fTBNTH5}^jW zQ%7K|m894a8tC|;p8bCq?m|{HWU>jJ-LLlU7syorreA7ZT|c;zcV=kn0O~-pfy=vR zx~C`rC7!0e8WZmJ%Qd%>xr`B2lOu{;nz z%8ia{svc!DRSt275p!Tu=_I>}x|DmA$vsZ^s-5jG`jsYJX5Y~6HC%GmuJlcgZ*|&2 zy;fONvA*5uPh&y4TbN9zl7(bE?_rkh$C)t%$)g1v|Epf#AV?jc4RDk;0B*dGN^uPq zrY>d|I4$#>^gjkfrOoqta2|IqG)A4$9z?|CR_^0a4{ewl2Ij3}K|^{4Xf$QKlO6Et zw`>PP-a3u-Ybj0AA;P%I7^elw7F-w0gJJ98;0@epX1kaKtIG*QzS2o=sDr#)y*&M` z<^TNyolIdQeP3oj0;lCJCymv-00je6s+o(1l81ivMwPrPXVHl`Kv4tDA;hA$e{t%v z;Gn4!0Oa<#BilI1iXoJpGaxkWyg1JNJfY?URrjvc$G^;qF(zauO5;cW-C$P-WZ~f)jF(n z?_aEQkVoU{%*sBfelL~`F&>T0M)~}bT#z9Hs8IQ97sM*(zx^M6ECAHq937#D&Qw1~@gAy5o(zkdU zM#6p27_V)r*$ZHC8FqIhg;yK<&Vw2W*PC`@gG0i4`Ic<*<%f+(wKt!Zv18}fDiGM` z=7vBV9T=%Wvfu5cs#B_)ECCKo9a^>pY^C?q0ONkzq)n%%;%%Qr9^^V2B;?WCg?i*b&E&Y{xuw9GL0cLnJS(NH%lz2t6>viY3z{pa<%ZN zUM=*#(|&@IPVSwQXU}evm(S~iS%yX+k(wo~)T_BWZZcoh;8(_Ou)@`*M0In68U(Vv zRe+r3!pn4|d)F@F-Q}GM%?PQ<99}254MGnpHZLx6H&1qdJ^m>_lc})xXFp+7qiZVZ zgdALB84kxen|pg*uiCs?>7MLnd>RtOypH$n+vgAK!eQR9N^)U*hc~2)%i5>&-e(l8 z0mEXH1bWq1(QqYs7hof%+gXgm`TUw}(!upx79H5U+(=0nf_{kJ?)WktG3UrV=kmlb zg!9(ULw{2*HOsz)YJTaHP1WA~p$?f_le)ff&I}iV?PFK@4k=M}H9Sw7aG)oeWnO-N z4Wl#UM|;(x^xt<$WNVPZYi=mz8zvLhH8JDVW>>zA;jL=BXRYO~hiJaLTmn+NUnbh* zkN^}`7H!kKjS;-uUsjMgRE~kh$}!MsaQ`6T5^&0WFL|W`kiYNgru|x`E|PA^G!e)! zP5QKqHlMAm&hbtCrZ zvA~HGPLnL#{ByLfI`tTg_tb}y7XO$8R3sX-PNggE<7+E^CL{N26kvpI z%ZjbCbpNhWKUAtdZa9T44#Q~FtUXu1yethB7wKZCR0@ZK{gVqxGCxz&j}}a(AGrm; zsbfyd%TD@112tMFMiqO*b^k#gECYn=f5t<_V>%M!qlFTgY3!Od5{uff89i=A8&5m# zFgbN9#}Db1GyYABAt_h(ii*d|dsnuhaSRa!sxy9OUe2#nJSGlnd|*&ncC>3ea)Cq) zuek9v)ak9dV|GBrjbq~d1ci)Yb^GiVOFBWe{K&|<>kunnhZ&s6x=S!zT>q!!s%YH> zEE_}vesx{(IFEAC!1pIss0&;bQ){lP`rOjRB-{F3y=otl1904FF!vej^c5rSAtL z+8poH>B#T$@T!Dr)HyD}r|_y}zvVTtb@9M5ZE7MCWwhRVyO7u4>4PCJahV7M14GcV zbfnXSsF~jhYil1@KFKgId!C1)bnWBHnitWP(3c8gMFmxz_PJ2cyX3>PCj5c(oMnoHSW8JFm1puUm?ov}B!@}{fa+!p8{9y2w0Wg+V>Y$SOh1wtrA-pmu%=Fp zG|3Vjydd^evQ($<_!m(zDm9JZA%ulZUggP0ZC={KXTT9hnl}LT7D`ypq@EJHZ1go< zI%fFiedw81B!9^PoEom6j=+GX4|#idrS|YgMy;kRA@U9W87KB*zdvryuWr{~;z?BE8}u3Pg~K%mV)3%YQM` z*Yp1s{_o;{3FXJ|Uszj&|JU(<5&xI*{~`YWC;tQdw|T~3hce&huHWF z`o<5n@u&2SKfuNx);GS)#_!)Z{y-c5KJ(M-yTfe!OMT-HvhnNs#t*mgg1vgxbFhuq z&Dkq{go)2C=oNX0jr>Nh$U+-=VXw#{8+m%K$dHXZs#jz`8+kyl$O01?M0w{`xb6Uk zXRhodd1!g{gR~nYtOrxxof3PRSHLmCaf^Sc91TKd>w}E!g|*ql_R!5&1l405^pP@X z4l^&!Rq-sv(Gc*-7`>|4QnhQ#vGTzEs^!ulm$9X)d&{x%G#*5Cy9nfODB^xWo4MM( zBIN$M2!pHvpTcj(Ad)9Mop{Eg>H-~0Cf0J=0*iE1qix5lv$vlRsq25L%NY3%G{}7se*eI{7Aul z3R(z~KaHd}B-bfG+QIEySRzNyzZuW#R>d>Ey_Od`#5`+>vU=V$v-Smw?DEiBKHI+K!F}yp+OgQ29oyHmrJsO!+)lT0uJ?*UBJbwK1Pl$Q z!?faRl#GL+!FVOlHkZZ!ERcqTiOtP?@s^u zs7vqu3(p;TNdNgEJ^y^#>)$OS*u%3Urwteo>qs8XnnWfDBbO&d>oz3M*6URY<|z*E zWgBF8y)D_G;QP_K=P|qvr1+o8g5mYW$=^roo=SpZR!!tK#Ok$uP2X`lA^!B=plBy)FjkM{Ks*hO#d0H@5 z$HF5j?_ZwF_mNo>2h1yS|M0L@4L88@xj>fL0UHywv_KNG_Cv)+PfP^S7+WRyP&drg zPntuu&9TH3lM%tVNO;xMk^xm8$Iq!<_>t2~%M|;mR%_D$@qEaG+oPpTljdfRR10cN z5{tW6k}RCCBZ{!{gK7=@BbZoC_l2mC47<>7Ut><=_{7^-q4P?ZPN~qq z(@dd@{36TLf77L4?+B=s)s-2^mt^vlV|4|tld#cuc^~GO8Qed)r|eZ~QVbn9tf!zwhglW`h|1`1_RDTUJdNx)Hxbfb^sDmoh>)Be z>?Vr!x4&&AQsQM;X(*aeVzrqB15NR!uuSri7h;^Y3ct8#5njaj@Pqr5yc55HU z;AqOc9ZCON@;$EK4AZ=h1F2#0wjtc3+YaHpb6TD&OhdCVn({JkEKf^&u-j*hgVxLy zreN7my_sIMx1H+54{|4$6(x|apWHatOI06i?9)+D2JrInLH+GpHh(Giz#te&D?fV6Z*B5K)&IGP7TnCNc{Pj8% zDy`O(v0JJk!vmC9hy=hH5=;*o4>NYg@7;kY43g@mf#9b5Y~X_V|8E-l1b&kgo(7%8x;~U-Z2T z&W-kt7KtoB-wt@m4A6F@u4*iBCuMj`hZRA7#&!r^<{K>fIC(^E-|U%6!_QcbqFA-I zdR`GbRvcQ^pjj3r{<1JN_+J)i4ddN*!)6@hXM~SB*@DY{4=(j4s+i*{=HTE4sOjWr z!$--fdOA1S@Ug4`FUHMm!zWk(;|UyLj||K&mVx?FrU8~%dx%!XTKYI!N@#tq*W z{`qqxq|-l7F6LF9drwLJW3*v!vW10>Uz>*ia$1%<>0fwbBMlDZ1>?nu4N#DAUz1Ad z{HuONYo^m%Gspxx3ym$ZP9(q`@RRH360}v<-h}5tGOYU ztYkyE7Y+9hnWEkVZzL?s$>l-k%=l2Z$PBR01ChVPAbo$h4LvhVgrS<>h-t1+A(m(^ zD)H_`TQ<*ML-R#tIB($ijkwcFz*L*3l~p%S8)FP{VOvA!&`K`ui<)m4<872Ha^D+L z8lN3rHSJ_zk>c2Cz#>DlXS>rT0upJfoWUl^p31usw(lJOOvSo|v=0g+iIFYNWyi0F!3I8MWFx)*w1%ep--5*0PXPn)G2n* zf(FoQtEXPVsFeAm;xlc;yXa)Za*JU|OP;tYol zYORr#5&9C1lFhnSVovI4;*KU|Q}Z7wFa>wqN`%m_aGeYeyG8j3@Zj7Cf`fi7=xoq5 z?z2x11vZ*KJrvo~g3>uGbzh|=?47sb{{Df4#gsRYm_YEytvD1r%A0nBsU6}i7C6C9 zESAd`4#lsn$r}jc_b+E>(2jR@a;^Xqa=@o11b%D&Y#5@-m`M7O%+WeHPSC;eRUI4` ziDL4NXx%NzTKpL$IWtD#SPk=QPekh$VTcVD+D6^gf6&u5J;9)FXvO5$i>)U`(kolx zpu4S?MAE-##l6w(tv?5mZGDb>t*=s|wNp=fD6s;!^D9=2ByI(4x)m!V2`fG6l`Gz+z7;#E&uK{&a&!qp`}9J}E!FFC1HlMil0U-d<&SXQV&Y7iP~+_~ zY~%$qw7--^4Kc=|xWP)XWBxgtTTCA|cPL!<&!1YwdFMbeyI=egeNyts7~Bij->8VZ zbHl9jwK^>-!xH7gq>~T;CtNqxE9xckwTULiJHo?tN9c7w(^?BBam^XnwpU#KOH=wz2~YK zjJsD%RroahAc1a=Hl5}ou%}^saE-XNc5lwg9$M2D%+9Rr5UPPXnb&bnp1e2QnD$N% zKE!ETl?Q9&&=v#TKD>aH$#$0Wwos%fC0H7ny@roc zf~8Y}rBi~XQ-Y;af~8Y}rBiRiwchqoq^LE_z21s}n3b*LNo)mN>}fqkPiIiH^=v&= zQ{b&R=3c+QF#V`^uRm+ZV+TyYXK3FYzb4r0Qj8)lF4fxXwLEx5V}#iI*LGy!euD^!tFk?_^6g68SMm zwA4J`R6PKhtWDQ8RTqaJ1Na{XEpPLZ>cRYZX-PF!Bx_z?Qe7$|JJlgz36CZP<~eO# z)6`LkMWaM`LR21=j{b?oK>`{*v7*xlMuHuV-Tof8}0JT$LF5iSLy*!iA zUI#l7nQJ2HN5KOd)@OF}B=UP4H)QpvvMcIONnxFKO6mh%TY+UyZJnTCHVLhB^>l~v zq;pU~`j_4!7ycuH*!HH&b|PgOSePzk@p)9Sn$GrOJ`HXT2fYMb2pK3tfwE|g+gTLf zl^u^fwx8|f9-vI@N{DA^WLu~g9M2dM+RiL^$UGh0U`J|yGg7xakqynhc=4)FC3^$; zRXnkZU+~icv{XDMs0KMQs)v4NbH|g0m!smB$;k{B z=s3YY5ui!6_K(Tc(Z>17jghtQXMP`9`$F)rP`~+`pl7=t~%LG=_tfDY>wA@cyi~(C8tMBx4v3A>^ z@(Y_HhOk|y1>ozim9_uc&(RhLJARhRgi*2ZjDO@bEKar>ozv0AS#*rA&AO+#Y^kq( zy2ogr8il*@GxnSnXrTHE4gK?#@v1*lyVlDNIX+Oivrz?$WrM)Tk9BJede4%>1%EfM zw05cH#Y>7X0>R>G<%MxG1LUonP9JUm;_I9q$gbJ9f{ryi@O2PjDdhO-=)S@cO1CI@ zUT6^JZyVjysh-h+nZ(J^)77BU*MVT);2h-~O&vK^cg0=re1|w&dvBj{IpYO2pycI7 z2}tUsGH)QYpWK*E=J`73TJoqy+fLs|!d&u8KUSKsF|QWom`_Z83$32)wY29dOjjL4 zj{H>H{>juI=L@PQ*9+VBuQ?}ph85-+Pj##04}6v6A6DT{vGF9)^*S==MjJ0k&W<)- zl=-?6z7cJ_Si%jflHZZC(sGsfC%r<6ItgP7>&8I2Ym9F@4rQ6t1W2~q?jbexj|lUo zM6AMgxRZWNqIEg|eC>P80$!vQYE~j*pF@Hd&jZ~lLBzgN50OG%;+L%YSg}{27+HVg zyw2#=TK_;tB*#QLj!5Ef;glrCAn?AHa?%{I=3tU-A?fh!0j<}$bPqG+69#dNg3rjI zXdfAZ(;g2Y1qkR96Wr(s@+}xO8M!|$h?14Ilx}Tylxf)hHG zX}i!rKdLz*y-RFHcW;_&XAPI8nc7)KR=|7NSj0#;&3L2p3<&E(yRxy*EKDu#t>_=w zx^S4*wWtzTeARO&m3y~?JmO9EJSR1skXRc6>)+`9)_gli*jPkAOs{rj_rn!eze>EU z`dU6$ljeWHKN5ivx%VY-Y=N#$78)%L%G6TE9|pe+AVS^^yvWHM97(q)0oK=NCPow8 zk|SzQB0+EtM7a8EGq*?UMnDm*J0!_=X~Q!-4gyz38_&O8!m>n6o`K z`9ZYNN$!m{yq7{*JdSabhR$s#Q=hy5oB{^Xc=|R3k=j!+s!x5_EQ*m%dNmz0CfS;n zN}qLOtuc=Q%Gcqv9POkZC;7rk*WBh!XBWHXHg7t+ggvoTmwnSxUGL;QurQ5$quDbO zSMYcziG_PG9o4@0Om6Hcj7OVf8qK)ZuDO%XLAu&aXjLVyBn?SdoMRggmmW`RbM{yu zD*J*6LOB){{a1Oxiii7ThZxNE^RwW-Za?=E_p&|NB6r~)w?TbmD|>8gqe--qsEXI{ z#_m}S`x)l;ek@ORp!w4Ywa~qr&=_pO2zSCV=2oy+r3WuKaGra%?0ib*)Q{aJG@|)4J)9(`|}K2tzwPnAMuh3 z^M$rQm@9I)znZpSSokj0mG7?L7~auLrUp}$Z*YI01FgL7 zJTy_#ZyK%&%xizO%X5v~xN(lw(Ptl*==PBWi%WDD#7F1g{i;2^@gRH23Ao>t58UtC z2XOE54H(I0Jp6;jmvv{ju?e{b7jK*hn6N$tsYC$y%LKsB699jUQWtaYChN@+fV%FJ z)c3dkq^=u+u_Vo8WcQA62%%*}u$XCWI9Wp<7XilH0C@9l%X4V?m6Kj)l`94x5;=3u z{Ht!b`kFb>Yi_#vx^Lg`<+J8ob3^R9Yp=WJCgJSn2SOA43I|M{d)@VOCVxAALyWoM z7vg{6fb+gR=bGu)T=$K+UTm>q`_GJB6}#qUn@^P3!hUC7f8953uo0qhtGa8h{-()i zV|CpX?r&lv-?{M`8!3)wVey66+=canTu{PK=M|z^W93vfIfz8AmegXR+4jv;MDV)z5Lt zS@W_OBcwsbYV!9*IXS*X%r{82pue$(#b!#&sC_!g!a6Bg5p66;)xG>$}Eis8M=o|urHq03oPV;33wA;zTvPY8Dg|&!r`yQfK zhgjCoUE!^ia5+6lUz~rD_+c~B0uPSi!Ll^%BqzEH4q02tvK;`>zyVydK6WT?LrgI* zyIHhzm293QpMY^I`$ef~+e=vvJN0K?`Mh#36*io$2$|Za-2?UUwpza)_kgB5 z#JkT20u^UAldWCMFtHUE9`7>?iTI@=!Xg4-hSWAGJ5sOGr7$hbA`{+id<~gb5ur_` z45Tngn`xf!1WSymJRt(Ns9H3Qt&E)=A{3dt46FCBj7^pELVlc=$7`?sW8>KGWy)s? z*r))G{E&(23rBjJLk-OT4G%0r>FCq3e1$Uuk=iJi_nFSQ{L|0hQet!U&SM47K0Ez9 za7ljmzu{ysPtlRY0LAw*m>TZLR46z*+Bh*MB$&zT+XXh(+e{<>&CcUsf{EE zEh)+X(LR~{VYD%Xa9hJDIic^Lc}7`YPy!CB`ehJv`%d!1Xv25q!(jko9}NSy9l?iE zYt*G`yqzgSe$w! zl72eX&eu{;Vs;%_T%Ml8xa_vqRLk4KNZ&O#)${s$$+ka;SsI;Bk-bYv_gw#CGV3_mNSML&O8y9#?Eoje0R-c@cp>Ot z+KeDLG-|NuG3@WOJm91^`XHU8z)G4I?nP9xXt7>m3Hii&;5xX}qCr<2ZPwG$hBro6bCSUd_KaZ(vgstlUBH`V8 z1^bzT?#3%Fm(FjogET`UtQ4pkouCd_jV$Etq zfENKr59)$YAMEMBvW4zna@dXVpQQ|SfFhBtylWaJzX#`|Nq9;0 zFsdiW+y|qIo%nYai2rUMpI!7hVg{b%BUp5rqAFrC6O_`eIYgMR>0_mr%7J_QK_xMi}C zx(kKWT_t>Ose&iv0=1JMwI8o3e6>|=yG&HTuNymd*E(qcR}d+{7q`21F(#DGb2sKY zzB)r_m6SR>vYRotRnFq{FUahdc9805AL>`lf3vx>{mdV(O3}*3ILQOytE$OQ<7uq% zmdx~M{nsQy_cWqUjZ=~r6F4nT7o?xr=O+6t)tXH1QQw zF|ssv1Yu?TP>&c4UAipMCeomTP1agH2xTj=HxIrEZ+BV_E=d2zthvPc?GV3Z4#0SY z%(n*+8≪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 !lTZ5(?D#>WqGbwd0du1<^};_9Th#?{I3 zce#2({5`Io7=NFuQ{o?R<=gQiTsvQ3Ju!`|GZJ-NJv~v+)%wJ2uFg!%;p!~fC3Z$)0as@y7IXE?1n~ErL=#ufO0;tI z?8FML&PlA~>N$yauFg$3xOy&aB%7DGimUSz*K)Oiwnj82Hgk1BqK~T!6F#mkqMZ`w zB?h>AWE=@$Z z+LCyXtF4KLxN1-A=jyV=qg-8{c$}*%5>IipEfMAFrHSXcdRgKnuC7eH!qruY*SNYG zdoYy0PaNdxn#4O?ZBHEH>e__H)sDowTVrg49wUizJtT_iCy^2$krE`45`suk`$?qS zKq6&;M9MZ2DK|o-sGlN{ax+AV`e}$1^%jT}<#EUqRfSMdKSNUGNk|oS2c(L6E2N5g z8>EW56JkZ(1-YVr7J@|$L$Ij3Az0Ks5G?BFAX(JgAz9QrAX(JUlVte;B#ZJiM2mVS zM2mVCM2mViiI#gvw0r@gMg1Z~i}DO)i+V3)i~1!97xnKTUDPi_yr}m`x=uI~COBOv*lrs4IOPyi+q;S`y9j^z+Hom;;(&}B8R_(g9Y}e=) z?H`fVFKn+0udm+mcFp#CsjyWC!(Fvuy=r^94(?g?LHIe@b|h@79(?>AEc(D}*mbDb zp?1pMR2^Pb8}6zOPZ_>zJ32)|3=dMmCH6z1$y9;6KEfNly}Rrm9iF?V>m`|4Jltb{ zX;0S&;_LQka6ED}zW#_UnLcd)=*n>H>Stjz*n0|#T*TFfBA0UI?#P|IBl2~w+!OgO zSHB&3mUptD({0fyqVZ2)v4~xV|F+N{(2?P*tM=HZ=`g!CSEKb3=T8$J_SoMwi`>)o zu8}g-7XDQAj^Uc^cT$?JgW(s$Rl{rh1ZIxtWB_!9yN>Jv+JJ_SaBry&x33ScnzU2GQY~C>F8cTjBX_wGxqvfc?Dp5EF{#02wI#Zy%I;-5r1yy&_ zi>ZSLB(;K|st?d}lcq3+s!&!ri(E`JGoD3H7JwhOH8JJtXF)*p4AsF<)t`a(=o!rA z$!CBubT^BhDxNjIycU0*g3jdF4%?^ghz83VU*AT&@0-27r|W%VjMG?| z7VfML-mo}~m1a?u=UH@m3O3go12-o^zik$dS_xv*$8v7$*2zJ4(w?sO z!nfUlypn$LOV3T%mk)PDKaD|a4zEW=Yu*#-(Rcbit#!%aMJ>FDMJ56JAmZtfgOG?ut_j2{0B11sIgZMlw&~P=2&X9QLR-_)N-tl4Z zjKQZRse=nuhreKw&*5tPHc729dW}x_qloZ3A}fjb(Z{EMoPB&c_3`N`ef&KZ1q;F` z(nkz(-!V2sTh0 z)NZ3!x1$fgh!2)-z%H+3(U}EtyCS7=l7ycmMrBryPBCSSa;X9kl|dAe3V?AmGlFd= zZ)r>DTMJ5W6Qz4?(OIJOCrCBP5QRBM6zzJGEXo)Y1xM6Px^#RR;0K>!R7!9njUwV< z^i!by@Y=y!s#p-DDPD9`$)`(b99D|&gEzm&f>R-PNp>2;@?E0}hrz!k$TzDDE#kw& zs|>UVfqID?+lvi>dajYVeUJU1IRJ)3+HmO5aOj=k(7_Z*Iy*&@ZU6?5MN|t)!2wct z^dAP1q>cMGOtDHma2x)E7{^L*@m%%EP4J4Zk1CC4`;pmP<7xOvc$sYHk|X}nqJMt-b)>@6#@A&Z z99*+w_@)WV>B-CCH;iYY|9FPfkPq$EwqP~h>2Df`@uVbpO8C>&-?a%v^z&DNSdrin zTWXT3;JB=3wc?pshyYa~W8$ZC9yy1re-E6hLlKbeS4rM|4JcLbk9?CZPvfHfgwh`3YJ@b+Gli!4 zzXHc&S@f)w-3UYYeKxQr|C$*6o`WBX{$%L0w&*#M$zfGIFO$nhr2#a~f=X+4+ylTm zryHcNpug=niSc1gB~)mSJt_gb z7_J@e2o4^)B|K>dW!z)mZ|2=^q}tOJHS!+5@@X3FG^daRrq3yaDsaKz({fHR-$)J? zIF!Ny9~urlI2?)$hlawpnv;pe;r9=h3#ewl-zfThNb(Ij5N_~6S*9)|f0f4ndL;i_ zOfChWTtwDkOx=L{XZSkOb>=`cc1FI&*GIn1*I}BmPJ7uAJuC7C9{w5+e~X9z!yWsk zfZxsJov}`Ufh}2mYF{r`f3?qp$3a0Ozhlw4DJ)6o-TDkqRt`-!R0$fKZ=v$Cmj$aZ z5R-Tai1!v2bE#E}3+mieTOKSCQAjw_w77*ucEyT!AlrE(7jK zrigixmlyJ|mUt4cFO7d4f8C`S*Tel1)_x3x6s6 zMZq?uNodciBT|cz13S|)V1NH?`@68l>?#S{KRDa|o^Bj4hnyr0L-~+(HkHxT={uCs zQ$Y{dQqcf<=!~oY8Tk0B2=?&qi|oNXUVD~a{RkA0qyi|Tjp==nF-%sN!^9XNN2QE5 zk}~=ZDWi=k%IIVkT_8#qGZLB+n^Q~>U8+eeqgG93t&VDnxsawG3sa^a%UN`hEKtrl zV7tmvU<@mQVL=#|K8;G656+_*lxmBfCm_lRjlqsWP!Mvmqial|lZNL#_V-La0^Qii zT(jdRzyPE^*`c1rSDh?tw(rA_#<%fCQl+3dFndtxkJG3!Oa?6}j9^LADHp1tecJH6 z;m}8$gQyRF94G}m0i;FwV_E3ydoXsoriCYnNB9BIF!^0m9Ui1HAfAR_3g0EjUpEqV z9a=A+cWFk`8s9!FH2*IqmG`$zf_DiSb0}FdaG)2b3r`(``Y3oK{G2`|!(n37whsT| zZ|_!49#tc=p4F8&Fw;bQ|KrDNRuNUN%(_pP5!=^ zzTgk^CEi~meo15h(9Du(lR(xJu%VAw(;Ekv7C5Wfq0&f@7T@dX>z`WFqxrBfxzpI! z&}yMJ4(UVW4&|1blA61p9sCV6$8?qR4(NO`Ro9R)5=CA`KHcFP9v=R1`RKZudvavI zhu-e7>uI|b1A@#D-5w2HNx*hoi)1>yc6=4neqsk7E*;&J8!#nH&36xhFu-SOppyY= z&kg~3G7-WPV{p;io~A7QGEE8nqN#*7-sYv4yHqWRR+MbZLtuA#KdN)rK%uXlibOWqnzT0B#1 zoCi>xks=RcNVjo9C)I77&FMByA6Fj)Yq%nE8@Qq1;@@YPRo1(K#fj zmMuZG91?Uao*iJ2nt~>U(0p`{{T-|Vi3hMVLAxw0F=2?>JP8mWT9ZOBoH9+syj;;$ zP62eqj%dy9v%s60cG-_K4%56{ycaK*9I4q}MtsY73Pm;imU#7H(_*D&dqT8#-5&d! z)IxL4uiGiYU2hiG%BL~WHClm>F6R z&5BJEBMR>wylLr2OG2a!8INDP6hEE-jYyaU6`0>6*&h2-2U0kbvHE7Az)VY1VD^`l zFmS?QDs>5~!F z&_~Gzu-@^&?R0?!^fwPYf*0{9rTYmqtOtG$9{DS@=7AW#92N}1$D%EQK@?b0yBcVc zJQ5}>z2n=5LsN!BlZHdJ!=dUGuAPeWJP~Xpek;<9 z3To~2b3GW>HAcN!M^G=@ajjQtdc9gx>NST&?X+b<>{9sgcWqxK8j{U_4HKNw!GC*S zw-4*P6SOfnF&n9c53op`&_)4aSz!q8WkV=Wg|Li8vGPZYcC=MOw?_XlWqJaW^r_)j zb5#zuSy5T&_t-me5{rx`i}ZHNIdZ1-SV|Wz@jX>{MX)(=*xT z$@EpWTkb3vyr?Qi270Dvps(o}$fB580zK0+(AU%qsJD}8C5}?98i#1G{;K{=JHb&J zIhiY8#!`DJ?4rtjn2wfe7vU^KWGTI0j>p*6@@1_9U#_9|J$UcIR(3L*{!AOdSLlHtUJyj%t5`kVo_6ZWF} zC*^w0cAm%-Hc`e9s6!|nINGV>jSX|>F?g9&{s=>L6I`30g0G`<<)7=_cpctP!22qM z@R>_7djk4HK>lNujLR@r#%V}38VPEekYgyB3FW=DG>}W0%mkmohJ4^AYtgm0%dHJf&idH+_tMJotdAnWw#uxg% zx_OoSjW6`OW6iowyPkfu{JpGgrSYVqb$Q2{uJ*DGj+LFI_*86YTixc^P(@cNxnhHT zJ^iLD{bu)8vTtCn2jY_7%0QCzt&CMv8qX$TuM?iNRj|pJ^~rB1f{6aEf=w7M87^h} z=VbVHE%YhpGPD|$Oe0bR@{_{aW*Y_9OY-kV8VsO(3Byldwn9Q#kQs(9S4+AO+t$^+MXyMwQISfcA`KEH zjg}Qj0%Eony&_c-N+Y6;Smu{L`}BJ3HR>@+LRf^j8Ui5=#lvB?rFu0+OCU=!W5wpe z2iTUZiK|DxrvACYsLmJ(>0*SVqUtQ1XIY&rc+7UGUae{gY%bzASb`z9_*4^zQIoMU z^L(I|YSMUKQ6QM@BE9f&GXFVjRuP4Vf>gFi95piUJdn94+j_hpOe3>hs25!;v!4qm z{Fap#A>F9(E~DY&W$p$LwyaZm^AE`&-z>MFWAKL1SzgKnA?bb{gB?%q}b%N~>3dov;tM z;7V1*E$nBuQ`r9Y%qV09kR-!U1RPHPCYOwsmKC$YHi7LwzZj08<2^N=r|oREe=WPT z=qAa$1#6?;h6(9NnyBNaAka~4e~9^tuI6fQ(B12nPE(0E3sG1$@vZT>{KDTVvz^2C zUzHiCyg+Y(ZgjOxmYS|9WJcLzwg$HUip;3-xxlAu1Vs*MrK=Mm1Y66Eo1xFvG&}xPUUc>B32{;s2AFb#C1|7YAP1?OK5#Z zW^~3}w3}3yieMfUMT(Fl&|(M!eES-9xix~bRG?z9aWUJ!Ix{SBE?fxZsF)hpvHhzu zqqpV+2!*Fecx#Gb^HPf`5@JzIURO+t1UV<4imAu-5o}pkH!9iwx%p;vZp?Vy2=snq zYKAJddlDPpL}7cyoWBW?R9!BQF&SAvMhmw48ie0UCUM9#O~lzl{(x6R0BSIT@pZX_ z@ZXErvhp1QXOX7YU_?PTUyGoa!iTR>*dn1??XE*8Fp(u6L|qd*PbQzuW=dT$U7Z*9 z22_IPN5Ub!QOaVO?F?2gORM884I>>PuEyWI$oU`@8UKtSbTOMd-h% zO6-Rg&@dr~PT=YcZKS!j6}W|YLxMFFLdr~E|H8$(DlDT?XY_MQjRoq1^XuL7>ghUv zQTKJPxmHhcSfB840wc07`Iqf~>I+80j%e z1W>?Sk_>wUu%BkHc`FJrV+PNI_L4uH(V}ItMHiMf7fM~6D94;We}SoUg_$CuVU`3( z^z=0Qe5>Gg+PO)Lh>$y!8jIGHSCqx+_Lz2x4wth>kgr6w!TL;8(|ea6Ga7v;KW8{H|s1W6Ai^sKvJ7Tz9EB53QSGH&b0;_3i!YsB(?QJM-ysx-Cj?R)4$CC6`*GHvuiCggYr6ExiSy<8jGoJlVu5Q2=S6xKxfeF zH*nLN3BjhR#-~5 z+51VZ=^ECx68WdIIw@fb80YjHPMM>w)C^W?!cPg;+|;t!>5-D6Kgn`)rbNH2@#s?q z!oW6<57(@4t@Zr~6rb55({u=lzRlC!*}d7-6Otnjy=sIYPc7CZ63WOE$x!NMiX=dI zi-Z`1`U(S4(Crdr=4;3TN%5Eb5cv6Pw$cj;CpcY7XyF_M{w?JCR-e}c+HCrZ2)WL7 zpJSU}fGqL^sy3xm4hktcGW2l_yPjR2q;jmQ(KKj%<~-P}cQRdGhLoZ|7!1iT*-KWe zcN1(q4gxgs+(xtCDEG2D;#D7c0Clmnd7rE$8Osq194eto7RS0 z{%s}%AT;0~Sh!(%S6eIMt0zg*Oksq(-_YFC)8Xo+4E?Zd>XeWTe?x~0Loq=Px<2_Yc^iz>JBy)Ep(=4=1ehkB;RGB&Fq`IWZ&G$l8N;y zn6{067BC8F0-#Fc_~Vn{mn?Rrm-#*oYzi?!oItR9*_d?=f+o%%%h4rR)TD4eouchTV<(b?ihon@uom)Kn!ok&@?!#-=T& zV>=oCa)*$%u?o2$O&;9oFcSRRr> z;it|6H+7$em%3Xt>_%0{Nu3HOb$0luYk`Y8mv$|jjP~#z_^0co7&)iHJ>8}7PPYlp z={CbRog1#{R>L#hb#P3#1%B!J;FfMH#mqSYUg>;rO1BX{>AK;P?g|ZC%BNC@<7wJ{ zMA)M^$0x%hT`L^YU53NUE8&iA6}-`{fit>x_@Y}2S9BflMAr#NbPo8T>w+6P4li`; z;Dl~He9&D^0gz9M+4%Xf3Ggs_4!;(z;x33?1E{erXfj3hv&B9|mqEIO=>pddQ)5pf z`3u+(8+#L8NfW3~9D86(;n?@oD5#~((s(Me+$CHgE9A)#4a zdNzhtB{h9|koKqGmr~6pWE*HG8R|2pUZkK;3JR5RtYVFa>WzS* zIIoGpSbC+rp^fzG0eO2#-hL-<;{gKi=E)m2kkK!4KZM&pd3#aZKtLF`pA+FEM3`|K zyi6|D&gbWAIDl}Pb|w4*Zzm_g5AXpD4L`zK1`x|q`3!s}E{b*WU~B-6cW>fX#T0&Z zY&%@{?gMSV2-Zu)lGlJ!qWxdqux9+6qs9QG9B9*Lc$Q z!ibJ!jL2Yg@0z|Ixqht6K>{(l*WuM=2Bj@wpG5&HA>~NvAgMQ-r3DGa%sRPe_~^vO zF-a=6_go=O#EFJdayD5;BBhi>N-2qyQgX#$`-EjmU16DW`f-sd(_h##J>DPOF8ymWbMKMNbn4|}MP3uczRMqAd z43g}kU_PnMfFfMTNGRD zjk1wR{83eDWS^>+GY9u~piw!SO-j`{ zMKbIVKe~a)_Kp!_shoyU1r4Ke8b;+bj70Rp(oc98o%zWOBO6~Fn}H_ATHxr3PEc1M zoVt?@eI*$=FejD`GM$ini0Hglr%;`A75Z7rs#CZ5(j&kcrcX*TWjw1|=vGO{M-hl~~^r$X}?1Cu(TQ6m28M)lTZl+i_t->uph-S}|%(rP3b_ zF8VtnZ>-`kc5vC)Ddw;_A$8a!w;>+wz$p;}M}&2&qA^oRW2TZs4~2F6B&RcHeIjE< z`v}d8frFn#PDrX^OKH5ol~R>1SMs3OpuIdJhA2={wu~QMMGmh8DnY$pvr(iv1ANGK<6zXE zLIuk|tv{Ql^>dDk3U(5WgHd!^eN+stuFj5OHZ(f62GjM8C?fW0`n8)>F1U0Wtq+ad zeP+;go_RP?a_Yz!O+HmekqU)r&<2|kyfBsEbMf`R%#mReBSWe~X(4{I_EjGndmlq& zofbp!lb>0fQ<%LUCudj4youn3D*qG7G;3*%irz$H8|3@CWU?S}4~wf8B(8f=ReFX@naJ9v8Z; zA?{mCbX!AoTk~f~x91)|x~++!q34ovT?^$Jbc=W|wYmn8iQW;CZ7sP$uF)_qK(<%d zqIx({gV)miuZiDIG0Ap(D%pw?Nymk7#}nbE1v34!W4QBY7LXiD+bKH|m(oI|0lSki<3+-kHr~I>{-TB9lb|=O_ zS@UD(IiizbPY3O`GBc1@4h)Zwc=Z^cFlm5zcZl#^gU@T%v9A)x-PKxOW_+nKzEm4u zYKdS=l^T#&>&btI`@cC)anac7xV~`fL%7<)FXReLhUf>+;jQ@FJBeIuDT8pawVYgR zoj?(_wzHvH5j^ZeMsjxLbL#!{eHcFW0C#AH zhz-@*JNWo_DFp(491On}9;8|g-$gZ~M2Izn7*3t=4)&b%fG$u*t2ckt6!FvK$WY2)jUUjmRKbND&oGHeu!RPq&(8N7`R?H0kC zko`Zy2bCuYBP>`Sej%kqI3k;v_F&f_;WaF-@Z~!x1?h0Pcz1Mx?QlsWT%QJK=n>Hb z1d)93@tw3Pcnbn?9In_e(xKtck>|posM<89O+?eCP&C(id^r;lT_dghqR166CbOY& zBCHgG0Ni;WzyL`NBBm6%QTr#f;0Rnb{z4Bnru@+ss$KFJRW($Fp1+O0NtbB|W>w4k z+0c0+niG0!$zAj^I5YgLY#UltsRxP}e3D)uFb$$`xO6+E$bmw=={ z?>+Ee(raz+;FHgy03xV2!h=-*;k%-^67;bQaUd699ezRN3<7?K5hZCY;@828wrD)= zVdX*OpuSrVwT>LjtN5MtNr(hAM%u}RJSE>rm^1U~UMWrQggaAT5>VR0OsNgp_$?B! z!|z;vHL3_7d1D;W88moL2m82KyOzFu262Je5CTZmOGUG0MylRrEMd)+2O8_=)zj`e z0@_5E)W1o&cC6UYx3HA$9V6C8>an3n*~NyOBFxb%gVFaz{RY8p^M88}&-!tnlJb6JU#SiFtHwv)h9!Qpf{4cspbl3z+k2%zujv zn!Ct*^oe*|^A9^;1FW<}*IaqI3`hiCegeM){!N=OH8X*|lvIMW|? zcKwm!Yjkb?HUP?@m+BNUjHnJDh2M~#8jpzDgkRW&`n_rdacHSNaER(sHT-!o0`Z$x z14OJxxK)1PHmYgKp4-IO)d9(9Q0B-bDQ7sjHT{adpxpdNmK`6IA~{@+A#6B!JsEjh z*kgUDyVfc-lS#w^y1RDF1gzr9Nd zpUR*%6HBmK19}Z1%n&{k691kE21*^Vjx#kMMdG;&&$J=>_8_t5hrvz|sfzgXPef#% z0%GAn56Xg2)zNt1A>-%Tz87vK6yjxGwZv!$afL4C0V<9SAv7FkWkE_s3Zh;~1&G=X zz40vVY>GxBh0z!hc1RElSZxK1*sw!Ts6ul{G?HQ=bp?ZlexXY(A@D~t@TukqM)4_P zigr#xaf)zGFX&{M7d`1x6P#g6XKbJc zddqQJkVzl+(~I4&gqoNF1ha|q{~4F{vs32$(kghLMGeWj^qaXKO@?rVTx=KIb+%3`x5|UH%5zxZf{QWw--r>2v!wdY%5Brj}(J zc!y)zBco`u+(~Jg#|q&$pxx^W`KEs~xeA4)ODFk$`bjgu0@EGOb_TcgxmI|+TLROs zJDVnIfBPd0knV*Z)776_*%jh9gX1gKD!;P1Kb&Ir_$n_Qv~3HtBWgNaLWq8xV7_hb z>m$c6cG}m7|{L(hAYz3dgFIGF6sa zvu=3DaBp z6F8fZloCxFe7&C5KDYEkKrFaHq&UTdYNS|(0TJ>GE-jO=iD`5soM27gWTWiq+`ZKq zbjo96(|s}Wusl* z^6d{NIsA$?4~qA6XL5cqHgPIiK2K7`h7IX&lAJ`B6@n7dd$}vu5u=(fw_5TAfa?9fY literal 0 HcmV?d00001 diff --git a/Packages/RAD Studio 10.3/VirtualTreesR.dpk b/Packages/RAD Studio 10.3/VirtualTreesR.dpk index 1a5d1abf3..e9609de45 100644 --- a/Packages/RAD Studio 10.3/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.3/VirtualTreesR.dpk @@ -46,7 +46,6 @@ contains VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', - VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', diff --git a/Packages/RAD Studio 10.3/VirtualTreesR.dproj b/Packages/RAD Studio 10.3/VirtualTreesR.dproj index 849f0aa7c..b68edc58b 100644 --- a/Packages/RAD Studio 10.3/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.3/VirtualTreesR.dproj @@ -91,7 +91,6 @@ - diff --git a/Packages/RAD Studio 10.3/VirtualTreesR.lib b/Packages/RAD Studio 10.3/VirtualTreesR.lib new file mode 100644 index 0000000000000000000000000000000000000000..f718e145a63c9a8d3f435d0606e215f097f86838 GIT binary patch literal 1167872 zcmeFa3w+eabuT_E2_ZnR0>m5JU<+F|7$XTV4?EUtL9!7DX*GatEHBc67FN5KSM`NdMySAwxv2WtMN$jRH{jGwF(~y)p~Z_-QRC!&YU@OX6DS9GiNSd!`MsH*#ws3@9`%&7Xaiu?gLF+je_N)0Sr2gzn}Bdqar`x*Iz?i}9IQRn^$Jqph>F z*+$=Gc(m_mw~1%`py$mEZSomE=(%IdwoaQ$KUqF+Xx*g0DQ(`^v8AiMq}#r!vlyS! z?$*t%_UGi0(3nZ<#yFuDs=O8O8NsVZB1RZS-|2`3M16ye1Xz~s? z{f<4(O|D&jhyUPaN57L5g~Kd0dwbAt_ilE%MfL@3c74^Luc~HMduuK(mRor84aTRw<#@wf64;3pfroI|Ds_W(og|o^qm0 zIiJmvDOaNWl%(?&$TFF5o&i$8+vIihw)?$3%+jaQP09e6mQV*cIUPqMU8`re7dZZv zj^mWvAZ1b=WVuX8ttW`NqJ6ig+3jladiOEQY=z)dnP>@HWHeZp$BlXzy; zJ2O=QAX2(!J=Lgv-yUl9kjq8gp@)sGWh)b?v^#qC(XfsVN;nMqYU||uSg&HJBiS9h z+)hEr^=x%UUdfY%XLv&_kSy%H>WQZ<_ zm&f6DJtRg#MN|w3$zvZ){jT&hW$ai5n=0m(+uOY^PY`o}npNH>W|d>}jM-Un#wM?4 zPovlC*3;c3(^VQ%)Mg+t;OuJk1ob5KGRbr{O_a2z$>HxMeD(MS89xKvhi198?G2uQ ztEtan;L<1)d{puX+Hq-gefoHA0zh&fQi6h1EavJtmcM^hh03K}G`f)Yc9z^OCn@CS_{ zqsb^jyw?Ydoa0wk;L|X#5EUDGd;QKpfKBUgxB`kKpcgBrfl)Pf11m@BO^!fNupP7F zsma^l4=Ph@Zevq0LqS^M4tK~Y9*{sjOhn8EzthRe@B@Mngv&{ryqEyQ%gN%Wd|iP& zl0jwb0V<_2`0ZoYFiUYIRb2@3GO6H4;y}hewixjV=}wPV%_(?<<$%%T^A$-L^8iCI znkWM(Q>C%|AovRqH^V63U~$ns(_@7&-{=T9%VgeE+`GGt_;MMaS}tTnYMKfems+lh zvkj`)CBQ1NSi$tVp$W6=HaWRc<6?P)zsFX~xiw3Iyt&!OQVXc>P8W-7vDU)h$8H27 z3DHkamn{D+lnHNUOmJQ(KY$$KnAI?U2g^}!y$mU8T*}2GuxkfTA!WSMt^yF~YdH#1*fcf+!S<-Om zixFG-ux&Kb_1bXPSKqe1Y6(?GrnVq^WFYDXJ`eUNpy4af) zsC}gC`^dc1cJ^p2GXG%lP^|db%SFSs%Mtv){Or$*iVwZ<_QvAF)+b^^=Pbjvr;f&g z(<5^fwnH%_edfZHh&BAr8?Iv$+x=EI=c(bug< zpTR3V-87NTqSBo``g&+e#P(HL>j*vGBk&!zy)<;E;D(^gQ9M429z~Ms&upUF+AbaM z0(~wWZ@aWr)b*Tvz+ zl1SUd&j2bLw$Sq|`gx(WQ>NS*OQ7HOQj;%KGRNk+TIe?f9rVLTeA9(8r45^hFD~cj`Wt?Yzto zvAu=5R~&skXq7bH5&4}=^`h+^FbF&PHu!JnW#ZCX$H&Jbws#asN~phY?R<(xUtTbM3|P zoA9Z}XN$e~J0*emsZqIc6_9d75{eeA*tHdGb|$U;JO}Me%>b%eVQ?__)1z zY?{6J)K0$jR3EpG?Z@+Bd?NOuQ>S>x*jMrUTZsRSJ+|uS6YNE&=K;CX*W>B*277Gv z>AgVn^rOJ*^fBOi`s4Q4?Wd#m*qYO$eCO$B?Xk6|zsc|Y`P=qlHu805`F(K<>bebt zd4x?oJoJ=?CuTkJL4=XV&mmO9K;-O&+f^GyE-LZVUO=+=WE^_AAg>msAd|o7e^P{W9y>JsBu=I{?s_DqFX_PXuuv@AAJ~AXCvF0 zoWoI<{C_mwUeVvkBo+fZfrG{w{npDF`5=p73c2NqfcPfQ`I#wKX9?6Fcd@*##$WSC}6(?aS%MXZi_krisJ16am5{hdL-%d*yup~OWcef{2E`Aow=QHig$!7iUk zw4&$D>X}5SqDj6+Z^+YYB%-OXXtGZVB*t5s#)_u&9ifG5>_E?$r zKK?#!GicC_N@*UnY8Ws6R1pNYPz5WewHpKxN^XK~5Tu+4GKGySV6mw({jACD30=2J zTPkQiRg6H>7&}LppO`=|E#jRO&-9o44mB47oP39w00YHz#)RmV{B8zgLKrsk&SJ5d zLMX;a4{77#hl)s!U?Wx*LuZHx0SYfMLIbTLd#r+uOl2{=7E)G($q0*xKpt$&WagPJ z^32K1gNSk#1LEULs6s{|RyHz|#pVgTcFH)KE(8vx_SiHLJwI3U3^p>2#TGEuu0%w@ zoxq|c{603an8gH5sJI+|BU4xm0@72|kwi|-LQ`uii(Ly*^gQJv@in-5=uY}Q9-92r z&+siHo)=%39m^9hFO;_nV#(R|KEPbeq9Elp+BAF25?#QXqL}O#L|5=8Z4JG7ALndj zIg4GVuz-Afoq+o}Dp34N0{FQqyadoij1Y}Z<~uYiK>@2>t3i~oM6cu9v?{8@Iu=`$ zfVosvAb5QeG?X<{BDz;Lv3xM#Yf}JGn@M2j@omu?_`TXK^ss_(u4b{t3Hb8_rg|Y$ zk_rK|xe8l*Y>`$$AmxE8T&ALh~k(dFNbdIjm%nx=Say1B#D!t0cFec zpAl4$$l}H865%3yu{MzaFCbzpqF*wJ1l=BoN7nMUPlc^%Hh0#*p%tmfvF>rDs{xdWptruH>!O1|Y)S#xQzsP1I zk}lMy6Oe28PB!u>7Q0b)$+n6}S2;wo&r*Sp&9Wdo5(}0b`EIbJE_R`4)FS)ZV@1Pa zcT9$LvCowuA--sQd|?6&9~Ob_VZpF0v^IgDNSgFWSwvb1dAP{>*S5-)MmalC;V!04 z1zBg}a}(d7-G*<-Y74bHC}^Uf73AGwk5ysXE7o?}V>d>3@r~LZigMdyMbZ6u8o*QZ z{k#iB9i-e?EyM@m_&n!3ZtrQ#U$50L&v;y(teUH?dkKN5oa*Z+Hbu=6=LB!oAMqZqWS zqmwAW{8}BwTvZ*#>{1=Qh5}5r)wKMtre$Vzw1$G!6s)J9o&p;McT<2-tU79^U7GPf`t?; zrT{}_^~t3ate^nHclF726x>BYGX<>_Y^I=-g6$MwD6c-bivo=0)h98QSD)NZ0S5Ex zlkcbCFa<{`z#6Ce1sVl!Qt)pS{5u7gDfm5tQ$-X^q@bJvOsmzWW>WwWp!yU0O~Kz#5ToE9DEKA?-=^UE6#STipHlEI6r88v7Zm)Og5OZ^4uaDb z3Q8%MLO}%uvnZHP!F3c=QE)Q_%PFX%U=0O#QqV}jMhZ4ju$2N%!F?1wNP&}ry%cyU z2vTs6f=4JAqTn$Kj#Ka_6#O{_pQ7M16r7^qzf&+u!PhAG1_ggl!5Io(rr=u?e2;>& z6#OFvKcnF12-wI9OAIO*q2S}&gl?%W*40OnVej~g-r+^iG`0ViecIlMbdhdL46Jpr zdw1;U9j$_O1!?y9=joW4`j$F zcn@M71n*^xeS^RYU@3xkn~1+PXZ&V1vX#Y_tN5Wq<(ANj_6}9ONL!hbp6@Psv5_jr zUTmbgvE%h+!;O`qxO=ycuOd%GvBJZ7eKo`0px{+^TBb!+a z+f<#AvyjFqEA*Z8CCK&}BB-Qlio|}rsqAy4hY~48jU#f&L-OuQrlE6NqEq?(+6)vm z2Ol)yLhS~Fgthn;y`6BmgMKyfb~ZAD#a4*66^ig;TN&{JR0<`uDnw*WzR22qk#+eZ zSLTeI$6^rk(RE4f@Z5*X>aoWA?b7F3fY(SJi>(%Moe_NjboAxWq`KIBJDgkjDT=}tCWWWqlwB@i53HrV6 zfO-#uXv46%t5qT+O`hi_#lUKyE9f#Z31HIVMBy*92w>Y_p5t>ZZ-5$mz`0!&LnhT^ zKivvTBK7M3yC&68KBCddkQ(YnunPSuxgwM5GX#DJKzRs!ltO6Sq4>3WgxzSEtS}=E+jv*+@Z27v+-JXtIVDaUwcC+=fN}V747=ip!E~89FU{@P zvupSILcUhmq)~ro7Ot|-klD#zTky!*Y-Nk?DD!z#zL_%LGPXqGydq~l*<~%^S9Odl zW!77>^H*lyM{UPpVk~MqtB~5Jj~)pQn=P{!Ja)7Onq2`g04VEJn6{HCa{kK7u@DxQ z0uiS$QieV7Og2iU$OY!@@}%aT&PH!%wT0$xa}6e!TX2OpI)iG&Qx%3G`juo>!*n9^ z9qjhPa7M|-K%K~bC1tDeHj()jFlQEOXE8cPvo2#dWlKz!ZrSXQZ@{q?(P36eVT3H* zLL{fu8-}Yozb_U{ckC@@7?@_JttT5!Y{(m#sTK*lcDwN&-QVl)Ma6O!TVJ)SH>iyB z4SV4%C4%5&=4e2cbgRw-*zT2912UyyHk<{vUiBMg)vv+P8zNTCT7|WgIqbrATTgqF zOgkIr7ew0HpkvR5EgfwQcALFPPgE@v8P?x42kdbg7S}h+=$t0ox5)TeY=$hOM&YxK zXsU*or83Q8cCBn+{cM4~$?XUPWZx#j>FJlr^ae^~W$tu%^yph<^gTsOb;{be?AnV# zyxz7SlDIe?;;UJy6Gq0#Gf`wftS3fmwKxKOP2OH%nLWVPq=Tc$fec7f8Z-kk4kiez z%w*UNyD^cH@pjN35*8q`fiu!II(K{hP8?lJO?PcNCKxk)j$lup;NbzxEUA^lv7z0L zkQ?pyObKg%N+KBfjlHOwpaXl>>@fQ;z+aR{s0nlP8E}k%v+`Tm$RS~&vRzir2nE6L^q_jP z8{cNu11f3GuOc=*LnLM=+5|FFNHG)QJ9v#& z&ug`2UZ=J2mD(o0N^9qN5jhe-M6M|^k;*I5p14+2embm|ZU+0@tlfoAJ3hPkOo@-~J*6i-=C zQ2xk^!%VV)naM`J!^ogX;4md=(IAi@D$?KdoKhbJBE6K2q*3i;2rP<1 z7(nn>MrTdMUrDFay_<~kmWf9?pkzq?InySfs`W_$Db<3&EM;Pmmop^{Pw+R%SkdK7 zhz%F~O=$(&DGtb)6k{a*Cbmk($eE0t_-E1cXVF|To92>P)Cp%{F1aIVE?KAMlG`zt ztVx>rk>ZJhtyt6cH^3Bp7rr-z#$0&G$f-lp?!}DN3?G*~%AiK6-(l6{u zR>sR<*cL|ztc)+F0DBYH#+&FD_I4`cC}U2%pWhnC4r*omgZ9|m_@Chk+r*XeFWF=B z;?Lp=%g9@5hdtxR>>71T#AUEc>*)+e+!u+;O*doG6@$i~b z>z~H32QXKdCSsGJjMN)i#sGau&m$n%bfDoR9Q z3kx=hV$b3*H6_&e5N)uV1Iuj0L9oaY2dN2h*JO>GAmX6>&yo+O!mzA_0pYk1jgqzu zu$8cgwh{;hldS|8yk*%+xK3>)l$dNKWb7YEy?l=S0}=V2_Yaa){>l3ULKeoRg;e^_ zOWG$8=khk;5Uq-B3v%9jPi&MXi{!pNAi{G`!B~DpqVA1683^)QV7qeW;Ms2J<2N;>GAUHeO=>2S4 zHaJagmrnu`YJ+rw$f)eK^sR2`*tog7n_0HA(H{0dHt-NKr1A<4bU-JXZ&gckQ+M}Z z?F!XQT^?5tT3n{|XQOBc#DFW0P0W_9l+Hf*ig9{meo>FSB@ku9^=M)8iIAeHlr0SJ zGIeX=HKB%$KFD@tOF^fF3oR`>@w`-{@H@&uPA(+ia^f~}%#a&Rc%KND7v--CTv6=L zp8{SfJo!_=K?Pis3izrBhp~;5o2V;Sz;VS^HhP#H%tq7(bXAAw{z*+3tjSJ4 zyv?T7tWDiWNgq>0%MXEEcd>QZPyoAjY;44qpmY^ct1BwITtV8juP1eh*Dv~Iko9MS z(C#OOcR~$E9N*m9c;($Hy$>3L5|#_2cGi}y*bOeXo5n!uVh#`XKZSzRg}M&{wDPzLA`??;>G`GXOm@+m#2P@WG{g zZOI;>Z`c^%jM8f}qPD=zk#J7K8uFCU;dDp?0a-w;noNuWC?0zh#s+{9VU%2t>21rd z0JgIQBMiD&Z=Uj0NtYjBlLr3^`%oFpe(Dd}U!SF1Z5G7_Vsxq4)Z1gnTKNDhe95se|Ko*aSbo6bhn zbEeL*yJB!;&W!M|#cY3`D!^<=u59bE;dr5(F5VfK_~64x`WlbPNw z+9=@Xn)5M11zdYPb}!M*SGzjBgSR{V0dnL8tD?K}1HvXK95CU`Hl~=MONvQ8L(Vh0 zAmk}b9awDSsY89Umxx9v<(Em8<_CijzX=EB#d>mpM3;PJk~yU?j8I(>LQB5nGzZdx zSJEwG#6+)fI{I02ejs98HHTtS&1P@%5MqLN)vg}d&+|Cq%%BngE)?g$ej!fuOqzZw&awa1W1JvYHVqInTV(~@5jsBRn?vms;Km#Ne zScJMX=UCg)*lKTUXz%X!hg=X?U@(?khD$$~G&!zAn>}_smvm`u1u450G*~$rKGGxJB7>MzX zAh{7unkaglM1!i`IT+l=W`R`n%Qi?uyP!;6?KUhSRV`$J0pamABbjLE{Cc2_wK4G$ zBfolBB>GXLsw?fP$)DSRty{C=h3R^0>T@NJ`A`dXR%_N-n4Lh8r*I4WB--OAPEPP^|IrMb);Lc$3S9LRNsXP zZITjN4!N8eGK@%U_qJ~%f15<$TsaI6n<5ACP@dG_=GjJ-37salZk}YU2Lzmk0Qsl& z`?E`oxo}C4Q^0lv>#iIKMw8kU6v6Hw+Mb}Ww#tE!OFFiuwO4)fL65MJNXa*l26Thp z+i#FI2hd$Cl_pINsffNZnxxU&5!_5P8GsH%v8H5;_eOBxA5qkr2rjB^v}iV(BsknR zAeLcYv)pRnfvFjiS|08R*0|8GbhObO+nhqYPG%}HSyQi3fT0o4ZQicVB+$g#EW@5J zhe_Q3MfQBglvGc8MZK^423$Q@2PjJGv?J0=-Vx?sSXt08Ry;Yeks}rxa-$u4th@S! z$)m#)6#8B~LwIk8-LzXgPu%+<_DX)-b`ee>#gSD!SLh&)MgRgn z_KpnUo6g(56?r~VG2XFa*mhR_sv5Svq23*hkz>k<&t5JbhNnc4uIg!+ljx^x`^dZSaht4~PL1cqvD*vh zg=@s;8Jl|O+)#IUPs88OfAGt}3D?%M6D^F)BZ{KS`CeGpt%iT6Rncbnv}xr$x&!YX zq{u#A7d?g-@9^y>XK;SfiTl|{o zWfvw4*ACxZ7V(uwT1xP?Z20byVSo7p&ky@chVRBV8@WNab0b%czm5Dla&|9+$31!2 zQ`Qj2cEw4+Xa(OCoz3rMBfEstH{=*Pb6L-{7w1Y-vPE8v{3!B*AlP2Z@#d-$5LrA) zs7T#hRn{(pP0lyFKayp|c@p~P=KiY=xPV%C(Q#g_nNL-Bk`uH7H zC-DbtF77RorOC2oX?9UETqcbNC-1dv>y$2(Iu(90jeIwBrot%y8kBVd%DkEHjIQMm zL>utDk#_@!9l}Ep3`x;Lh;ly$xY}l;Fu6!bar_v_2zb?5WO(zG!lhKcT!Us z>gNUn^ha0m2LaatJ5$;7&H;4Y>%%+G4Yyqy?z#-Wh1B^L8l4ZFMbryyI&|nf3ziNY zy2KuxilW8qH~$u#j+4eLwD7p?t@fvB82QGUq%H@SmczF4y0`0IkJv7uKSU0lU;pkS zYmeW5yX89E8^8y>TqcWjokRT7grmr2bgmol2I<91 z)u}Fmg3{qA?ragJm{h;z;vasTXkZ$=EtH}0+|hdO)ppQ>gYSw)KB|HjFNQk0kSFKg;(_+ zeYlLA#Q}Hgu?8e1FR=9>^|_^Rs{7Vn5bCw|x|i#|TNe}b`a414-yI!)WZ{dp^OD@~ z6L5a_(8KNTE(%#TjN8s{30=TIcj;*n3j;ny|DiJ_hYwZ2bLT%If(BbHLU5Y1!I>~_ z(=JpUhwEbC+IHUhb^G1#it-~}ucGlIZQAjY$HWU!-t(9_KGKaQs~guxO?uOYg5UzK zeLQsexa~~)(Q)g4i`BjSjq|9c%o~C~+DqfXQMf05=^NDIs*IP8x4jmzU6fC@v+Hed zSwHd;q5M)BTGn;vyz477#4Z$c-u2ZRM#_l-ICK#XOeinwtXqmKV>|3;pe5}&7&&Xc4_G0 z(RWyA{pJg6HdD1%jvj}D<19q^s-V~91VJ`}m>>NL+?t|4jjup#-(|rmWr-w|;!&h{ z^T$N<7g|hOw@tJzTt%n1?r5&o4HMJkZrvxdRpd#db-$RvG}86OjD~n`ts6~l-Dn2T zLRxnu(OM&gwC<-8>GAW@adzU{j65Ywj@DrCSJ1(`b%}d_J`*ob&*dvl z-^K^X1IxtI5Ap-2_i^{>BfOTpr%XKk1w1^(Z#(@Dc<~w!oqhwq<9PQUyoQZfEEw5J zvh%Ir%ui%%>rdz`{oeSNrWe)w&x+p|(6+xj8BUnLcQ>9z4>Kl^*TDV9ZJ+oCjseI# z811BcIgt(x`3;QQGD=M_uc--!#%iX|F<@!7DD9a9N_OH-^6u!<;MZqz^cKDXbIyPU zzaf*OJ$xV<}=dK(2**WW%zIXJykY=Bo`0W2KE`BWnhZE$Da%6fDowC3L(glz5kvAj% zd$^%n^up56HDK{_W3h%_$dM~V=0Am&!2^drWJyp-Yq zQUs7bgtt9-CYZ_T_0$0f23>&S0u1`?1GE4&oYAyojoN@%AFyz+MZnJucplb^JRab) z2v2_e^Ww8k#8XZBk=BQ&eqa#5^CI!>#CIL56{&oPUw%d9G_xk;Of8M*LO3Ot>0kpW zooeJz*it#{#1a-=%zGd*_iA=J%mdM?yWU0zvU<*29lx;d`o&X+0o3X_XLV@SU7GcmG3$e+*8Q)QS%3Mm^+BuE z`yK24@03}8`>gc`FIa#7oQV1D8S5|p!TS3%);%%nBNO3yd+5cI*3g;m2c%ie>iDj8*LSVI{T`eHSzmtkWsvA) z>n}CygRfeD@QT&LtPh^C{@^+5@1GR}Cpt5@o4Grr$;v)fL`Q1?c<9Wlv_c709zS#$ z3#4D+wh6IP!crc>n&{hDnIGMR;SYW`E0$UO3#>=Jf);=GCF>CzaG~#SohU zxUTbRDFbo!c-tGtA+$^32h!)zp^Gf&89MY93-(1qXP1Uv+kEuxhj(tk665(|$fXbi zA$sb98r=h6d$l-L9JAmT5|H1hH^eGHy3EA-q4>M__Z)nXc90+VNZa?9w!IpGsCP-A z-n2P%O8j^;0u&3vZm+{lRWL6|uOX59(Q?D;UeoR@An$@L|H z)P?TMmP4C0C(2%%RCa2Uyc+pt@yq!4EI4y3HOMz3--)zI-3Z#_o1y?Ud7_t{2(ak& zyeoPyjXUtzRv!IJoaT!@Ljk<0O^uG>fL!!4BFJN4`N_42IElnlP9A`pW%A}{JqZz{ z{N!gTfG4&oCodr&*LoGFW+C=e6(UY;p?CfE*z{8acqh~vGxfRaweY{5J_^OV_dqAck=BJ(OuIp;gDwRZp@VP}AF3bP3-ay_LT|x`w}>oa9zUoqS6) z3Z=#uxKsN&z4&|naP&od{ufCkSg&F!D29-WL=&Op5PG@Dw~f_h#I_JO*n}=dFu0sN z+5G@ia~~n8VCWEL!XA}+=_^U;=cc5WDU#zWv&6r+^!W(Vh-ya;m7_c=?M*uOP6b(y ze;p(+3X60t`pr+&>NjG$vhpIfMZY=QPRhr~x0k*W`C)O_B4g!Uk%C!j%A^XGrzDeD zrBK}5- zI__Nbo7b_v`nCZxwE~@jsDhx0Bo#@#BNHUC)**vVjVLnm)1^BXWu!@KUNnlTU=krS z+D#@&LN!qm9TOBR^%0r_WyPNV%~|4j$$dErJYK#&sl;qN{K{{Be7ppS6qr<`WI;%h z1SGXd^qQd{(Q*-BrTT+GrUiP(+hvZpQDPEOhoE+sUF!iOnw{up(8AiI_u#V+pX2!a z4L+}P`^gFTEW>9rK0$n*z~^av{+ZiP72~rCpY8Yz;PXj*{uZBe+!z#hB)!}jQV~K-%qID z|2KZ&mgjV<^CoOcu@iqK&V6(_=QB37w5Ip&M}~^bih+KY|^QN3}Kl7Wfx9 zd|0dJx8hLf;V?W59R2`Y3>+T9xzNK$;AEf~P6lu|6o*M46*59OU(Bj1Y5 z9q(+vFb!TRw{}Fn9r>X^34nJ#M*>|bi{6HFvTWoQanf|?%v&#(R6%@Va+?KiSSJoW zTvcMxZwMGVQz=EQa=x35{JxmZpo-d`=`?mWAirQc!TOQ!kxKaGFIzqXaFP<(&LhE! zoH{7M3Mxo!@s*8l#cd&msuXe(2~+Px>f0q9q-3SPw9A*Ya(HKl8Z+LYjl-4}Tp)Jr zEqGKudi*NA&n)5E@_!VwzrIYZ~mFcd|ofBDgwQvY^kMZzwReHy58}_;7sgKB;nLdm9DqXn1@ZN?A)tU7&(%?d0${oA-$$^Ehgs?Z`Y% z*%x$V{@ScZ=C3L6$UF^y#^2%jY_2*DM$R(>%eRR8jg<%9x$wY?-OVKTyehiDGxXQs zhvnT^(XYiog+2Zx?(@&hs zQ|-yMlGu>P;8bjc^=L9O(!6JRs%PMtA|DRV<19zN;RnDtJ1@2f zeoLH$uVJ?cXLoh06ojU}dTw(%cY$#Pte9i3G7C zyN}&u5O6)KmmkLM=CTgrGAK5Bn<)v&bFG5L3#z zgESaPG*Etq%`0K+btyJLZoLsE6-vQ2SD?qB;0_&s<#5=%@I-bLuDeK9PKR^2NKR@# z+aR5yk{%rfRO2a^!{+=8`%$>()N%Kz?D-uICzYL?9k^eo@+*hKx3d+gr*w^+^2l-0 zu&u|S6JArKpeWD7ci_Ys@zBatx667CwUEq&1yIoUOJ=T+Et$gwvjK|cFZCeC)kJGd zmjGx$$uX29CIcND5cq&@d#?NuuQuz?8BhRZyIu?BoS1-QM?#WSHcC=m>H%bl4y01n zgxviKs^%CPbAV!&pd#t4MCZtkOjb0)Izg1nVzU7v0|=_}6}>8RbQH4s<<^@)`NeK@ z;Br_@tT+cHphvhM1!mKBZffz%0?e!L+=Hr#GEB8 z-&gA=_ar*0?&u97a^Xx;J|PK z(GR~T>Lfs_#ww6HiG1m(gdc-9m}BhdmC^@vNs`bMHoE6x*;YBmuN2WPflh5LQ6~H< zT$DV4i*vH~`<$Hx=%mXUvaR$Z<|f>nmN|h|jYR7VgvJNbJ>nSbmm4h_*E->4!PR3N zxJ~QJ@`@5>!3`7dp@70q2g9a+^ghP(5;kK@hih-+G;01qsF_$gKNn>}r&dc6bFy+# z4%q^iDkSAhw3lcucxBqjwqIdYuYi;r$Q#X;9Pl&drh;i`XQPem&O*V0cn$6Yj)Q?D zhc55}vov2bXW&LHAnvxnTq?+K_+&5+pvrhPLj!sr>$w6H4d?;@^~t&%Et;BAbchUR z{6>#666A8^9T0(E#~sS)1CG;hC56!t+7^w*sXNWSQ;kc6)oofoq(3gQP`n78-B_ zn@WX?xI4_S6$%=`Bq3MGbFbDUvM zTna?!4rJWWlHMxF0#|(ps5^=yzb%WZZRS z2DL6#xuyI(Ch})o50g=C^2A_}n2PfjmdEbwhjYfBK6Svd&8&9vDyx9u1t1@-T;{C> z_h2D-hq7p&dR7t_8UT{(4)nUo;isa$4<5Q(>W)Wt*hm=u0Ro@m&y>BWPl05OF0 z!>Izf2_k(P(IU?QkuwpcqhkLSAMAAD? zGr3QoT4lm+aJy+X6HPb3yoCdTD+zgN+a`O5AW{x$xLlb|%o^NqL?rGndoKl|Grji$ z|D@0wZUVW)0E_d|a@-Le(yYJuSL%(QI>pzf>Kh<~Q2{g#E=l*~nb9eX=GzPh9f>iM zP)YOEnLMu2Z_{Cy9a~fkXEk}nD5H2ODmM%7Ban~;&tQ`K3$y&*hTZ`OhOk~Kc4F!h z4xjvWsh8YIF%%9$Wip`@p#C(tN_TE3mfmT10Vd<#-Cdhlzpa86;E*|L;sPNEO`h$Q*1%Lk9`Ge@7+ z9`JY7v(Kyxn@)l)g%u_pgyuziZ()$Io;E|;Z;CaXH0GbSy_itl4j--_HY=yB&9h`w|eaEu1QStH7hX7`wO823T)ox{)3g7 zew}HC$gL_VyB0M7%_Ki9PP@+_+pr*rBbI3{bkBow*)G~}5W1iOMhUDru{<|xbn^L> zex+-cT8IT1X;h^1HiA09(l18MnGZ8;o5SP4J(n`0nGOyPE=7Sf%t`<8`TCnHUHzDa zoTMnhB@xo;S@w~wfU%mOzz74LBV|3(?t#cst|4L^Fjf*~E#d-?ykyGDgvyoyb@^oa z?VDHElZ=&`p|YpKguCb*lR|B6>L8>qn^BWr6;u<-An1}_lY%cG88p0vr>?j{fhAW* zjT}|>yK+?;9ej3in(;^$&d{fD;iuMQ2)B896Up06DT?HV-*=*FrZ{Ot--GLQ;A%Rx z`7-;lWcmW85cBCsf+}PNF;mhtdpCG{Lb#|-cc-0ykmxY2Wdd9Q=waY=6)+47n7T}r z4?%AM%|IUF3W3F*Fs&;&+r%=3j6R^kZFBgID|B*iLb%eF>QG-~z#x4?j%y{*F(wuu z8xm&XVhLhhzu}wS7>r~jIoCDmH2@@a2ua|4x3{!5ZK11nb`_K#Hlw;j-+Yai{caHE zDWnXcUV=MUr||uruawM<9Fdp$u**+-Ifn}_k9@(JO+$=L0Zc0YfZ{^?Mv8JQC>6O+(Ym5J?aQubB zILGYbOS8Q<^vG(OAlm`?4pq>#^GZ`rnli69&5$Gkz%Jxjhku_iz5etS;43akvAJ>5 z!xL28SD3cj|K7FTA1e?)afMT}_kh^^%QVvw(egIqs}rChCYfy}>x?hTZ1c)Hgs!^O zk6sBOy2Se9KbH*V8fn~lP?$K2eO`9#&SIBo6beAk9=1FOI3D^7n>`h6u^T8tq2R)HxTfW zM276f19bO}$?ApxC95(aDCB_krGj9!IB}s~P=}aMSmqlmu2N;2OP2WiD?r%pcl7Kl zpmDAi)vQuat_l>^dNk3QE9K3#AjAoPRR9q%YdB14EB3S+LDfBe+@+)t%sL`u7o;#T zZ7zO)Va4cz)q5RwvPyCVbRJPhqMI1YGZ(rdo~#ZEP!nAJddAGKv)S9>><6>Zr4}j1 zZ~6KrGuL-@;C^VQf6FGDKEa?tQ&vHK@z`nK*k)^P?P~Kf^X+^L9MZlmFK)X{*`pJK z{?*TYx0;tDtksDPAg6xZJUPt5g$dvG9fEbTuZFHbcwdGZ{u7Y<}AlI2TH-PJq3x=CZjezn@hZvM+ z2$6Z+E` z3ww^<7E*UowICX%FB}p_0O$rZGm~MUrC{(`cCT&d^ zDQgc$Kv@}^Ssfq`47(k;ADguPH1)_XT0mK{0fnWA%u_&FRERvyl$7s7=JiV5zR7pk zCbJV7+c)WEK3v{}P7@*YL)O7t9u&sE>%3polFL#`YJZ+`aw$K0P1rf97^wozS4d73iy5=FI0A(Y#u@XpF+{l1kI7~X z`zYk<*P>>IWmlr)HC2ZZFntxCZ;nEL*i>vLh_;eu5$FDJt*%%o3+*R$i`}C~$Pq4<9zSc`yZe&hN1h*j=#7R1_7nXJ= zK7%E7teW9Asc^)+UN18{D5XPwFPf2COxl6;?z%W?CdDRBPAAEF=I1wIaI>!|1fj@x zkVvXTA~UtfW^d57+l51}iId6FDz;u(#ln{h%s#yyY&RNKd`6V9j7N8xS8R5ob&KBx zlOTtCTifQQgqgd%1sIOT^2_!{h5@#xkfiD2Y7TW{4#|bYB(Le@C3Sivw$PxEPf*Kb zdoc3|zcvF?;?l5Y1-j!CeO2iu->;UKZ8iBim@QYs}e zE4-INOig0o5<;8`Osss3WB)nY)l{4DUWWHBlP)0jZFGu~=%G7%ki^eTVrC`}T)8&g zOs6Y!=^bR<+TiKwlZNmE1>g~Y(L9tYy86Mi5zMxjWLGujseFtWtRaQKwWb@Z&;tuo zfhKG*nOgM0R=~{C9`KYFiO__yQ>q)x8rA&hK<7S}&$Qc^+5rnK`?fx(6XycOcqE36 z?3)Lsrc|9luP+_efa&yyQREM%B>zj39@ZIx$ygvrYGE5Z7f3%OzH|lH)!9$v*n4%E z_7A&v4<;L9`Y~e>>_C`V33Pb^!e2~*Jp>)3$W&t{2dQQp$=hRiAu<~;WF9&x&`3MC zN~kO>msFYQPD{+s_UM}y@a>aq-!@=s>m@VF`B-d9BzZ6yG+U5l-*JZUXSQvVW!Ge_ z2@~sCs8vaiqbs|hgE#ZB0?!qNh#F>EgKd6S5PMqCNQjsV=6_Q^ls_ZX)f4EYplJ zX!&dR8VX#Q$xOeGWphRz)02o8pLO3%P9X-X!O`r{FAb)N&XHR>bea0J!J(|=<}lll zNbHWunM&73uw&mWHq+5<2+_eFdFt1UlTA+@5wOIW$Gqc!**powI85Q~kPK)k-5?Fh zUEW+CbWG2s0v>h_q@-sYT)-B_ag)A53VKNYgoGJ+T!S#lAWudc{SIHBs|T8G60EcO zTJYyM6JsV`w|WK~ZdWgyhZVwUrXvVxuSfy9!6lc}xeR*EjMQ{Ea``CR- zVJhtm2^?noD^`iHS4`Sg=oU!7Ara16Kz1o2hd`O;FgSe(u;$sak?n zdkXusy9S+Zc^(CV;E#&T27-26mnHU+(uPU1&eq|CVS`*`72x2@Yih+J;T6K6US>9` ziiK?6Vs-bU1!n@;iPjiE;?ahx|0)ZzNpA-R>zo3 z#xE&u0?cN0(e?ZxUr>qaO2{&I35dcrxcmXk?6d<#gfRCDN_j999!&C%o0q4|WFPFo z>Q4A{9ms&W`Ij?GrVf+FXb>Je?Orr|V(`s}JEl;xISbT~3N)R7`QT;nOKSk@Khz%X zB>WA#VA*Q$%Szl?34ZCZm<=I?dzCeX2`To$R7Z*!3cN<`F2FQ68Dq z`}O+tOljU8;fU7Tuiq%3XSzdXYA4f%ROgV%$?L^TDis7oL!a7n!i1NS2`N+o^$mf8 zo}SJgSn-9F%;T%m&jT3?M%vpH2{HS?sE9S?gGXu?yOIud<5dGvyVD;4^J0@NFA&k~ zFl!^7u2;z_Hj>OREDadhAqvT9CS-g zjL|t4*1!qNUig%D_NHbE!f8+5Qn1qzbV{2H!fJqYZ#hd5i6x^XvY_|ob3KavqMYyq z7YeVGt|&r`OHnJPEub|snf|iK5LtV#(Xzi)NRw$07(Qc<6~DB92yM&put8A~&Yclz^@LWtq5{c@?K zmzZY^=y2{2ftLi4CEfDyQD+Zat}9vuu-<9*xhN0KQaP!IX)PU{#=NPe18PZtoNR*r zF_rY~x!Me+N;s*9p1h=gFwN8K4efGcn_oC+7N(0{){rM34rwT&NUXDuE9ChuSJ6@; zS3q-JXx6ZX3SeUK1e8*`PfsV7BdW)Qcl1KL#Fgjngk(-e#|!M?~@=U?M*i2OIPT&;KF=Bj3xhJS<4By zEF=$ZvdltO!Amp17`wCeSZ8k8h1Hbw;V?+%aOq_x604Mc?8C|-aL_flN?*w@X?D7? z2O|dJ-B>?V8KZ~Z%UbpPOCV+DE(NTlCsaLPW1iGgsG9S<(~`Yr9ITdloZ@a~qHl(!5Xx=MQ?6{G#3+ReF4JoV%4OYq7!2cIoW4-nKvFa0^b!z>F=@@zN8EaS8_s)eJYl zu1QZJ_A88;drf+D!6Y#T7QN^kCI=bma6pN3-{%M_mq7!pDkEMU&r6ZRGYGQT zc>wAZnE1-uYX;cjbU5VJGBt@Yj7h_GDQODU7u=S(^;v6-EnD3_T2Y3*-^c;J z-ra;PH3H^esi(^rT55C-YBf5-lEEuWu6G)?z$jc!VB%sow-`wVU`nLdMPR1ePFx{s zY+V9Oz!2~<3E?y9Wyd`25Gj-LHe7LDsGit-y=dNS{iv2m^3Zw*)Y?ajCgg_ZA#E9&pDxsRRq0s4_0F3K2^O41%psR zPLoe$h}e%47c=D}Ho_j-=y&xB6LAs3#~9gV}}~mcV|2@El2CqjIgg#cRTiDh*i$ z$ETESOr<*AL5Ty))e5VKu@h66#_TbRb`1hHas!LuB{glSHMMC26R1r^q{>sI%@%p~ zu~@NUhZ)F|c4e?&2@xPW+Ezw8aU&nuY|h0Cb%#u9gbBXI+G0CO9l43cfULlGW@gy| zQ7&+{==Cho=8JrrEiu$bu<%sM7YceMO+=2VR8{;Dx`+`*ufj(-ScP6l;~DiJ_fo(l zV!0%CWVU!@3NWI0iT&T{>L)ynt8L`j@{Qe3n`S|$?)69|Bo>k0Rm^k`V#LI{ZK1LkFgK=x78ECAZ*>UBwMx6q+= zOmHU6GC^Bt2VqC9XR%V%EHpw8EX8u*6v0JBdW)25vqYA5OKf5i7a1KTsaeV?FaAIf zRde!UV5CI<5gJ-_TtNjqQy1-k4;7HECy4GMZhDeWSmnqKjUc*9o2wTGZ=poSdRnAK z-)qB;l1o0J?}@|ANeMu^PS-A4OQ(Yac1`H7xRlB@{PVBV?`;1tVHkn9WWuoXRWl3Gd$eycV%BZU# zAG-G>)iBhS#2zY(-n&GjfMQC=Kq)Xv)6j^6ZQ>pjbfko-0>zu)Jc)**No?c+OKh?# zo4RS@j%rbKg(z4|Gz~o@Y?9w2%nK-7kg7A}^P%gZ7gCYbQ_u*4C8SDli$`u{0Xv!u z1oH@=4CvXkP~PI{ge?jlph5&3(qW)+GQ4$;U>%^;6=WWO<$S-f$0NV0NPXwOCC9l}YbW#a&2*L~tFNY@N zo=n|#vgo!`6pK((C&-;S_)np3i!ASFF?35~AVr~yQlf?EJ^f8^_CqZ&$=NU3#o0T> z+24ipFSzK%**nMCudF7TLooaiXj3apb}G+U&R!Gr%L)B*HWFenjNAejn!GxbZhuZ; zRkRp2Y?DJQPXeGLO_y>yCM!ZU;7Xn%63crnVY8fF_r4JuzLcv%Es!RwJUX_jwD8tPRh)C-x?hZ_|+U1UmPH1&QK-cQ+(;3oB&M9cCGg6n15(feZN z23Q^Mxib6vc?8u6&OT!)BR3H2`0X##Y27YRZ@$S(O(f0H39ompRR-}E(9xKtFw#Q1f z@7QBy+P~RjQ{tuQZ*dx;<7-fOyqJLc5Nb>ISru z)&sQjbQ&!y*vN-iY)1Cc zlB(H90sf!|`h$&@B@D|8c{n!-MRvj|$TfqyiR&PX zVU!mWS58{#eUfGGlQiiRZ34-~+>UR?&_XFgIc69PZ|3m!Tl%Ca{E=2`sx|7UQd+GgXlQkKSPNC!5`kQgSiWQn1S z6|Y4?47I)Ti^ecKeMx@(Kz_X=ziz@%gt#r@OAfq`2yRx~fo0q*4dZ5s)<6R<1kqB> zO#|*NLlLv_0UCdRKRMTn3QgM}bxUe_EeI}Gcd6?=@IDq> zQjdwcCokx%iPwT83I-Oq$YB_sZ&$BLA(Qla2fZ$0)u>xDR%H+1`)+D~jC|u_pqri` zxDxuVW`Ze|3#$CfsnSkw$loKDVeG`$1oO?K@o7FyF!N}Tnn&7%c{EbZySh~s4~Nw( zV~;J@`g!=6_92=~{(^_slN2#mdxeMBX+NU?d_0f%cwT&lJ$7sS7JKaG_+7v%-bTx) z2LT`+qQ@aI{j9dc=Bx28lZ4wrbo!f+eYm4ZMdVGHYQ#%6HHBa39U6L^K4?1nI}G6k ztv55F%rkg^qSFUaY6=L13C^NK=?f`(SInjviPWhCNTw*4KFMX5u<5#9(I|NWD_#Q_ zV5|q_*GJ^nSLD|l;!AMoiLIi+=hHG`p09-b-!e&WR*UcN%c(|2 z{4x9^1^!5Z|2HUb;^QT>iMoI$l7%#fETHLQ0ZkuEvk1B$CZ*g|Qpyb*O1TG8m2y+G z?L2%JrjJcDE=|*VdH8)=A3uCfbMwRhqWO4us}|(p4`_otJfuC$!$-8kJnYww@bFRX zF&_S)c8rHVq&?2Vk7-Zv@MP^1JRH$J#lyYY2oF28C=c(}p5);s?Tb9zsEzV)v-VXU z-lILm!+qK_JnYh9JiJ#s!^3UbOFX<;dzFXVwbyufpZ0woc57#OxSvMGPVF2Ido+!Q zz1n#m_GxeN@P6$g4|DAj4+pf%JRH>C;o(r6@o;IpgolgbWjs6(FX!R=;}tx-GhWHV z2jX*ixI4a(haZeD;$cU;iidmR)jT{HU&h1r@fAGW5MRl|w)h%;cl-_>-Vm?n_r#ld zcw@YUhg;&Cc(^s*&ck=d?L2%>d^-$}TjCG$@SX8q9`1$N8H06MSv_6MS9#Q+$1VghQdp z?~Fgm-xvQPzbih<>*HVL4e_UVWBeK36p!)d_!(}Czr;7hU*#L)ukn`n_jzmlEN_dy z!8gax@hx$Ux5v-(t?{>bNBko1j9=pR_+{P|e~0ri#E>f9!tl8GDd>$9lPMtdH*>b94Wgj|awrJUBMULt_u~fw9BONZR~qB1s#P$!&|2m?o2EAkB=EnKXrhZ%C_+ zlPrpV7L~41r>?uJYt}{nxUS9*Dn(@#Q4z&;-JkOZwzylhKj>j|6O{bP~?bE62x%QdVO0Io2wT5fa)OxNRN!`M=&!y_Q_Lr$fu6;h$!nMCjwQ=pQ zQypCUo0OYtUr24|+80xIaqVwY@8;T z?dRGPDUEASrVeoJsnj8^J)N55+E-H_C~U%C%VP z^IZF1sV{KtnbZu|o=rW@wWF!0xc1G|*SPjvD#o>AsiRzbKJ`4;zLk1`Yu`@2$hGgJ ze#o^KQm=6BpHe^L+ILgG;M(_6ajw0XdW~z}Prbplmr^IV_Jh<(uKh4|ifcb&)6GS( z%TG&z_DGylWso+@1hC{6JBhaqZ=}i)%lQmvQZtcsbX860hXiPvccw zdo^CowV%ZoaqXYuHC+37yq0VK0`{-{E7-sGzp*Ey#jz)&B_VHWDX@R-HL!o}b+CW! z->@g69S8f@-oT!W_ABhkX#WoOul)zuzjgxrU;8!qzxEsA|Gy>ve-iv(`#<3S+MD42 z+FRiN+9~jV?LWc)wcmmNYrhBo*G_}~YyTJgU;8icf9(wMe@6Vji1>dA@qZWb|5?QU zONsxN5&xe<{J)&|e+BXXO5*_|6fY{|1#qL zmlOYAMErj-@&7A`|6fV`zlQk#RmA_75dU9F{J)m?|1#qLR}=rghWP(-;{VqY|G$n+ z-@;2d&v0Au=^@-qG%Scbiy|sS{{DxB ztUrFVJo4;rS1Mmcp!!yf1)PRbb3R>f{FvxcoG>|@Yc&qL#HDToPO)aOYdKRcZ9VD z*F@8)NYkk|>lYPO93;&%e(3e8IPfp@``87~eZDw&0iGL`eCs);F&3Qr=^zSz>&2^3 zjwqqB;@}sN+O%!2cck~&0r7yQ>-$I9q=neqIs=R~@h$9WHF_g*$VUHNQQb@Qvii)2pAs+c+_9s7qH?eWDfCPl zocWoysxGF#--Gv4O_Bmp2LWDB^jgo|?}{XLKC2lT8p@9H(*d47{z#s++T*SyrmY;-gwv1@7;^5z*6%k~+;MiX=R?jL9 zOyhyJzZRKwq9g==xk%3l%u1q-oH+PqaNy163$)Nq|EoEkwcS1Bn*vYtvJ_`9f*g!ksrve3Lt(5{)HkG^cw+t3%Lk;FJUq#w zZ5S%igFO89DD*rlqMzmA8=_C}@JbpmtD-OS@apL6bivG3cjQ7OPF%^uYiOLTP57a1 zKC+jG*CqDjE)k}mhv_GD$x9M{Nk4y!bjc#zC1D@2BnkcRlH^icKDVBSZ~R;f2H@wm z@HL;?#l!2_^tV`SsTgA2+3dWc;=m_R-HIpguXyXn0~lr%PhMa9){h33-dmw_^oq!c zD{^e$f^UAlCUnuj(p$eKlFy3aYhc5%U^)8Wxk%ZW4-JAy2j2Q-F$=C9|9YHo{NS~b z=LCZRIbIQY-wd9KpOF{!=g6o}2yZr26r)S>q`7k^^#e*fgn#ilyoALYp@Wa#j!Sfe zko*u|6^-!lP0`Qc=1aI?)AL!Z7DQR^5+E_#%wka&U(2R_EVhg=5&2Ez6;uElYkIX% z%gtF2A}1nUWqMUBp6Dt!ZlCO$m%gnq+S0W^ze9D+u`TAgIu@Uc9r|bm4?h;aobDiW z9FAU(zKY(6orfrP)UJ%SQ){*n1MPzQVPY&DD|T z4!oKx6+B14g*0&ZViuhXF(3*>;mU7vz@@>sMuC{SPZA2!swm{S$4Gr%6}^fOx&qrV(RaY;hfVKbv1B(RzW$ zl`M80fs4FybC)Z4<)JQDWTfoPX2?Gm>751QDvLCfiAD*cxPV0=gzk$%^t_)DEIHIM5AxF-7JEC=3p&KD#h~0(R&Urm>6U3Bnrq)Pt>Y z4X>+Pv63AsgQv3cB36&VcqMM<<}YD|Z`VCYQH>O3^2p8#e02xCLnHdD%js2CW(uHY zyuCQ*tt=4rG7A(|OlA)82IpE4SZM_vT^sdm*CA!TL4tnYUIR*{UK&{>>xnBe00@vD z`waGbdfZLh-L8hMjZL$98aKK(y2PbtQ&(3B{!43XH+Ai3@0#7%MBioj>E6-NB!1(D zes9^>E`Q^Oes^x&*43n^pDTYix3(EiW;br`+}hpY>T$PqmEeDNPwSRecTX8z3G(co zrtS2buJoHdTFM~p6@xV7oeQK#ca>~*sqyS0Y%t+j>uh#0_MYY4CDn|5Vm7<*O!1i# zHnSe?w`#}ywQJVs{Su@TEjU!>_3t9OLA(^>R=Q+tg721ntY`;2GzVy7MUXETo4Ez& zB}FmoZHmc!++!Bm)3b^dU9J~ZEQ?xWbrfr+PS*-XoP7bWv{_}v;X|mwGIm9_m$!7b zo>lAW)~{zp;TMY8%uVFlCo{fiI-RwtZ8%CInp=;&DUh~k@hpcewk?enwdh67R<<}_ zAyk`JiuEE&Wf3=`RRaHYXVErR)S#DACQDgb7>762G4#>Qy>vo9KL(qILkRtf<2TUt zWRs14IMg^am_A$wAE$Nt0Y80k!y%+FH6LL`6>MfN3;Y401?r45?#mkY;gB(oyaocj z`)aeJIxk~I3)svicB>s^9AV9AVFpdo(Mbf0D_X*4+*U_{CSL00foV@5rzDv@-$_Ko zc_<417pVO~w$H9^1&g&gGo2rn7mtI8U5gV(otvB7jZM6@p$U#$itbVlx0`N4MPsPw z2CI+abo;F&Lc^`za3ne^us6yq@naRaaVnmWcUG;0SKF+R-mGX)@-rJ6&s*&V<5(){ zeJh(e!v4apd-7&C$BpE%wu8kX`HuOm0h&y_=>X6>PoZ~0%>PHJXTd>zJDb^N4Qpf8 zFK50HR6!waWz7q8FR`|ir1$l`QO8l6Zu^D@&XfH?<>_Z=bm9;mr8e+fFrLxP3*6Bvl9l~q~DE)rF@@I1Y~ z%VeS!b_r@`K?3;-+QzDl>BaJP8@o`JYxpABhQQ1Q?B%l9OIVdmV@&uXVxBirT`N;9 zVDm+)^$id|DZZZZ{yO=-nq4eQF?T=XX|6}oqh|9@U8@;E*)BaKuV z^lCUoF#7pM+0P#?GAX*g8xmo|km1N~t&vkaCr|#{q!fptWW!?pyS#3V=4k1k2O5J|l|CrmPxn9NA5y+@@-F_jrDXSOtgl*!2XUS}I6}Cf zB>CjIGzY1N=qnJ0^LA~X;oZSOS**!~>h%f3W1`hR8e9e8lYQt0R#bAe-L%or+{0Tt z-Q648+?`ELSQp}>&VjVeO`T0!8k(eD<3q~rnvi_aA)X(th?8LHC!i>;HjSU^&Vq`aYz zsClLar?|MaQU0b^#TR5OB=o}!hc6Txu0=ROb}cr)m&f11uVvFUrgMpT#W%0Q8ko`X zPx=T@DAM#SfsNWCY&iC%hcbaxArrNM zPR`E3w;SKo_Wk(A5oh)ozNyUT@ePAib`syPb7hMFqypb$(M-Dr8osHLq$+2JIg7i< zI=!4ORdj)_(-p6!3zWyMICN~T_$_pSKHNoTGF)^f!xg`aF8y>FqDzo2`{{B37qQ=l z));#mRM8IXYxFWN4zbm+Aa*y#aHWrthAO*Fyzj^U$6hvqbi437$o3$gKPxRY#F};w z$_ygq0P+mso<2s|gpe+_4<+qF*+WRthtlbLnBAL^4reZ4*xf72sS$6frU9fL#M5E6 z8^3A*f%a#pR@4Fm2DQ-oZrhO}9}cKv8(Axk{AQP61CsMn?Ge5F4eCa zPzk?2lu6ji*G|H76Vgen_90zAQd)b^9tX{UKscyD8NDLka?s1QC^cOdBJwuWqEE*c zwatT6eds$c=qV_AF`b_3o{Re$#@Sjbma*wKSPUc6Fwvk#bWel!9{eA}KYjiQ|I5YQ z*qj^O_T{EGOe_;vBW=h#MRHyx|ciD{e4q;YPiI!9YX zn+{9EnAvpPcNUoS+t~C!7sKgCR<}{hsoUsd6#X&W6Ee^KQ_B7ZnHq2&QI7p1x*aZwrN73k4v877cDJ<-vF^5ld3xLjF2#U zqgcS`zsoZTfoK4YoVziD4vN__ddg-A{fPu9Y#qc?6X1~eS!vah$n@nVHudx(aZwgw z0tHmNQhhmxLlpF&jLj$SntRE?4XrJ4Ql}VPls-1Fa6C)AHjyEIWy2ChXTmCG^&;8a zv^v0Osg9?@s4WJ^rca*ymfDEoa=J!ud`a^j;rO7T(!3+Y7_>NMmZ4tA*lQ!qiLTK3fpq;7zk>Jy zwk>I%rO$K~i-A}o-5Nkcsd_TO)09@UHu6z=npvW08g>fMWhRl* zFddUdVeIf4Mx;A0-Up9n@dpS)4}s#MpXOIbpW!#fzei7gNHqAbNELm9uaEwQ-^`|a zg?}1h$M+g6ya-*tgW-kTTGlIlVqhZlTBNCRqUnuDQ~5*_4CTrunodTVs)X4daMAQy zN922vAEfaRc@CTLuvU8pZ1eC%EP4^&5WSRN6K|nvz?5PEoBk#vCpQA}NrK#cYNF|M zWasHf=v1WpbV>KA@Bdpyd8dvSHDm#(FW<1aJaGJY(UweViHOI0k8k+x{+lI2e;~f7 zx*0?jc|NPG_Namfe&v8oqn(a4eh{IdeiCG+@Ojb6FX0D*mS4zhfrp`1_MUpW<$+3uEaCE|4Z2)#GU(3^`yduO4& zw?uE?w^DCbN8NmB^j*Rm$0p&8ql=H9j8lyVeNEf1+?fka*XYuCqyq-;WFtAZ@#%-T3~4a1S9&-USgBNp#@M4%s#R$fHHunEOy9zDBA*v z-1ZHVn?c3kYq;Io5CNaud~@bR&t zZDb#|%5kyg)=p744D|XLVl+9b5h5V?hRL~UYxnTI!@j-4v>$Pn&cs=fo6U5v+Z}=w z9_vj_1i`b~fndj)cFCcsb68}0d5=x%d?k)KH9i8~H?vJ=0oHV&Xbdj`PZmh6Y^L67 zSYS&SEt`jYyLyKjhJ3?fqe)mBVcwj9rqp3&5V--Kjg9J%16eSEe7FdR4d^cS=Juw> z*6#Me5U4#0P#<994ndi$1fDB8Jp&m)(xqkt@r~83*SFzw-Px~drK-xg682x=4Mk)g zGwiT3jc~i|9U-hC6h!vky0C-YRzGkH@cIsLwahH3eussES*0Gs;n8#r)DR1Uj8}SC#7YTnw}zR}&} zZUC?#o9Smg4uO&7g27L+fLc37lAsBP6_vA@?d%TcV07Bf>2V8fqf_faOgWZGw&M^j zpm#0sHlL56a*9X2x3J9)>n&oJ=n(Rij@o_@B)EfhIxG;I$PN)fvKg9e?Q+=JD!o!{8i1HaHR-S_e+s&@#}76?9IXFS&>af*2Ag+xnmQaunE3++ z-LcA|v@Yt$x~Sfr#Xjo|GnuP}96I?B+h!RWb{S2%?z(9R=mwP93<9FxI>;2MO% z5yo*hWsO)jMVHo}(3YW*OSk$eFF~BSOrBs(Ur(-qMeqfh>DAHbv4Q3GnQnV2PvHRU zgDUVj<&CXP&zG6D*JdL~4;3RKL3-JG zW&Q6#^yZG<;b8<>Kmf}TpRjI%IrkoAP7-aT({>SiQglF}RwBN1R#(cr@Z&$?8@3wN z=QdxDj%o;zG`Vak*q=Op&>K*5LFx%j5R!@i+7$ z)PY1)yQ+1*q0pY;){&+?{-6jaRR~vJwnkap9)Bq4>2$Yk9T*sqw!1#7!Fg_DeW794 z*^PRJ8oeS2vL5HFP+hS2>{3<+#p=E&jX+0)l|sd`HdUb*Xh9F?p?8lsgb*kgDqh91 zWi)_oMwCg*+B^qQXB9M(O)e-IQ?#ZL5q<)D5QviWixh8!@PlQ55d>GaEbs7nai|Ru zp|eIxi#I4(>ndZ-sHY}|!0vPk8G(loZp!E%3KXmCBa0-T2Tgjba?6G}0|h$~T{mh~ zTofuO*x?&NC{y1sEH?#`Y_Q?=<_2UG(a!fklsqSZ7)<95@DZooK+(zsgT-+J$#=wn zRP{w*jnVnp$Hi7#PAG6U0U!cdW<8i?kM%7$F_Gq(RF%~hktu_(QrSwj%O-0|-r*D_ z(nDyYHvoD@#|xUlB=>PuexDIxtOap-JOPToBs#DzTXCz0<*IkfWI~*w@iey9i0HWt zE63W^*#|~5hhR@nM!?>+2-y38x@rR$8YDCcaI$wr5be!@UIa$%qgkqVc(iw<5R>$R zLm1tjIp+5}1K%3->}d)>*gZ$^=MVjS8gB!=*;(6Fkd-1-V@B9F!XF4yHDxvW^k|)s zp(M(!av+aVPlGmTHp-9dKwjeeio-hpai|ZIqplh> z!;oWq`>-ala4ZwN9!XX`WF@L8pNe=2joJLDv?|4Nd9hAAD$s1D68+#SeI5~=jfim6 zx)G5FG;&a^7$wbOv1>V{nZK|u|ByEb{wL#}5xG;*z)U`Irw}r|fXF7VEKv#~KDn?{ zwZOU$l&|` zVS6YOIpDTkJTh!OJhlG_~S6U#I&hUY#xV&o}x=z^3>B4Ae> zuT>3;;3}{6qROCh%l{yO79hqWRy?s8+u_d!T88TsF4aIM$oJ;tGu{ zJ3nmLG&$h4YcvMBsTJlgV=DaTZsdr(G=?F1gV}0M+9dKAUN;I+WI=GN4PmVt23S}! z$n#uEO?V^q3QdI~ybLusfZdgllX?^3Q-7$i3!@}Nv-F2>>$-lHr^4X*1j^&J7_25zHISB>t1 zEuFrkRhMcp`LeC35P{_s?!|tndUn+-Lz1(nf2;0M#zq`+Fz_>q)2CO`s@+ubx60l$ za|(sA4UPNjvaxUKk@Yo-IBqN!fM`Mg zkcW0Tbn0`a?GBu~w);a4@!9SRjd&V$quJGlgFSPa6|Xmifbl-<~(rhtCEP-D3Um$3=r$Bi-WDgWRilBldaOcqjk_8 zsWqjyd-h4?G}Q)1(t-0MP-bz4nTL`r3+~YSO+nevmytJ2PcPh4DAC*w z1DjC5L%MW{>Ad25A)=7)}$`8@d+AVU9LWuVe^LbmP>+4L-lu!BUk?@)ETo zN-uDqqZOfpeFITm#^j#gcz~(b(Ns{TDieZ&O<7-3_6(KP;_>bt3>qb-6_$m%{XEsD zcCy4b&Z5F@3Jv6-bI!M_c{fAs8sMz=Xrfaq<<)W;^&ZM{fCS7MmIE1ixQ^J<+61ib z4!{6OuV7w|kYA9(#I&X4eq|R5FhWrX!RlPuPF_jQ!Qc_V32%az=Pq=`d$Kxkpe8u~ z^^9AItWM7!5DR%}%QAi|G&UXRyX2u8=Z9^4Oud~C#v$$73f4_M_jQ5kpa0yaGBj(1 zWw2p%2|4wn%8tO!lr-VnzC(~s{uSz32=8msVn|)>GJn362?o^a5|xP|S^|4eGkPyo z4%IMaS13^L6~ge5%e*Njb;km6)n#;ywFI%RjGaHC|$yESAgmCw2W@RTeD z@Ju1HKsju3phPKQqAVO7@{SD3iP*tj402=$r0;QndD~sOv`M77=vHmC0YAzi4ZFHV zAM82$TS(n0xm1A}&I`V$G8sm-^fI~AGJV#gh&hr*(=aVMXc88CBt{Q#1xo3(EM;mP zc03ir8q8}w<(yL;Ne2ysq2YU_zKJkryQ)Hgsy+N>Y^`MM+WW1Ww94&ssDC%X88X&? z;5fr0hOOl&n^d3(8HIhQ(Dch@*l%4}+5K7Cj*`XC9VIF|2&$3Ly@|ysRcTadb$~oD z?CuQ>1xf2qQ;*E&0G8~)vx7xIWW<+@eD7CdI(z%3&|#b0L1=E@Sj~L6TQ7FNCw7LI z>XZ?XC2}~~tSV3shzu5Q06F6Xki_uY19J{1(3E@t1-AE&b<*k+I~_O&F1uN!|eDdC38!~apoSy>NPT$QhmE;WCU~pbLnmEBALBb zLZz0eEsgouqfQz{9V<6bB^S!us6s5r=0Fk7(iygnCPUlgZS;x}`H+*Ek$6H2 zIk_c0mWx)2E9gJHW9M*^O(jN%(#_Qyor}cC84}%Mv$=1BiC~>lT-6V*OUb??;FAuP z@(2>T=z_6vTZw9;wjJ(LxlC4oqLNApN_ti}7a&#_ zH#wue(*rv+^k#bHYaaWz=~tyT<6PK#w_*rLeH)#kBzzdo9whS5LmoJLZMtfb5W4hE znpHLq_YF!z_)(=?f?P0~hf>8*KPWpvwZ$ZdsyR;;Vp+jeq!745xv>g8urL*9pfm0& z)WcT5;;STw^iZCzMWSj#*(v8_Rmtk316}ue{mNmZv;&56HxS%5=<(oysc@u06|>QS zDov@nfHQVuW>tU@|rmB(-o8JQqkmBsmJO^Ru6@!Jbz+KHR?D zm~71HM@1soflyfqbPtbshIFA@OsR!O2nI;8Qe!5W6hiuVcMlr|6Doco^Uz6wO|)~X z3zg1tNtGGR#e@*`*&bul0(P{S_HCm|TQ8YW&c|X?BEy3T!arsk@ma@t{vBs57gXCe zd3H@KVH2&OO#fCi`bm#tD7&D8SNT|h=L#pHhDvL&4Utf=rv;6)PE|Iub|G?8$^`<5 zlEW1Qq0&^rS*S3$&d`RBVi2q;QJvc|Jx>9=?LtSn7^&T5Iv7=*bb;N-DFFWng74}laYmKfTN_7c#7p?buBj>IcX$FL2z3KvQg(xa1#j|T^(M!f9MGym zxlT_NE76@1(pE(nwET7ZOa-p2s7k+&Wphp*FXTvv>M~J~Xb5tGrC1#o)9QM+o zS9yvHhq5xeW1XV^(z|1FrZN-@Z1USBDji*Ohz|A)4fXmBXA<@mSDree!qR6R3-STg zJPE}(OyS*-49K)*qnK&Di1%ctyH()`~@ zoE@hffskOG_qdNuoJOUkeJdP`40-#p{qKa*lpKV#SEK-K_R1x-ok6e4NX;lm>PjV| zY$hP(_Ed(2skAes;xOBvwMvA2N71)Jw?O(0S>e0@@=Fmm0_8fR&C;;gI+EOmB2oaB zn?-38mi4vEpj^o!pAPSsXGm^UVGjIpv5Lq$dcp9iKP_xhHP|{mFl=yakL^=hvB>hA zP^gcoxT;v#Iw?x2JL!U%7*_CVU4KEwrBYZK!Q~4T;;qXj4at^hufZ1xn%9cy4&yk(sq;oDJHn6ZBFZ)jA zPOEf-g1LQbfTRLhxh2veXd!R&CZrna*0h86O)=n%SPpR91oxE~J;b$rL-k^q%GHP9 z5V}GeqbVyBQW!Zlj_e!m>w@dR{*ca4{dLA+15+}ijT4a&qz#J&)=;QClJMO6)Kf^t zD^!T^^$C^$Sde1!@_7p$hgyj_WA)n(UQuP6+l z+2rDq>+P!n4F$&4zBGzP$swm57WC9|{AK3BhLS3eVHon>0!e*+_jY>xL%n@C*e3h} zVR#h6iXRU7Y~Ti0t-2}uoxlk?WrckOAX&;WDR<8#l@-9Ancf(~qjZkOrbhLYTCy== zC?ii43RO*k9F3k)Zy(OYV0U~>;3D8(*RpL(BX;rX?LpCQfv`xG*fV4xli$&MPoNMg z#2nwcN6KLa!~#5^)AODXXh|Sh;;jH334hK8=YlZR==V|{FS&cS!v$2t*6JWsMuF7Q zyRAfkEtz0^%p+xxCFwz!DLL{>zW$T2^*Fbs(=rZy^$qz}&4JL}?*?MP7jOgZ?S*D)zrU{kbdz zyZTwx%9z3hxqwVMI$9-}6d1yW&7rWalJ0dzN#|i#c6#+jMUS$p?URBvVcOCatObSw zC^fCXh3%@B;tkosH+DWG^kw!5CAMX<<(6c67ZQh&DrbE_jgp=z3vxzENfvOf5dfDp zGmvWoE4nf=>B;q;7|kIMBdMHM-M$bkO)_0aVQG=KKIB+pgw6m^17T?=7JhnR5W@AB zVk?Z+{qR&bQ=p`vo?bWp6J@S74A$jBaCLS)#4@lK9#KaI>kI{^%q?^Z z>FVX?wyq>m2D5gqfh!sp!WXOTTq^};Bx@S(1w+woRIEgmk*;~jXC7D(YlOTw2V5f| zO}Aa7ttRhmvKn@jqYp3|lQ;X9|#CC9rvI?Pp8sG2!(3z4V1O!$|#| zo`A0%=k@~9f&3_Tu`d)gUX>X#%3QyCWYsQb)iTkIBCeE-^3>uRB5BKL#ehtDak{*8 zFeo$8Zkgy3Rwc{Jj>Kdp8q8-l&-3XXt$pY98NpK2RU zZkUNiWTMSb_UW&4DqJsIg@}28ngI$&c&N2Ko4Z>ZvDtLBeZC&-S;;VFBcqTLY|HP_ zaZM%DD#JFR&Bj%M9J-T3S_#6$z})J(&K|}V&t9y8VfH0j3aQBqx#X7pS1;o1iGiMD zH^6Glg7w50k1&27Y|t71C|hhzA#{ZL3A|CnJTY3Y7vMN<&lT33ux$1B26eX@qX=PR zDLJn=d6hL4Id;oN$Yo{{lO}Z1ra(SYxfYgWbtzjY$D|=*wE2d26P=q0AJU1mBnNXj z9g&8sqsH8W9c7D*I@ODrM-ENm#s1Oz`qCs-pO=ELkz!aa>V2G1UgN%D!Khns3Pcn* zHp=qU#N5YSZPp|&?d8Hk{jN0g7|_g}N;Ck2%%nltJY82o)yXXnCIEUz*Q0+1(j~1i z8krlP&Tz5QYWC~iCIy;RMJm}*11F7|%& zrD!mAB?vQr+?pC3Fg@b+nr$53*EH7W5l3^V8l#vsjs9pvcry7?zA=Yvz0vP&p5fiP zGd0i|o4h3tZ9u|HC!^v?)>xI>h2|Lhs2pQUGwLev2MI%vHdV$y8e9c2fK7L@7=m6= zj`sPt>^*Yru-`xu@yzpMUn!x{u@lO%Q zD)7G;|8K+pP59r8|8D&E;r~7Or!@2Ff=^6bS@Sf$YvjGW9_B21kcWfOhj};@{WuSg z#-}Nsca$!lr^^@UGDDZg>GBj^zDAcAU5?V_dAhtnmpENc@UVwXGgl1#wjE$z)B$Sv zrmp-9{;$_7_5pk^$L~MZM^XoRSA44U?`r*fk^artEO|$<%4auk-3ya7qELJ)@jtH_ zq-$

My9Q3VMO4TZWvjzoOzVX%<{EGxW0{S3L#L|5J?zKhw@;=6>M65lSi6-|35YX3#?J)8Zf_%3Cef#`R#GWI_4J%`O@^Rgm4 z6AUy}PJt3V<*cMkJY_DJYz!15H$KH|_Qp(nl(F(`WE8V&M6ObOzLABMv7+q4iWyY# z_>?xvMwf_mi`XoYtjvIUA*Cs2b4uHODpRBA5$s$!-1MX_UxSq(j?X65~) zI;5jRkl%=-+(ysYoJJ#1tOQuein9TVRONQrFQ5>FSvAfnU^$gg&gQ})$jXxuQtW!!RSgVrBZaHBwjd2 zq|zl(l%jNEn}y4}!0e$`pj$67gQ0H$t36Ug4BZuX5mzXuyGnO#R=kSts_9Zgm*qSh zj$hBi_r+K8@cr>MJiI@?o`)ZZ-@?Ot{{Ro~ zj6dRz6~{lp!vpcZ;o+M2Kk~3Y{xZGzHC`mD@FKAiRZFa=OFdm$=+Z$KfOI7Q&6NNk zR{}6x2^8;2Xmt4mDwOyVTA4thHHm*h$%!OtnmCPT$$6-0@^T&yB$v|9RXlue5^Z}= zvXh5*C;R9QmAo)HNl89JNj^i*{sGNOqNqUfNA&EMJUp2E9a@>1k7uccc$TU~x)kP2 zZ|V-TJhg{jgy_XMV5B}q7u0HD3bhKPzDX~BK+j$iv+S=}%=LRQ9xu@QofKOjM5|y*V zZ9>qvBz_@~5x)$`AR4?kz5&RHZy`$Qr59d$v6o&<5#fB2o_&^{{T)3!N{N5S!w<3P zO)Q4#gJ!nUY$i*SpZq9<5>qUh6~72n6u*RreQdhO6`L(*P@@?7COGuDUZ1j7HH zV{+}k;@f$6Y5XpJTm0QTTomu;;U)1wuKgfB#I={>ejZ*M5AsFvF}^sypI;Hzc$miz z@GIkocujngUlso#UlRWa*Zyz(QNAqx7!S9^r+K(F9_8V8#y`))*TlcT!&k>=czAjI zaUQ-s{uB?l#lObG?eQ27Zy~O^D*ikVua3XK!)xL%^6*w-q8;&9c=(R^&v^L8_%C?4 zGal#RuJ~&_?2f;|!`-x^xH*23hquK~@$mNeX&&B@U|jppgo|tcCsD@3cP7etxIR(I z!y9S(epjNJhZ_=$c(^f9!^2I9S{~k_!-}r_yymTi1UWTYrHY>25(B7;LVAXd~@OyZ%Lfytx3k;nRM~nlV!Xu zSI;^N!>qen+x~cP49jS8_RbC$Hz-$(5WZ*YIu0^?ZBs7QQ1{&+mkA z@~&hHzbo0scP2ad-AOm^Np9!wPTs|PlkeuclKs3dImr8yL)??}^MPcL?@o^K!Q_7K zO=|p}?_K(iVMnEV0{C1?0(@^QX5 z`4k^ZevR)-#`t~7qx}Bl^L&5u1^z(tMgCy&hy1iJ4c=9!_ z9Z0^xwS&nMTzg;gB-h@bJjJy`$uO_Qro#Uow|!_pH97-YoAH=bM3RKL9RtpLtHzO@^kHT zsUX+>GBw7v&!_ft?XOZA*Zw+nfNOt~I>favq$auc#ncD6_P42zaP3Q}N4YkWdW>s- zmzw6gKOVRo#5JYsgqng zmO90?=h<{~QEc{Uxu*Lc!c+gdnz6QME(I7N|E`_@JNy6^LtOh-+{LwT$IH0(op?Fd zUWiw6?VsXRT>Eaknrq*SFXGyZ@fxmuKVHkVm%!(>AA-+oKf>Bb`*C~?*M5Svk@izC zdhJy(dhKW6^x8jTZKVAioL>7EaC+?*aW~ig6`WrC-{ADxFTv`yI9R=wAXc9wR-Xc^ z*Iomw*Ioy&*ZvL6UONtMue||oul)+_Ui)|Od+k5K@U;`f@qZ1Dul)urU;8aszIGBU zUwacQUwaEIUpobsul)`zU;8~+zIGZcUpqrAznEA)G(?k&m)#UpICkcvHVJ6`3u1E!xw_(hc6DySWRF~2-`o$2@D^z+zi0Bn!*-%Pz@oaS2wQTw>7K7y7 z9eJaRZ#(c(ux$M3iPwm{BEOFOG;*r$rN}Yyb7F6K#S=|shc;JEZL5lGE{~L*`OpE3 zsmQSduLdhblKzK0JaVk(?sx5c24X%`l~Jtl4n`OA@PTMO55JF1zmLV{Fa~hl z5)i;>@2zA(D)vq7MXYY>LuI&^Z#P%T?-K{#+`qKB?pITdUu2PORTWQGJ)~FdU@SPh zLnLb268go|=BmiW)By41%cqz8Ku>ny)dx%U+nuQ1v+VE+7M;bz6VatSd}Z`)JpBG> zvpY5~+QGv^@ts&oM|bh?2clSq9*SaCjIik)EC$_87bn~td^LD|lIlP>aA$cd6S>F;ePBDYr$Y`En; zq4G#q^^`kMJW&M*_3sS*Ga%e4`*1dkEMJecb{OGAJKGA(T(sWuK^`;N$67rS%Ej{b?t!Ta#m zO#ZCUcOU-`^X;SJtq$9Gi{y8|YsZeApt?;giUEIj^fDgqiC)da?~bo@$L2?Gqr1&? zx1ERoEbgJZVIJ;{LMnSWK2AR;>E|Q($)^8`#h^PEtv!j6RQKw{&XW`Erzg5kVa&$5 z2Q|@vmHiVne_s^oK2`DLg?dL(9|mu@hfOt|jQsfEOTm)ye>?T`N03_fLDR{ONc-u? z%SP(%vWep3#dWWmt$RO`BCklTH||8_S@cCDAqISox}F`rmXU&@B6>~yF}(fyd{(Nx{s`_OO$6?BYBu0Y3aJ-#bgCpKe^RB|PnCQt z(uK;oL}AD8j5N3e7jPZln!ar$sNij)%;W36?qx3R_zhVPuaXZh%X(NYAI=hA2j2`; zjNf0zf^#AbWfDf@IdlN1>zOieTrd(pQ45qkyP|*1!+WAonEe@>KEh(qEdjOn5C_{? zMr1s62AQq?q?10dj~mCdE95`tE!N`w!OL9YHShp<|3<@|%oa$HT*Mr~?;7 zIcO#xpu79%g(k@5S{8!}4Q&ES$H!3r(x-~*nRa|$q`SN`QZFl0DeyAAfyFKoX}i#P zGy~IytL~+VO407pr%I?VF2r-$e4`~*n8(V1Rqz>16O(LuAB&;d0EuB2*;yHRpbRqs zSn5Q{*JlCr_ygrdq3a@BtG-4m zp?D@(B5F&`u6UBk`Eg)FacD5oU`)Be@?%Y>sRgf}0la(41~=8DYu{8k;dgOS)5r<& zH8C4mCz>&QDkJs!xWN4}v1NxFV73D!hQa&#mrx!N+d{NQ5*rZ_O_v=tct8HYX%-rY zG-S;4+nbLyouXEQZBDtgNLSTV`9tVZ9a)%cR)V6ysVXxchOVl3q5%{tpEi_7s?U75 z3^E8{9G`ur>C~3cj6syguMk`t_53OjEjvkXzHDUEX?;ZD_2F9>M5X1?TZv)c4mKTy zs=F^b0B#(gfI^xE+54m4#rziiCD_>!Xg)u51k~GqWF-$j!luzRm&mRm+LT@MQ5LHf z&+Z^H!UVUo4Djz^Xsm$Nk^3Ru3*D!WFGkUWDEfFMZtkU%MgdeFU`&p7HZ^H{M>A#{c-KGY_tooh4*5N!K*pR2+DmL4py873kK*rq>VSvC-W6 zj;V&Kr-|Y-q_^>-F5|r{8JL>7kGFqiljp!am-6Ptg=43X>I{MD=y@In@#Q zZlt?XCs?BL2f?vdPPLyjlk4vy51gE8KM~<&`tJ=7oCuXoHJ6Ekh&KhVxrfnw{f3$I z=?p+`m^`=IAnw5g>Vfp4gsG+z5}~x~Dr<*2i&r``{*fdAn!hpi=HtkZfc~+45WU77 zE05kvA~GiMq38~fIE{i2$45z6#w`Ak==*3We2Si7tEM>mI6eCYJ^MLbevcf96)5UR z9XQvK21o!$dLZu}@zDiJr^&>h)7!`JHt_`AeVv{i^T@vl)?dzI zmoaumEIA-m&0_IlcL|%G$6}Xb_J2FIq$uP0Tt+FcpvT0_kr?Tw=Q8Y0WIw%(eP%wx z!o~$oM&yY9h}gi@7O~5+?J{6xJ52s{R``2I*df&BvkUVlV23VY8oQ{`Gvpui*5EWT z?5=BgUEPY6>`)obXqFeTdKj@>iQBpPOIYFCbq`XYnBSL4Bbyc7trX{TB+nb&CRu+| zLwT}GH4RGiB#U#B=tlSY`-^kl%j#U7^E|UkGFRWw3d4put28_}&huwJ*Bh5v&O(AB z4SQE~4GzH+M$aIyp}!y#=26e~Ja2ScVEs)c6$T(=`m8^y&>tc6l6>;9&oDNt$KABu z?P}QC*fguBaie>qOI&(3b#;~CzqGb?Q`e66uGx)E^j(IZ?j0RX;x}&S_m++A@;7ei zcjwk^T}^uWx$<{&Yn$<8cH`#Gt=%229(P+;3I1pIv~Fp2_mt6=6rU+!Gt1Z&*@yYb zbbPoUL|xyrYF*v>wX7(7h_RU!Fl*Im2u#z;CEq!V%`7e4#W$@X ze5nZGCL)B-qAyjYtz$(EZ02sOv$%9)EaYOEwvIWB30f4!0(+*9-DQU@3xI|p*doI4 zZzQnBI5D1U7HYA$UxvmW0F;}oe&B4IL7of=h!<9@URE@~X4YB#VcF&tr!*<^C@WgP zX4WzsKg~}FmZp(kJOPvytza|Vtiv{67U|WSI9Iv)VnufrZiuy9(>SMU>FuJ;Yp+KMLZXk0Wn`vhjKNuOkkXr|^JvX9oQ_Sii1aI#O z`Tg(&AuiX4a#wjM&$xmCb~#VIK<==@aDs2f)N#Z00s* zaRqJ*7d#hd+0hr7$heZt+=?>{4(S`BNwde(zYF^)`hYEGGY!`G>9#cxHb^8zd51%y zCZZ7Z=xP;1Ub6~}+eK5G?d+mkNSh9=(bJ`DW+i!X$Uk_=r;Qf}2k*VM{0~`BwC*Wh=v9-4B+dP6~l+FO3Nrj4!db0;ezA~iJ$o3N z3cwH^1q(S35D5Z^e`AlHf~TQY708(PJjf{gvDm|MeE6reKq0b+(uW!SW+-8UFvv-c zw@;?Xk!Z0h!md!f^(j55Vyqc{nDyghgWwY%Ntv}Qm24rI0n+Y z2R#u8^zN%I1ZNq1$+=Yx6^?j>S0Qo-Zus#i0HS`Ykg@%ydbmnWIABa+Ugf(0gneiXhzOaymf-KThDNZ<3 zBkBCmRV7UdG&u`QsPvsH!X)T!7Y02zaZfP=sTPI$*BZ?rBfyu)PyTuej^zd%m<^aH zCU8DydEA~o{){k%?J77TLRjla(;k0No|(=GOd#t?p5wgM?F5ix)EAu+cx{nnE?Wlp&4^l#LVaM9I4N=W~i4 zxKK45ezmzfse*RcK%fxgNU$k{2>(h5Cm3Ys0uY1g+yOq~w1pI!L@?Nm!~K%)hykhU zi@-UAD%t0>PNZZJ?APSER*@-#uaX8?$#&UfP02f)f|m{$%_B~NW-!TpT$SI4BUNP75YnQPH)KwE6vLQ4HaI$yv`fDx3 zdu<}au>H=!w?gsW6o3nga|EB?mF(F-KcB|iKyP-|b`@l$2-WD5VtxR=rU`!_NY#|p z=+hTykf9{Xv%RLebx}l4@oaKg$(NOlktLG!t!hz0Lb8r$*q$0i(rlC;*ZC)$uQ;sp zABXxd9q6;L>G%la+lOhw5?sLquP;i~Lsp`i@|_Q=^P^HdmFl#k0&Ue{=SYT)K1lL? zo*{DXPDD6r)hy=EVI(g|MUajC%HF4ign!73Ak6{k!qo7E49pl_#>6gUy}QHb9S*7> zvdJq;HmjiyQYazSs3Y%y1(yTB9<4oao?9ps&u{Hv(U9juJOLAG@Pd$(@o)5M>pox# zVKktex>c^z0ZVtkFNEnrz?_htEXzU7)iNDbEVO6X>^$%J1>s=byFd_RY%Th^pTJcxLa?dLfL{-r@mI68MjLvT)nJYDO&L&`Dv0;O{)vL4n z(^xj6CBJsm63QU(Vs*^qc#~_)-2KQ_s)k0EogX%AnjG-jH5vnaVF_FfDk$8T!bg?i zS{i)_*pg0e+1}1WbVzOnSS74lg)QzjUsE_|W!x21V9XEO3kOr5dODItIhjFJO1eg$ zh_mNRq3Kn|cg!r#LG{2my$;y0sG=cL72;w(2i-uv*_@Q64AD$3YJ;?GyEK7Ad6K5@hWJQ}`Di|204rD{BnYqmnSZ(9g@6Bt6h)QZtHrow-Y4mu+wdjJtNnC2KB1syss zaI1|ytnOiNAJ(K@@P<0PTd4^bghx}M@BvFTfG0AmB}o3hB*LfuP*vL-$R$%ZTo4Am zegeD+k_C1v5G;@>)xb1L2&|s{4+oh%Q{UsHvj}?ASTN9Q29+wrLl8wiuhxoroWlBV z0Mp^~hy3mEv7|<4{v{~ubJCh_7+IK(zNEdN+?jA_V0VWSh|XM$#U~cZZ+?j1Nsl1T4n+Zb9|t!nek(R7x;*sxUbcCyHbIHD}XC?#~E`D8b|vA_@r}kUdu_nt(hJ<@XhrB?-$0a?Au_DWCIUlLP_Lt@Ad8@2Q`VQ1Jws*5V3Za+`; zshuqGjkBn*o1C&Z=$!MdYF??wt^v+^k0v^`QeLeEq25DT4v>IZqgRPrA%kRxh_Vs6 z@(WU!n6{MM?+lC~Se+}|$t%e*HTp#x32y|rkb$HFHNp9>XH8_*jcT zQ{Y%b>XwUO9|*XWhFNH26tpr{+3Gg>+}=Gn6P01Bt7bRCB46jvtFz6WphK1R`T}Xn z*tCs4r?eOXJX44)P!5|M zC{aq7C=2I)yd#LHOQi2$F9ta>1k(37z`X4)UD_nlTy(28+JK)pn(<(XDk#x{77#hA;>S73UwyP=>sM^DC#@32tNK(kNZfz>J&!K$YgwRb`|AFHS zkKi3~QoyG*6zN470E zc{z$Wk6!;SAN&HGgOVQs+6(Gn07Q2Il0g)zB@{A|vYDjyY>OGk(<8>zJPICPux>Ii z5;TTl2~0=>Q&OFBl9}=<1w$iB!!?;(DvmR(s1V*#x*w@;M*u?51)`j8YZu9|ePN@O zP;E(N!qS!0h2YqM>34)hQCFz~E(FB3F)Ct3NH}NA*t*4>vBRs}_5zUeUyG^?%g#p0 zt5kV&F&{m=nL|aL-h#);Ua48nbLi!hRAj2(v>$IR-o-h&yV9<)nR% z2x69)bINU0Y0+Wkm63Ct)F-$~<@h3ZTB33{27|o_g^WA4-64(;ny}A##e6=W@}QK_ z-gD86T&Jit>D>)+RJFU>jxgG~XfsF~H~Q?QTCUS>VnYZ*k$)edR2S@2TLN2rLGOSU zLC4c4lXZ20sq{BJ4VYGZ<|}g$4MQ`)f;WT zW8}#5`8Jwn8%zZ2l;UbfuQ<3aC3_qZ?US9KE3oLI3&zH6C8~|uj(~@3tI7CHMN5#$ zR3b>Hr?+3MJ0+PI25)BH%6&Z8_E@Puy=e<5Dyfv9q-TY50m4)!F`b@%ZvefSUiq5G z{%!hIsm(YS_TH@^KdEn{QsA6`zhbpq@3i|xkvPPBDAGio2V*g>3 zBSBINH^Fm3pWG0#D!|Uqe!>QOUgh|3`*vfpF{d9DiC_mpWhKx(Jc8iy zeS)2cDYftj!2l^%YRn{)LLg4>ZbUGpN~)cL$UJmXV3RL2+^-9jDsqy!q{_^2TB1JN zBXbEm+D!YlQKhYy%qZt$u_=+^!DLLeAj!Yu4B=0;ZIfr$#Nw4NM}{@B(C8;Uj-l*= z4qoMB1)eLMh#D%bfpAVGBmMD+ofRsp5S@f>1KF;pD4kO-ZAI zk7Rg`%JCjyE_9TOk=k9RKgL!CKj8UP^_A1dWeDp||D#D=UuiIxTaAhWy zejm%`oIJ{th~UqLZzhirgLUSpI`m5@Ne>Qt>Cme@#f3vzncbnS6q4Ohzd)ec`QU4%6SrsaoD%HAsNt$eWNre_xkL7#wgFF zqCD&zNJ-B;xPUE;DaF_z1wEvHs)QV9_UQ4B!6XCxtKM+ZUGFBrI`46xfX$g0m3ZAc zJPJ3G{qUdUgwm88gtS+r03~-v)I2+bUX|H`QI1@GN<`UAK+5f@3=30fXDBLWvnVV` zW8YEqt!dlvb0c*i_L zy8mi6`Qu_0BY>Oypx8^Yut}9r+UbE|gJXMapVDYfmgj^*eO70~Iii(V*g7dnsXOU{ znHW~^YF&RpqEacWy5Rm;(v!9OGgL5j+3zG^ppW(^)Vfugk8OOedWul0w_I)`AL!+q_n&90Eb< zEs)gLcWsQ}#Q76LiW7`wBohl|bbVoTRb>*fY}` zV_2N5vBLzVCIQw5p`I3!`Z}n*hZEOFm!0}&YGQFPaVj6%%%{DNMP>P z4x+S>BWi}TQy=!wJ^i^X1eO7ItrFw{GU@1Om1I(YZVkId_)LMzS%JqyPOktd z?I^q2J}FoerY%8HqlKnJ&`eqh4Et9v`FXd4Z|t5(=*#RAW@<2&TaxKrSTFbmi6{Di z8YNv-7UYbSk}Tk$gNBKjf!rfl(Up-&Ppgja@LQ&>gubAeNH7B7&Em zH@%X?S%| zNGMhzQ2JAknU)q=Ci=qm$vQ|gO2|Nwk3#q4Z8;!q(F0%Dsc6{bks_;voB>9k6ugaI zst{GTV7m+(k*s!^v$64(&Su%wH)p%Yda@^5+Ce!zdUZ#7de&xmS2ZYIu z-m6x&Y~XKod+utp^e&@xpc=dOJpbdOB5lT|k8F8{DTfl*JN zhg~d@4pIfJ!$1=e2M2uHSY;aqyPSXJoC1}C2>xMMHc56i04cm*x*Rlt4NG8^Y@Wz* zBk?`3cJsZ@^1h|BsjIuqZNB$g-fx0$AW!eG`TjlfeIGP)##ubUS~KcWUoa-j)%N*% zu));n8NdicB&ll(r0c|ng>(gtge&b6+UznF$f0{Aq$L;Z0dzycQFfg@j4htMMt2hH zAhE+hjO?2FruT$;hs21=aYe{#tck_A8X0WRS=K09Y)v5)dgjcr-ul{cken;5IbnwC z?+xlcFh&t?#?p?wa@$qbROE#%8zGljlVof;rr+gIkx6ozB)Lo3LOEQGdA_5~H@q9! z%!FZ?FwJ1}2DF-Gn@JzCDU*sMk~b5M%Y;`UKB5?bbp}$z)F2rUsiw>f2V{nK{ z^CY=%MJ=RQC|-`<*9XR9EgLg)y7-LYSD$yZVTZ_MS~{z`-z*aSGSC1GK0yntG1BEm zUc51eV?b+Pj+L-Q@dO3Y<9NyP7*5~o3$9lC{y+#BjlP{@)qo6;%9)=Sh zAlE-i1`h^~M1#ma{Tx~Zt&UMlUM`f2Npz3NZ0bel>DnKHo|~MFm;gjQvAui_)jUhY zGd4ZNVwjj;7;mg~y&)p1pU>S3*FpzfhyPpgPxJi+_(xECcBTHiLUn z>2eoc-c6S=x*Xu)-E4YxaSZXFw*yMQ4vFq*@FF5J`<(vlhxlHk-=EFbe34jh7^7hH z@*_e{Gx4CJIa}kezoKB>^P2S~p*So@FJ`e{;@Zego>_uIH{rWm|Nd+J`+NE~B7JAa z6TMA9o-MgViTF{*ip#_eb741rpqL5R#jIkZwMxaJ=Kx>CTP+dUEA`xEM(*WQRylJ~ z#9%6`Ta@i0i&UATnz;l#Glxik9DSalU@@zqpOvgc{1C6ISh=6xHG43-dHSi)W#Yvw zp+b|Tmb0SlQj6JYk;p|xjy81_2~5an_u&+aGXyn{yNEW*>8{cpE1}Tn#qnyo)X-%) z4{P!3d3Y?ol85)j*YNOt@%21>fBY664#n$vcr@O~!~5ecJp4esjfeNfJ9zlPxSNOH z8{fsl%i<$+`7<8&#s8Yh`!Zd=N|$Ho@@={#5Q9DbI{;6VA#tJ>G@bx7e_}m7YX;dS zwqY0~P@X?=4?P=0d5I76a5(V=jEcll^dd$tzK@ZT_!<3-)6d@^-T!0necZ zO`Ir+?9g9%jgrvRZFtEB2+-CqaVaHEpb34wyK-up(u9KB^6>kf`E&Q~-Fx@0aMNGk z=Tn3B?#`V#GjnF)(`v2KlUkh<^q>0irKkyG#=AL1w}>`j}5WWgT4fA zz!s$Fb(0jG&$m-1lzhLvqDz#S--Rpl}sSS3V) z=Mf3Mh)D2-M1n82k>IhWU}C`-=IAA3ZA6}C^k;g$1C)4iauVak2mN8bGg_!O&@R4$6NIAHhr9> zkN5DA#8NI!Vks9V%jg5^xj2dST%5#uE>2=S7blm~$0qvNO&<{+dw>>FDEVj*^LX@) z!bFK=4D%J{?g1)jcEiYzO;>4msXN*cC240~CX zmw3ap2HH!@U9CzcW;wmVdM&YfL7{C$V!Upq0pA}VhVcz z9o~d*qj)x9slJFO@u8J?P0zd4^IPgU7xR+R1L_%5PapVG#ud$L02Cd~$)5zBFJL9L z3L6FNDp9IbiBGf!7%Ni@0i&ha+JoZ76zWQV{3#_@SjdVqImcAWR?enop{kOVAx@q+ z^(g?0!oMn3&tO_qYH48F*G*$Nxm%kHB^;6PQU&fv^n`R;`e_?KmQ3;qzb|G zQ*(H%BXt>lTLq$>f{fCW0_`hJZ3iJwb+>A`L3kjVRg{<_X}WwXKZ&N(zojA*V=M7|#R5aV z{qN8J#=w7L;Qx6H9NAw)M1KmA`l&?arx2B&LR9{W?2X+uw6811zV2#sTep$6bqCFD zU7>LYk6mrt!|RRvc&x$C`;xTx&eVV@r*39$RKS&11J4*xYS3 zp5d_;Bf(=q<9Qyt&HzPSYnwGTz{^I}C%z?leyG*j>h( zJa)J77LWOjw|Q*4ahAvKHQwW~9ZANc?F0IHdLCPm+{j}q zlbd*~CfUqmtCB$;Tb;av$JQk8;j!zJ_wm?O$qpV{m)y-`Hz&Jz?3QE?kJXYuzMcf~ zy5yrgR-cUWSRi?T$2KJo@#xcIUyuFp4asNtjmZRGmwcYzlzfTboIJsANuK2X3d5Qw?L?@u-J_EeB} zr0(FIseAa&)O~zcs)O%N?dBn{O)B)a;I>aAJjqu*o zr+6QDbtLr!?@xV+4}fJqoEqbUsVDi~)KmP!sd4^D>S_LH>Nww*dWL@_mEe!1p6AhM z>LnhHrB3kZQ0gR)?gv|s9!QHH+b}8DT7B3fz?NcQ*ZL<$5U_dXgu{c zkB+3y^625zdp!Dy3Cx#IPGG)#YNCusKRr>-qrW#%$)lf{sN&JbC+6|!XD89`pm>}9{rya&+zE?ClWmRgNf&PGyw@F z`ooD6Jo@a!NgjP};uRi!e&Q65{%GQL9(`fr4IX`Q!r;-DCQkF{pHIBWqkl2+7LWer z#M?Z2V&W{1{x3G#;!8|zXa)zz5&u7{X5Y9=>LXFFZ%bO{ZRw7KMH*skETHTqZ6S0(bFLQ(O-f1NB@C{ z{~03wZ-V$o{}IGL`fE`C=s$t{N8bYdkNz|0fAlwC0MXxq14Q2j3yA&;ctG?W@PO#M zU;@#zU;@#91rvz=JD5Q9Jurdj{{a(-o+Bo}hza`+|u}jS-Sv9><1?jc#NKD2N2g z9b2rC!o2Y8_Mz5) z9}c`%^Kv}!o=A!Z-WJbhBt^G*=iSGM%7O4=0J3c9PXZxQoNBWL??~W(8KgW(#v$2^x z_VF=H#E*>ut%r=8d2A7#1I5QS^VslMl*dNc=rBt_*@kYQ5Ap3~@j&^(M5L(Z<>AVj zb2W+9-K1~rIJdlGcwzju@^AaTYBo9kR=loqxS_K`*L zhRVZ5(Ks(ZTr?E_%fde|{PV-*!|~Qik=J)`WlaKOwSQj~ixkHLRm1Zn8t>fxEYw+0 zwvSnDZe*11B-BMHjh=0VVOKOb~TTE zMocnjV8tZEV8yqW?|)<-is0E_{m1_S z8WGli5`Eo_+Bb6oT1|tVwn`8=K2xkB@=oTsj8j%%xylOUG{K*BWi4 ze8&_!%tmLi1Qw?p=D5F74l&050-d;ja6YT}tQz~-EHPKVsQ9kGN@o9`ECEuhR@T1x za#kF!n-_mo^z?R$N^ra3Q)~lK?@$8lt6kDFI%K*~vyXv&2OTfARi#8u$eQcxAi< z-LEt|UG+c#{p7l;mdbcjIrYeo3TZB2`q5&8)(}l@V~LBiMn*J!u7EyMBT#o?9-&&J zE8bKYe>J}R+~<}9cKj{sj_yhP1rvMd%PQ*27SWe#mcWsMoKG}$fTKYS?3B1)4w{-_ zGg;yik?nYauZRgMIeHySEKo%&pY&CH_Sq*3F!2tY>@V5Bubf3nkNc4I%=D?x_Lm-K z^tFIU5*Xj;6o$^?&Ykk-*l0OREQGpGFbWWZVrCQs3)$#&mbeT{?OGziK1=#kMmbS0 z{sC{ujC`Y08J)Xiq%LG%n?dJmMNs$CQo~D9gyv8VPmvEpvM-f+DN81U<7Xb)cKh5mb&jSve%!DoU3IFw2_SBD+=g zd6s0nwYEgOlIEeybZAvo>H;dIUcqt{hDYiJP-NE9+GuULY*%~NGVRNr(O#r$TU~3q zYOfWJs6katmbU|)cUbFIZ}5QIo5pFSY86@Rs>~IZh6)6I?rV${?+6C&2o}|CsSgzI zsILvy7Kx7?>swk1@mE@1y}sqnrk0ZW06oj_Gk9lnK>WrR`n|cfN&dzc`n`3_ww8d( zKUMy2XxyYzO6oUk-O}1zv?I8wr4WB5I~q4P26vRvC)HfCBX9@(rce6Ko+xF10J$5J zTz)IU(x7ietOPtsrp*R@!MDZ|HU}(1ewzy-`uinp*13Xng=~B!!bOm=T}|dy2sD#I z6rs=$nHphzyJ4;f$B6d5%(svongWE;n#RBJYhcJP04&c45}W~GeF*ExH%}ETkOi-Y zCoUCSmFcaVQILL+0T^sY$W)pP$C$56!6}q*)**bkfaBH!YrBLwv*_549V?h`hXPe3 zL9K>?s{oZ5DLAW1f(D4AMRe;j1*2HPSW1o!)^N$rrTxqoJ5;2KmdK(j*fkQB9vG1% z$QS#vPn9o~5j$D)VG9<)8OWV1(@K9 zo8_f1@&NNyv++&#s75YT3QJRj*=>QN7-bP?AuGEczHWlfGD&kBx7(2NrgLP07+n+9kF_7yv(1#Kzay<43sw zL<|>tL%jvk>e=`$%ofwvr63SaZv?K=ZkoPLZ2V^XH2^LUHii&|N`p`hhD!oljW`GJfKTey z4Y=AF;lO;cDB6%OZnLH`1~>LJaNA^$WZ{C_ZK19%Z87VDLFT)MjW4qYB}l`=^%L-A z`mh<=7fmQ-vSY=XRm-nqzM0;+)-qoc8`;%mjZ$Ti6_#13_(soeeD_5j0l%@xZ^) z(J4K$TK{K{ASuC7Oclf#7-*Bu3-&c3Upe*6rleAFQrs0TMmZ!Io zXy&CP!n2V}*?hqtSL%QjrYjsmoP^2mM&w>7xZ%hGcCpNDi@YPri_UzR%=|1|Fo+du zfz%(A*~o`9JuKv?k(o=rqg+-GEg7`Kge6)R3~$}Ap0MiH`YUaq(+z-gVK=-BgoNIJHx$usB!(cLpsEAJH?54u~b$+pIs=Lyu$Hm05oR zuyWhs7uycM=9t2bIqgy+0=hJZL(&@>t=W`WOh_tyAQZa1BZ+Wc-I0~JiYucL^4hnY zpwI+oe>;JQm9lrYfIiDDl3#9~2C=l;+dDf|$oVb@C%UNub*qTI=?S-5Li+(L!Ax9_ zXfeekW7M&7=}sqiU#v_9?m_w#^*l?v0IGI6dJd7BbtXRij(#x~|0mt=59G;~ z<%*Z(ua$?dTcy3pdPX+OLHY&Qh3b^_*s-}=EjQmf;>5wJCru3$GA+BF-?>N846*dq z)`5?cgr+;oWoOe4wyMkP_eMH(o^3MEGi;{rGU6x-4!;X@LKSFvNQM?0+{I?=G(S`l zo9ij(0Jy0}ret+OPV5qHO#>jQ>ZP6*ij4t}^qCTWnN@s~e1IGAJcy^eM7=;BltPr(9u_OLX=?L{< z&(+b{P2U=!-)q}74d2u@s-qI$CGwSE=2>Zx0n1g`%U{V7&>0baN@l5U3t}ua8@kIr z-PU3QhNCD}T%^tHRSU&~z|hI%K~ave*YI3x%`mC&IKrfVqHP(5^kqn$+CW=0(Nr}g zP%Fb+UhD>}$z=9DRao6yQ=t=MGa5vSjy0vgka_*|hE4F2w7sYnFRXenRbr8NfuRul z1)5>%#g(EGfB{Ry_Qdk$D!eIfXh6gvm31)^${Q>yE|tC%=~Ehn@CZwGmAD*WRi!C&Ta5$bSQR zfZ0b=IvAD{zD(JNWALRb01YDa_r7MdpZ*m9S!Ben;?Ix)KcN(8QA!aF$47Go|4f&H z8^zB`5>K@Lf@B7r-{x7@Ou137k7^Jn5o~&f?n9T$s^`O^o@o|6hs;{3oEOX{i3a+l z=Yg7`qPGQXx@~%j zAj(Yw9uiv>sv!j_?c7!baSnH@u8keJS=r*lUP7C5!_Zw@;5Wl&q5<|f8(^NZ z5!N}4FwVIZwmG-KG-nembDD6zxfymjTgcsWiqQsp%|3paF^s=a{C$@%g;C5><5!^M zNgVSOB;mSLkX#MBzXtM)yO%Fa_K_3NFd5f>nm)cjA77=9KcJ6q(Z`?C#}DZPcJ?LY zTvw9(hhU;Cc@B+EP3KpqaNas4MaJ+c7{gD8abF+>tBums1AJ+!A3r{UqyE$wrTj5G z_+Wx}ZR!>J^}q4uEx7JYl;hU~3|?kVTu0vm{IZEV_|l0m{-S*4#OGo6KY?bHPQ1vk zp7RQ;0qBjy6M-Wd?_3Kx(_BSf-aY4kO?CF@}CR) zAt#@LK4d{(J|`dK8;4(iD<4ImWB9d9KFCQHzaEtjSbN{w&~7TZR3| z0y)7b&e30H&T=c~Ab?;r=jcyoiz4mZxpFJ#p=|n}&W7LUMyXn??8puw0+kxGFyD;} zVCcM-2Jja85CaQuzfxLvrN-U#aX)>8=%bG>G(Lv`|6?q@zvWBGAFnXEj8y}?$Ogz2fyK@%e zal^@q#}lwQft8LpF&2W&6f(>L2}7R>ePpW!W0%(b&!3L}L;P&~;qu)J*$McS!(?GY z*=dxMMi9*~5ve@4wOT!wldS}^TBJzBm*`_xvHt0?#{>rYi)vmwQ}CUF_`{X)6E!c_ zBm}r%OT~9LR5w?Af5Vj~(8KlBIDQv3ERVlrW<~P6=J*C_z9R}FQ@Txe-6_7ucUami zjaO9O=Twu$b2%Upw5sM-@p}LB^R`QaoSWF##r*QIh5U-Kb$Hg|xq+`4dw_pv?6Z8? z*w>Ku4WxY&&!_SH9=~wx$N2u&`2HKd2Ew$D%-dj7u>bj&fJ+#i4Y!_+pNjV_jlEAZ zq2jZF->P;m!fyr}r;6_e-Wv|QiO2hg#IH9G1>T3@nQE~7vivU5(BXig-YoyCvwI8o zp9Fw6bY3&%b)B;DV4`B=FEMWZ;lPO(0}@58r}0{TeJf5s&dMKWHRSlI+YY`~G4e5# zt9voo5QyKH24u2($J7#|T#V^!6yX4s$U;4;Gsz;_* zBmD)Pj_N87JOezWmnmh;Sg9gG&m?*&pu^x1-?0MdQH94-a>>F{ZKg;o677|V@BR`= zzQL+@e?iZld7?IvD+7Q?5#T`>%xV>J5WfXX3*cYf1-{D<2S#8u^d%e=*RcHQuw0kOuYYO;R~WW@xbeNo@(Bv&9>wu_|+dt9Nmxs;CTyxgbCmT z0K8-eaHi*jmdq$Oi6+NepTbB9Y-q#R-R#H#Hr5Y*H+FzuXM7Se@7Ujge5d#oM=#)O zjxOTYfMTpYx|d&m^dm?+fV3a-i;kYaZ{#^v&et5f8qY0wcH$Yw^DB7%2+yD6`3ArK z8`tnFzOkII`No5IK7{8{JpT~Szs2+K@%%NcmD%X^FjW5c!dY1QG=~&Hcr?K!ADwE^= z&p!!V)x0*m{k`GsXJKf1c%K1Y^UsI(y?ONLaO>OX-M%vF$B#e-HT@S%e>v=X75fKw zKX|UiTmNe$#~sTHKMNoenO0kFqc);_Ro%kBZwc-_T}%MDWcDJ z>Xw&P9IORO;sIm04vXaUaNWWTEBLd+k5tBA{C24*iAf)CdIQ#>YtL=#v1co{WGi>i zR%Xdomd+M^WD)CMAMdM7Pd_DEiJDEBR;>vaLHSJU;ilu3(K?QNyJ-Vf@%_3*zzOlg zC!6Cxwq(IiwUkvw)@rpZd|PE4(@dFg2i_d6s{(5ruA2w)a2E5RtZunE{ZHVxo+f&t zDQa|D(j9URVCZ7&zz8M~n{rt+KALdsbJ;WEoYx2sYU&Z%t;`?*>{t~{w$`>E~Km77z1t7o+ z@Vo-gt3i;j;~zS97hiU4C(=NSkHM?;*j_xN{K8|0ArhuW(Sp=CK8_vd+m9vi_X4fp zg9Qm#{R%%O9ID-u`~iFmIR&|4xE)h^)dx?S$N-j@lQSphVkyt;-4R*=g&3V zia#lS6ph(A9QX|pvLpviFkx5OY-1DA{5K z2OEoLmIR&}4xA;sA2DhFTr5^B+1elQrMFL7Fvle}zz}kxVOMgbO zTQu5qC(b(7nI?`@9mXUBE`UfO$ex_l>FB;P7TF-%t(nuCMAW-59*U^9zNsdIx!Oog zj-8VpSpBB^&ehICDk3ZsCpf)kXAOCHGHPX?kaQ=Uq~Cg5v}f zTu|J=ivVshPF@7Cdd~JDfWr*@Vz8iDRdMtB4O?sR4a!D+&NbJ7q%)6=ZWWF)lx;?F zkh0NN@lUE#<@bVOp?*Yh^{=21dF4;aS5PdDrJ&2C_eye|2e(!ZrT8{-UMNO-hV#M& zjxG&z@^EROD$IY;1#G@D*^~Ya@@CcMa8G}a^l*S~?CbN~vgGOGa2agDGkqMa)r5tY zgK6)t4SAH|{d_XKcc@>zz9j24P~E?$TbuF=u=410nVVaHv;fg1vfVAL~KG;%_)@Q!7BSr zo9<1aiQ@+dg#46!QVs{#BM`0VQ;w!&;D!JJ&da(f%IRPcS^HV7bFEE*v2D%u%PGRz zxOYBSwpX!3C7AKkanO@Tft7gzu3^3lHQ*_1{3_2DuR=esg^j&;n^!a6IySz|Zs%{s zLFNn-!(kdi;zB-UyrR}InHlcsfq@6&UHdk&@jlk=j$H@H^J6Ox4AmCUO_o~T$2Ez>@d7n{M)N9{EY<#QT zY};cxNYJ@}Ug$j+S1rdh-{xQ~Z>`+fz@8kjT!-7m#D1jL=XWuXg}G&CC+_9L5UOjR zT+H9?ciSo!3!0o}ggOnD6M8O(n9U$x=gBVThaslr>X|iW>~W-5+ZIF`3f3U;di%{W zE7st`4%;Qm=|YZ8uEQ1d>YAdonOc^~?v;WN*JOHrsr4x<5Fwp4;k{Ter4hP6jR<(n z^%-MlE>G*qmV87pfcdigWLmY;LdAtUVqpo^pr9|1nGgBo zU0(KV{`#=F7PFVOG#t+=W3hFFZx?LkRStRppyZX3ScuD4EU#(Oh^snnr7)kYX7ls2 zz|zgXo$7)gc~_DJs%U`aCikqK^vFB!EJb8PYcOH6BZ8ycy3dc3cad1n`X){Yg*<*)4+pj%DI3+!P^5Qv|Q z*T|aXis`8fpdcvI-7iXBR)*m_)K54|rZ0e6fje`;>}Ka?iA&@2xegS}Eps-AZsFbw zI65@LX@#sPJC!qx_V+OPGumT{CIJ}5@Q>=ws1S+0!Eg=Jhx$>93w&?#!6 zWuAsf^l`{$o$U8G=J=1Arf?N>s6Rh}2=vOaY)3@hX(lf+vX2V6TiY*4at37z9Ro1c zcdARTkX3Sp{MvV;HCuYs<=3Cft%a%8ViJKeS=h#9Zh7>|3#n^D?Y##1cc+N#COcT- zl)~^&&3kdLTH~HwU7@~+zWP_Z--HBm_;eTvfT)AUZQ(p_goqGk7b}SJx(Sog4fDI!A-MCY+4{!9PSo5_+^D#wB@-;9L&7_ z+7EKs@rE1#Cq&Q``%GrwvDaQ=HeXBKD z&AJ4}ylYNZ+o;Pg!kwh9n5fL&yCsUb?scPv5@XXWSENB0KyF+>@wRy znD5Wg4i4Ydj5pZ)y^*{Ltw)Q%gj^1KPY^WI z;Yz?v!sc&9=Gs0HwxM^W&fMA^YKNn|gyTXUEv|S1MsIK1-nuSzA#o_TZy6gMG(Eox zDPp2UwIpi0C-JSou&$m`J&|Sn=WQ$vy^JXs$DLeTG9eHq0Zh??Z zB9#v1Dzj8RXbbOB(=AnWS$?$RAr6`FEEN7B3Z^=HQV>-Z4T?H>g)pZoG;ZfxjaGiK zaVKvftA#@2K_u)&!pC?E8~vs)fu5%g{bj9$jN|yL6kTWRN<7EptN((Z>g4VanpW1J zlu`;IpF-N4hArK4c3BlSD#H`VP=O3`VC3l0s{(dC`n(CJreQp9#SLlm82Qs#rYXtK zAIy0_N5sUT!@+L;^+>UOxxm^>YYbKO^w^`4qf<9;b+^Pr&8pOPsuWE=QEy zY6Qu>)Od>Q>|lQ~-FOmix}V!p-}Q5 z{rU_KCcn&sZ1kt5Cu6At=Jk}F&0LqdL@lPpIr>vmIZAW8l9j4?LTJV$IY)mw2a=*Q zS=E+q%oode6Ir3`m@?<+&zgM}n~^z(3RxjtuvfqecgP2V>EM?b%_B?25EjdUn-5!| zn+QicfFqo@7t%ds#e}wE<0X8EzmtN^3p9_s=4TsQ2|H!zQ{ymGo=f$`G zN*_ME|6DCe?iShloQY5Zs~KV!NnMo=hO6B zq1KSv&R)4XpAkbVoKL3F_)evTITZ_Y)g%_?3M@>z%itQz!d!-h`Jwz4CSR`BDO;3V(?u(7^ zDrdKh7d?&qk*6AS_*HDQNd!l3;a1NgM4NpJ(PpLT5lr{$QU1_Ju~1>B4wr4dU6 zPvZL@$E$OnAr2uePo^-0B)Q(n%;bI+|4sbt&iz05eF>>0zQG3R$MKJS zi6DI*G$9S?LO@zTkYd`rxK-E}_eBoA6KR9tBy3xhQwS_)n&bZzZ!M31bQ~Deo(}Q1 zqWkJuq$)mV5INs{`3k;PVe3p~oUffGl2yt(4QrznpZy`hcn*CaxkPO$#v1uNau%x?yPLx-=7$A|N@W~)LYeoT7~Xzzc>62E+fNNQofSrc zWB~SMv``s`ujjt0}Rr{xsDde{1vp_t~S%@LNTr&Fp(c`i01k+~@jV zerG>Tg`&t~hg(k_ComEgY}#6O)6e){W|+Lf>%}3gbN&c(#cX zr4bUoNpHO4zrYu%fEjDNd7ZMvIx__`S{r4-r-@=cpQaqjd4>FzhJKCK`-cOszZiI% zIwCvji%9#%x6Z&(h4Pb$YuUNmk?}2^@RmfuTQt~_Lii3HZo;g23nLB-;5(xG{;8ni z02!SfZas+sLE*(@W$cJ9m@d9GyrKMX>j|9`-=wYJ;SC*eUMZ{Mi&dhWU(c#vsLIzr z%?uTNW{Pal@IHfv@~5Kgxxm}!nqIFscnMsa_U}9G>%VaSK7&PO%kCERPet*7A**RR z8+R~f1ELQHPW&0sB9Tp*6P}UggwKs5ZZlvDE-W0mkG#N0ps$3$a|s;W+7M=W%~(4u z+u#E^b*vi+kMirrV*GwG(JdJ}!rKj8c{r0Km8oNJ1D-wh1HRUH5q`vDC-Cl7nEM&O zq<03r`!(MEmShx$q@%@1I$D8iP3G}Sj$Xp=JGzcve{=)>?%@|69p!VAC|!}H(nqQE zF$S~2X}sgu9DXD0c-I|U$!|Kgfv-K*i|1qf<`e?SS04KU?}XRIPBuCq!h5%Hgm{N} zT}v0uK{4?Po6F-T;&rO>=Ai2o>RDOK2Fiv%N)!?y+=o9(1k;twi-5vMX0oyApz#;MFti%>hqXlFVG>vc zN=vqN%o0EtfrUX$vrPgWtnlVITH#gYZ-p1V$i@Pd*d%T5=E3%^%5u=#hoHB@xGhh+ zJ2H;Tw7b*br4?E=8LdeL!m*$!=YQv@0s6W_( z2#WQgUbz_MO(p*JjKp9Vx?Z}l8!?)fNF$R><1rH!&#;w&X;rm0GgKCjD{yKftW>UZ zZcHWsSWI53PT7>D<64BE6K0C5T*{|OfNYc?1I!mxV3f5Z1-BJoR=R*uDiaA~Q`_Ef zKMe35Qb3fc5;8%AtWJ-INP3l)H=5*QRr(fHx>A;2 z%C3@CyDSHx@>NCGsiHGwQHm7qJdLyP-$w)RnW?Hgx!Yp=)*!04Sc#cI+ua8MlPLBR zJqcz@ATEa8Ie@4k(Dv9>63!Jk9B}N6h!PCr-q@|hZ2YpExz;cWf}I-@;tj|C37yXk z3Gs$+@`7|dAieGdX$4HK10Il8P#|%{zscRE6|k{xV8Q_25l@cHM6eG!)j|B`X=DTp zYbi%glr>}g7Q>mW^HGehk|5FYH_-BX?D34<$Dhnw(&b$aVAC0x?;DvA7`6c=V(7!1xeeQ+OE*9?mfz~78tH%Z^fVPD?BD>Wv#omBP zG`TrU0?ek)oyxY=x15c;%NyCF+}O81R39P^(-H~yYAZ|MR5pGaYsk9^IGVI!Bm)|x z7e9l2R-SkPG}?V+fIx%pVy$_Dz#PjMoWX2kBJ(}-EpqT1$T0x`TqBLfEXz&?F(xHS zneS>g-o{|YmuJsuY1j}+HRNvnQU^2l+^rYrm4LPNk1dEcO{03Ugv_(yn96~jJ4ucm zAdVkaR)D_s6!rf4yunca$)aq1ScD#j(9z7scd)xA4Zt+AsmbJR+LW6r5qZR--OgYP zoM%sU6A6+SeVfgQWSS_&W@_Im7r8>8Ov;&} z#cb3>H>pel84yr+g}SLGxO2ccVv@s+AoC{i1AsY`2N3GE0=SrsNB{qHO7O+513Rg; z-@}o6J?c24gvEaHidhO6H`)Daa>HopX={fqiy8=w<8IbInQ;`GH8EjaWMDNX(lWi; z-l3{ycH3mC3U&?(ZslerLd55${5?`gps-GX*i&GjlUZ`|5FhdN<^3}u@|e1vjrXz# z^X?T{oAyUK<(?_M_OtWaBqZVwlmMEe}J5Ns|=i zXHSkpiRk-?m~EXbFqef}>39p>8eJ#_L zr2P?nC6gkAqBz;6(1O9NoaJ!4S+0hIj*5#GIy!}r?cxzikYk5{lu7I+aVrp_YIp8z z>+ecWE=glM*?sv=bpgffUyseH(!FPl@R;RG?4BR>&^c8LoF_uK@2Cc)x0oci#?%HWV7xr`XVzlHI-JAeA8 z!EeMh2fY$eIpIUN-otk1KaLU!R_QkX(Ih!|$D|ZXeSSE@wDfd^B9YEsY2BNnHvoSh z8^4d;oqu=W-a45IdNJTZHvR~UOri?7Ew1m6M8e&gsj2JCob9ZgFAEBXr9AmBm~4}f zgZumqp+P-Z%5#UbpzZl9Fcf8@6YN@CcaWAE2UlFL%-GcqT-@jbVR9v{ir-?|PL_UJ zMM21L11y|BmZbrn>0;=zo=hJVQ#aBS?(gfA!y_*B!$HRQC)vj*QLzvYO{cePb5WYG z*^U8|2pNI9^xE0IAGk%52uK0Ltt}Wa?DHof|M&TYGQ%Raz{k%QE5`I}%3sMyP}CKJ zoqCpva}jDMB!k#7%%V?EW;CP=4;AmJ(QXXtAe$(^R&f(6GQH%q5e0aJeh=6C^bf0Z zF4{DNHm%QJCESO*#lqySAjd&0LZJH&(kSG;-4gs$kaVjK2~%|v8ihVTuF;TQTME6< z!cU5Er3-r^7kH|`T$q%gA;If;pb^zWG902EYgOML&rq3((M5$pFc@@Cw4T0bgBr!gZhLybmQdlvl)8HtUi%uu69yeb%7sT z=`j$3xRBAN8iy-YeW7j1ktVxI9kYyeGKz)5<7G&B*=`N9oEU+ zJjuap>=Uln8HifQ#_!GlM7hsDSv}4#(BE8U0;}n;Jr+Z*Nj0OHjAf*1mu8_7-JOMW z;uSUEIk}rb;m>eyg-0&3ju`WVwFbGO(4QP+AIYCX+PgHF;4&`Ef_hF@a^J~<`M0BG z5(+1%Nhfg%g{2pcv~^jh@cp@DC7bHrPqBBjrIw=2kL9*lSiN!uD*yuSXWLy6;8X{h zra|5f3kFcL+F8cQuwN4!=v;O#t5+gW8Fmc0^I#5wAm`4583+M0cOJ|?FfFooa%{VE zK(wqY$~C=o3x@W^f;Cz2VaZIA1D57cm_2Z9R60+nm5)J%z53CZj+pZCU7@dtowN|f3;waV13!H zzkV2zPK9YVi+(|vyp25aJBd|UEo}O1_u0#dON&5$Gn?{7*_0a!ZMGiv0)8>+cjJzG zX(N;sX3aTpVY)`Y(1q$V*09}JE2};Xu!x3&r9;7f zzUkgQ2sfrRhvuf)g?-tN<+CA%MH~$3FT84H`>FXMyZ+d%q%0yB60Uq}f6zSEw;9s7 zmEYJYPERaF2khE+hoUkzOm?enN7%Y#%bqsyBiTn`=(5jaZ0f_Z2DjeXL(wUHp;pMq zpF7mg%usa85e@R;Jgd(}&UseMMs7p+sSK8;aimup1xB2N<``SD$GriHbs;86{rz1+ zmub;NxXj)2?CRKLT9q+%&#&TMXYH}GQkgY+wvzT$6mpuJyv53NK^Oa&C#dY{qOG0MKqc2bGs#_%3>a%p$WaDZbTUVBxUuYS^Ykd{?t24|59+dNqd znS={vZTm2XvI8o5AkCNj&$w}j=onMpA~l1KDxXpUy+#Kvli_+Zl>qi;$#Ry6y=x&R z+0GB_W&HA7B@=rCo?`{cgCu(ijT-7*g!Y=dLp!nI1PPQyywI$8is{ovr3Uk3D_)Zg zpfmd%VvhT$pe-=Bxhh_itD?Nnwk5O|uy2EGL@7IaM+fYzI+eE#^%zm9GiI_@9B+mX zN7!zikq5lN)%xsnQT<-TzC;v8a&xQBRnH=5#cj$vhnmO&M(7XgI=i~GI-xhK`9YC~ zdb&V^>ty-`gi$4ZtGyc$ORp^>s>`C&D%oGQ+ugxiZr;6Gr{C{e?YR zU%1agaRhFJX6D-xg`p90mFj+i7lgae2B(ZR`*AhxT^&;9DrvGf*Aw z8L<1(WR18Q4a#^8G50qU};FR*` z8VS1zjRP1sbVzGU;d)_Gm0VCgeM4*~$=S8i39E@>>O|On`s`dL*trK9toGfC@HKf% zUC9F*`vQAej3H=GO>H+2ql!l@XpJ}_); z7tPPr7TAj0Y)YNM>QkxRp{@=9_M8rzybuP_Ez*hk(6&cNn!Q-cnRb1a40g>`b`wSB zZQaUNz$W))WuKH;J{y@?IOx$EQ$;0?89?RjCN`*yFBp4RSI<B$?7f8+t#OO{G_Wt|Ju)|^Ded9 zbRBM}_o~@bgu>llwBt1;C`?E%mZjC>n24UVHN@D+qQY-`4y01JpYKGnuV7A_g=CR)l|-F^5j~x&ZzWerRr$K zt7^Mi$YK72uC#Y{1v=!VYFSEA=Mp-5uC9e38~1pQe%3Jw$pL~cJ1hIWYGfmq7J3RI z)?2HxcBfKlM9r|fgF4}Y{K`$c@?4GcWP-Tp%Brt&t4y87=4$o>`9V^rjJX=!>)GmC zz3YPAbX!kf9_{v&v%^@t1S^l0+r0yv&?yeo%)twt=ZQ^kKA6DCwmz|;Nq)X{dhJ(n z3@JKkIVc1t`Gx)L-xWDxn-+qD&0dS@co4dve1>XI51b*TEaR@gimV9PJ%XKDBQ|8D zQIYo=5Yz#C*aG)*;IK_?-8jTi*3n)%IG{~bNMuf`n{q9ZkliZRp&`WN5j8GkG>u zCKNnimria)D>Fl01ogHVJ^AOndO`&pdooy~CF-o(l2)u}Oi#!~gJw{e%pF&M-=g!w zhD(=wLvEeM0>7Zp7AMSMW@r{Z;HeF%55qMio!w?%;{>|9!@L`juxb~!c+h-(Td2Fk zeiY^eQB3+gSco?}lItfQ4&tSxs}DDX+hOpXmqIgSH~n(xWg1)_%>&^K-KL!Ay(aS+A_sIlIStYQpQoXaTW8aUM>@ zsTZd!{rR+;CVo0(hr2|B+rBenz+jpN!{M$-sD~0esL3uJ!qa(9q3JNP$M%s2YY}wjW8w>>N7TGUvcc?@W7kybfg2ve* zlWA*2bt2$-fLk9{s!?TLD3*sO)YYD2YbsoKDO+NkZ`!YEckLg^ekiu3Uz}Qzj?+m3 z%wyqOTA|o0Y1OVT{GK^IP88Wy7o2^ae==)iS~-SBLiKC zY~HPBUnXsG({}fMaBcUe^K75+t*1B_LPXRT;hAjK;(C960yM;=$Yh$-gxDC=r1P%z zlar~nRqQ`StLpBdytOn=2Y~-fGkY^VgyW4xk^rJvrZN2Rx=xHvh z>*97|kAmeuFDP1oKCtv^KSkY(=h66(;J;qbA}e~=XhpL$)UuzmrI|FfQtV~ZXGCjo zM&zw2y{&U^`aad({=V~^plqjS_sO1-*cfedy-?s9MT>d!{`_uQ&oyZvd)f>j9p;Ci zC;_t0q0S8CTVhiuJP~CHZ><+eYjxqCP##-VHo^(`aWw3XExU;UoD~qXnxRmE;;!mj-yF zU-XgiMiu8Wkjz6(aQ^!lFT>7yT%EfIlSKqh%-1qC4}BMIUdLhbmQ4Z8V1UnzT~M$1 z*%I8)6sT`(Z3^3C2RRUV5DsbImTTPd;C(G1`sdI4yljK(Gff6Q>p5#-wk%Ef?zmG- z9p~N9B!u@>X)&axdQp$l&IAi;WueWC+yV1s4=N2H*2^d~I40Wa-JuA)3Qct|^(lK7 zYrYoRVsE9E5U zBhKS21|=*4=|f&~6WLu)>Cz^V=BC^0pbhx(T+$RBBaDcQ9UDpADXCPJfRT0q2w{Kj zWipIrDVRK8m|?p^Ue>G(boXJ&5)&3@BzgpFv4pdVVZ1u1P-Az79e6Hw12g`R{T>!4 zxR?iZyZgHzl$t6@MzR7<%MiQ(X!GCC21r3G#AaR!dG zAhl@#9-Fx>RZvjaehN*$CpEMwEp68+cp0FC!JTd|^stW+C1q1nvOduMEq%-bU-APLuR{6!3p)7 z+r;fB=b_}cxh*?YqMQLOlfr8Yg$_~ZIFi=W1`5=vyxAkx)bv8HwrzwN3SIh%$`G=G z;Dac6c{#~Kj!MD)KIU~KB}+@ivGt6?!7Y1RlgQ?-mGDwaS?f|tYA5$^U5}gcqr+o5 zTdI*N;9Qlss#v@x>&CXe{D`q#YPB4>dLDbBlJnn-da2kZqvZ8ehY>Kn3(v*Cu^u)R zdI_S9%I^;$Ml)|1a8-@sxYUmCw4T^g;jQZvf_PrNuz5shQ5zFtmY3Urr$vWZHr(9} zr-OXj6Isz?-|N|&ZL`@D!L5x%;7KRW!fatFTziDeNZRvnSQu{Fwi~EiA%76f$Tc1` zf%NWL9Q8U~O*=7LWIb>HO}b^Yt{*~C&t5{Q5{bNy1gx=+iQr@YD)V|3rH29|@97D5 zW-)gFb-0t0MUK3VPJs*AcY zYq_Syq_8LR5^XZ6a4tbDd)kX)rZg62Pvp}n8dw>-T#l7vr*e@X7l$JF(yg)&yLkcL zP(Cdq@q{*V(jWqt-DU14xMg=Z_yIiGyu=7G66WrWe57#XDX)TZIox-_M5M-3Ty1U> zj_XpgZ|)7Z(^xq1cpXQEjoeb`bx?~Kv1D6K#%~f_4g`jnmk6>IG3v#>Q%)1N9cRv& z5@KFbDb08MhptidmksPUs1DmwN61!iNiAFnBb69_LWf!6LmaVnSR3c&&?@YI+|B2|y84tAM0@Oe!PS%y3X z(sA^?MNwHws?2oI5^vugSxVT^TJ75gJZ-&XLCKHBp+ws*Qz#qs+K@OO_+q=@b!_8s zYGRA+nYF{vMQBt>kE4}c(7}7fSV829d_)Z|t$_^3N*ZY)sJZTAbJ0#uxj_In#vHl>!Ir#;0&Ld3HijZ3E!->m z1l@)5o?s4m9&hCv{O>~^s9WEO-J4u%vSKf%TsQ)2AER7@9r8rCg#Lhr(_VbUgi1 z<;j7R^i0PE9AO;xm<^<$hxAX4@Ip@rcZ03X3n8U5RzMJH-la;D=7^}9(YPbxojIfkEbjarqa%kz=7LOS|!5%(qn9eZh`b0 zvcZlOI7<;O0_7UrXK7e$CjmjNcoh}fBS0bmB$O)|ab%iteM^`0M}Z*tQ~Tu!SuCj; z_x^~pBpaFZ5=ysr!muH4d+e~M6^n$I&kPMSFRCgww)uea)Sa~0ZYlX-GLo0VisF-c zQi{CdVtSioQC)iehVq2zCCVxhA1;C)2Jvd^5;_IyP`Xg&9FUoZPujKQ95JK z$S9qqp8K727!*A(ybZ*~wF4buL5X&SJYz)K5n=X=kP-GgVUxv`B?Cns5k^~LX5@cq zu9rR9J~0T&m~Md8`iZWkp`Lj>g(8_G?TK$c5le4JSg41>t1RcX`l z=#PZDa4oBF7Zm+==IK3yX=Mg)!`-*-X!>Q@aKMMK$K4zfYGP~}*hq{Ow{&%O_8{7= zy;eH9fmXRmV!UU7LUYX1xtiJ{yZx|}kb{BRY;Qthe=!)sn@jvENP6pFWH)?&{5bgO z4$?Q;#+pm>fB6{xja2u4UJVn^kN?P#I)U$Yzqx)lakVUrCCy6#JCw!Jg_GnM4#EvS7o^>--BY+h~Rdj1dWDy)s>4_hEuVewYbPnA-dB6 z1;t<^UcX$SLLZ%s!vLDOf1W${`cPy~TaTIhHJQ5;^UK6#s2$Ca`RJl7@IiIW z(m+-&`Hpf)s};i40K32$mRad9WTl6F3!O8XN|pgu=bQti+9I`5t+|9+Z2I*)p}v8- zhkJwJtsB;hg4#{bG~_=T_TqJ}lGsRRlvGF(?4?^uZB9ElP`%7@ z&7Iu>|I}QGSUY6Okxz>*m(wigbrE_cr{v%?leHJRp>C5fmx=oA7n=*IGo`kv8^UPA zolyumbcx!&z1{6C?J#WaSKGjz8jX9?Sb)quL`DeY{aq0?{^>?!z#~nxyac3YrMBlc zAvJgQ_JQBR?jR?S4vJ8YjSTW>RkPz(n(obY?O7KZqT4920o$*P%<82xWQboGnJs<9 z)Y11Q5M10qyMisrnH_jjR}qWMP(!)G7Mef=Gqv7dOoiyboVAEZSk?ufW7K8XEICoq zMPP)ND;hH_K3!Wa#Hm`scq0xudLuHPRW7WfH?h0`YVMm07+nHP|2YmNx>b|ea@EkC zbOw1z;ZpZz;9Mor8{V5n(Ex=Jcf*3pZAa?>sY1QVXmi%ba%2s+Ke)BCr>m{KvmRgN z^2mji9)uBgsW-A>#r2u*1Wqt05UgFw2(l;Eq_p{=ss~sO>=B#FSi@qgmJ2$m%)(G3 z5`j>f*BomF@au7zZ#z7BVbeD#h9U zLOYNwr;qD2!TOj^TXIi0Y3ZCGhDjysS*AYRe}5M&pG3@hVa**@+ce#Gf6zj8$!|@) zkgay6<`fLnt!lk57qd(ou1Q4bQ@b8lCgB*)B*$uzl?5A{X{Pn(HJcG$U0h9pYn!_} z!Vg=?4r~$KdL^a{rb%;iqnsu=8l^oYgiW{%3Ye2TrFG5(dH_2R*Jgk;Q4DUGFUL~A z+)E!jV`CknduLtyrtCE&)J5(YCAMX@#kOo}))4)Y2jyx(4KSRJWz=lRDW?TOg3&TD z3s68NJ2kfyQrXo=3}jIdomKMRV7MRlvDSc>P>4FVhr(g?k>?_H5c?gm#Ztf^1ge+9 zii%DvumHz%K#HjRYD+9&uGZ6As;|X{DqVq4#?%u(laO4c z?fP%Pz^2%_?-;doJdNxbs8Nb+f$laC6y+CUpH!B74P9Y)gvcPDLbK_Ja{$yCiQTq@ z;7i$MdL_Bd9JRn`O`-<6k3p?`xAtH;ScS=O7T?khM@DSFcac3cjhJcMY3`8p+pI-u zfnY&Vm_Z%v*mKmhxlQsmw?iQ|a;(5EAzP}Ei$@!qQgd_m%bL;bm&uLwTk-~XYj2i; ziQShtQ`!NX9jdu~J9ezL+yk%@Gcm^kCmai{p&C^?S=KlpkA)CU>Z~)@E-C4}m}pYP z*VGy5#a5Wm6=HTGX5oY=U}X(C$i2*FvK4|n0BxBw8Ua>{Yvvj009mkDQ7&!Q^?(Rz zhC)^mTGoT`6QXhrzOHR&HY@P5rn&XMjzWZu_+BWM$1b`xW1&5Yu{4xH6REE0ve} zh$*BIqo3Q@EW)2;7s1`wjCC}2uxUt>yc|ublvKYZj6+K`$F5E`4V0g5trQI|p#v{uLkVH!iLcWd z`{=oGx1j^!9As)eW^Z=}FBky-9Y&a>&p- z4%JFzU&`joGFqWvmXXL)%8jA*GW&dXp~$|%A?lhrj&(BcCF~-ZSF>g~iX|CBof)qPo$?KY7_HN_F9LK`-4RD;BxZFRWp$V1h)kf> z-BMp;sZo|7J-jH9W4Y-fx5^^b_OUZpXu5G`!*25`V^QJgdN#>23vr|%nx>D!&3dG5 zfLTYA%sJNYd4t-Nt4%aw z7&(o9Z)mwj!k=ZHwlS-2%Sb@@g5nG&l|4{Fg{0dGHgxu}Ba4?_&?b9&dNBOvK-?h* zVng8)hwRFQ$`6I$miwXhlJUoIwAPU$v&z|P4jvBUR_iuqMK8}1hZ=SHMNkK1_dquZ zj7MiwqKeZ@(z)gCX)A02q3;qLAnYRfMOGrzk$bh;7#2iAD^^Og#jrBNvU6G@p3e*p-N@nt=xn^CCA}wt@ z5ru`U!!Ii4hz-<(;Ji>4_Rttq45S-_|8~SQgEMD6D3i&WxF}u?^vQl#*ls z5~(c*Hk+1ZxYqtUZ(_Z`UXldQwulOxONa8diXBz2rY(@}0A1aXiIDWBLm|XM@6vk} z+dN1MF#By2VLd|JceZt#6XAe-KZ{j}iE_ORmV)VG<~S&GRIqZ9V@(T)7^329;s2xZ z^-MNHyk1*}IO^mOZf5wH%rKW#i3}_B-0c(NU}ifcv(003WH!AcQkXIG4a(J#;~_w$m%ez+Y57HY)(t(anUU0S_vt{3M81Cq*mh`ZL!K@3?S zH%=OIdOQg-L?&Q-S44!^c5w!<=o+xQ=<1>i;E60qG@3I_$3X8H9OwbxW+Shr)fW=Q z9UWUccTy?HmnO3kuQq)!^rx|eHVGtc4DFK9!UqSS&Cqj2zs>0F04xIS8Pd2wFF+&I zdkUDL^l0_w+W(rg`0IujjRpfIi_M-GX6JcC=8>jY=GV<4mm<@=S=dm92t~6^xLdch zZmyT;W~0+t0yR_R=kSk>UhPX1Fh+!^u!3&45oUt&P(!q^5^v~Vd3%_YuMb0q*8bqM z_AsfBchMomly+R!Bu*sNr4|62>ExcSd_yUgjR|p|~J`p|0nq zYUo3z#2M*4uHBh#3{f{6lS0Y2DhHA@sq z%#~EyFxOX1C=+;9g$j+iqR^cz0bC1o&kb7Yk+q062(*h2z$h8$Bc1HDfxayd!z-3l zq#n&HGOi4wj?n>@Kr2K|bI?2$AWjVLrnbG|{)ngoJrMt<-QTsLYj4l)O`SU<x(twIdW7hP}M6 z12kX#@TjK5xFm=QMvt;YDPwo2-slJQp8o0do%lXaq)hh1#~wkE_3Z6)j8$d!LyD}6 z1jOPXqSr+vQp=c6jbahh&pQifJ{;*3Q>fIa3?|BqdBH@fv5-C%^P&C5Qa<#Ev78S* zYOLZz`;4`G=p)8DJ~U|f`OsdYo)0}{Y~({xV-p|xu+hwiVn&b;4H@?kfPKNlCB`rx zdf521z()g1lu5LgIyP5h1ii7$LUXoLXV6?UxKcE@%(#eZS-^(|*yuKvm?B{dlA$A7 z(~1`z76nnZv}4bn&4|oHPnuU{c#1jYSS4XcF0d)Iz^1U#MJzE@)>AUmZs6OeYx3z* zQ;m5%_9`3wzArINmYPv%zfpT5b&3AXkPq=$P#$dS-G$A+utSxNr-@TgA{V7h0XA8| z3}KZ>%5o9DMZTd4RWO}asqMvLdQslkw-JG?dxaW67Py+yafU%Rkyw(iq2wuzeTtgO zyh|vtf@q~&z+#cgS4-*DbP)p?%gXZNTCGfTZgpr28$bnO7|nZLZp)(%erz|Jzfg6U;WD^_{t$1lhYDoj?3iqmfL8MG;SRaO^y|5ow$#;lkDb&z0rb?!zy7b>wI ziE|pXv4@pG7YH$t4I)Ev0}*g4QO>@Fsk(`kvNL!#L5iGJ#MrG^xpWSAEBlvow2QtK z`HtgB46_ts2tC)SXSaHOSv`NEp3@*FP`>GGR?xze43tcyGR0rT zuL3p|CDb`3(VDA7o>H}o)J2bc+DEcnxlv9Z;KtL9D*6VNTuv;xoLKT!V`(rEaA3(F zCzc$~WXU6nB?l9i8F%oZ+lepVWZV}_)Egba#4X0|VB%7viw|u!dic;5BN9w3Fb4Th zv+-y!aib9pCN>xcf{E*mL%~FY@i{(p$oMjS9HWmvp^um7<7dR;&+ypGNrrF9IrMQ2 z*Wg<7I`jY(8goP2~{e1bl{7EBZ-zsqBPmHaXN{1qQMnEY2hbZe@DJ|J^EoLbL^ z)~6s#97sLLhd!DbLPe?Hr=Q>CvA<6Jh{s+@y$6S*!}nAs=DHB_(!d+5%#J!&Nuuu@J=1>tT2w_1Rk zqjyyGVn?Nv{u*i)rev#UDT_ZMOkO336f;&yR0$!pObX{(18(8MznQ>`jB^A#Iza$k z3lPBu*&r(pp|C`Mbza*L+L2=70DCtf$pphhh3G^8JVFD>!T~$-C1w<=6hHx2NO-Ht7$#0KpGn@ zD#E7_IZ7QU9q|hKH_urzpC?{!#P*%Os0~I`B(f14O}XtVKlXi*c8Fbh;31x`)zqBtr%q6bT6ia`y4Wr zurkdQDGOF#RtHO2v6znZ9-QqQ{aKm}Ikn1()lv3JWTiiA_9?801S@*I7_aE5Uz7S| zDw~>?f#@Yo3H~*mmBA0z#PNJv7Fstm zkVQ#y1ZAx)%eg~7R>4Y@C`CE2EYH!O?hd6@vS}8y zWJljHK<{JDzi)ke0V^W8ie6XQfD@^l%Zde7FJ%P}VkAIzKOrCAmXDvx$N!O!`4Ci) zh2)q5R(L(inqVn;F^6|2KydxhMqeaMhvwYkuAM8kjkU*wOkz+m|+Gt!4j%?Hh z6N`=QVB|(;FmbgJ3MLlOw&zxo4lXBpb&UZwdZqD)AWOzKgNZAQm%*itp9d2kGTs5J zHr@{=mL)F+xk+9TOkA71naEN@FtH>F`gc|GUfK#B28U0A*3~AT4knfpy(&)rESOl4 z1c!e(`ES9*%G7LpOI;jH)TGwXH&DG*sXHM`q}qas)u~4yPo%y|Fa83uNb2QaVomBF z>F2-vU-sSwJgVy48()(Q5Fj`RK~PYm28#wTkbr?;9hgiAN?;(94JZhK1Q-lSOlB|? zg0pj}Om~J_i!HX&#&%Dz9#5NEi)d{-6$DzXMT?bI+Cxw0f(<<c)TSZ z|0N#dfrhXGbH!tgc({%2d z@>KMjWn_dcr#yD#p?~No{kiEM5KB~qpxCv2D+1#^T0_1%gi}uXi?7N^G*cvqFg|%0 z*DF#A2v0Bli%CZ#qoPs^JGhgJ=3@fj0mlTcqF@3AQz!tijR}Ai9TOlb7s#jJZVCW< z5aB7XQ*b{88z}%tv;@9L6!!=8c82)dj{zpZ6uUnw2)*9;;7xYFId~VCQr13=`5`G| zC$jPVXim9>ghqko(bPy+k!ld&VPSrgD0uZ4O}|~wwf$5V{Si{5XTNGGN}xK09?TTD zk$O&pW9@~^4|+khTz!>Yw-v_K41nQ(L#TOdAX933lu*E49L7Ie z?bU(nCHbAeTm)SAF)m*Bp`v>W0eUY#?U`3tE|K|B!Xzx!}y_*0vO*{?@+JFoxH@TPdiepn(GPd0YtH9T!4R$A!?zaUt|? zT<94JIw?3pfuDkt6r7^q3qhjTCyVJns=486|M@MgPz zIq6cyhrO7b@K-^D!r!D%G*lJ-CB40e5rw-jUe?Zx{&5_&vrnownaaUv7)Lfbp!ESu zq3jqZAdP-}L`PbBi3HFDp(Mt{V2eT}+|v248PR@O!rL6Ax&&1L&u(C2^j0+_m%{Gm zDZ_`N^fARKed^(mMEwAxg+CLt8)9VQc&8W>R8#Srljn`uZXCs?FwSskm^FzyfAg0d!0J!9WcVjH<$oGyg(#DE0`2hLBI3+sX7^*Z*eS&<7j;f`^-1-2SV`{*OPj^K*EsB z?BFdN&2J|jeLSpbz?O3fp+XECPh5CSzzK9g*mr#33$T|3o&YKgJWH7NC?&j33BQGS z7x){90O^}sfApzQGyunhqD|{clZKSwbdV}2DLO8NM`~tcbeuWZ%D0hd0cE5>~&f{78 z!4&_vkHA`R^u!gpg54zG)MlDHp$xt{+HP1<&yNj!LSPff^uJAWTuOssqq(hG@W=v2 z20~ydg%&9Ns0^^yN)17$u@S}M03o588cOY_%CT%%JWk|a29{t_kEt922-kS&3@Ckq z?-{w$zBJmLfc1Y)XM7v4ab&~?THhaiHq`Ar<0yL#u7@0aaRm=J0suk2+?|5*$PO6ry!&5SwOv za0v|U!S$4|4QN2{OOM(8pASMSGd}ndstUdV7A+W{PbhdE4Gl*HA#`Yb=tjGLMQ8?C zkH5(G;pFS)a$GURL5s2#A{+gSuZxb|3K@;^EJZr~i_bHPBsL+M zQKn*Kpnv^j61$2rO+p5G#%CHX@Bt-ELk9X6pJ7x~_N7evUwo!C@{E%ll@IG0q49xh z!L>rw&~$I$E-ZYYkb-+DSV;jChvP{S7$4X|K{W*~3fc%n0c$o`UI4&v(dTdN{!;jJ zfPg?L+7etyZ<|5Af@iRyu&;1!r2)_`5}rjF}$e`p2{{=rBNAQMRcaSf?HCXm)+ z0<<3TEB$z4mctWM{sy^yY>2phq>(44?)D+cPeJP;zaLmDxqlznAj#i^)C zO;-ilr6yBAm72x|o{{9g5VE`w=#=FD2!OA>I&eg48W(s)Y8oH#OHCQ@ijyzHTSV>& zydlXS22M#$69T8DrfW#UJ~41slK&AnCpAq91f-^G1Lvit>jD>~rpbYBsfh(HN=?_3 zGu;h=D^k;qVEW~+291*Z#FFb3UH)36}s zFFm+dl6M5#B=^bSW0L#l!Tpl^7r_IP`}N>clKYKdyX5|5P?g+o2A`4KzXB0)pMo~X z{Z{Y=$^AAg_wHW@Uy{`cSo$^EBbx8x24FG}uUut#!-f>$JWIK(9P`H)d^zaL7I z+Pkl|1Fdwxi5zD zB=_G#`I7rmXo2Lu95PGp9tchD4?@L~`@_&u$^B8tCb_S`d&m8c&}zy3acHgN?hRr7 zSP1iH2x0zGLYTkQ5a7=kg2*>4)F8>jL%St;M93}4X`vQL9vQ;?jS96%^61cGlAIm_ z{EZ16kmRdEPf4;V)Goyd>Wox**B7 zgt{d;D|Atkr-phY`PR@CN&ZZjN%FL?QIc;9r%Ced;dDvP4v&@O>ER4Xo)MlT$uq-K zB>9eTrX=Ttvm|*I$%3=P(2 zrIK6_wn_4Wuw9bx3a^&r&xY4Z^4;MLl57rFNb%waeiTGS-V354dq7m=FMz1X3W$o_ z2BIQ+K~&^@L{z>Aq9Q*Aq9T6@L`8laL`B|DMCA!0DqkU@asWg{eiB4Q{u+pid=NxM zZYQGhG!d0=5mEUzh>EO&sK`DL6}f|m${`RH`56!u`8yyg^0Oc+^1pzn$j^bO$o~qW zB7YY|MgATUl};im-v?2Vp9fKqe*mH)zW|~l{}4n)J`AEFzX+lt9|2L3Ujk8)UnZjR zBi6oz`LEIB$blAVwFV<5Hh?V2MS_S;_ZqSjPYB#3xlge6i_AZ9V9DT&R`Vb5bZhVb ze|uyyI;|w+zz9_Tl`9!05;HW3n4yWp3{3>wx_<wX4s z>;4Yl*8MEt*8MMlTlaH-Tlc>LZr$Gn+`7L9xOH~|Zr$Gp+`69!+`4}VxOE=}+`3-` z+`5kdZrv{dZrv}l_C?GOA(@1}5q)Mv2cdC81EEO)7;Gj6u9MtfXYGGv{%Z#YA-(2m zw2S8diN6%~C`yrKldSgiL`{9=UvVty4&H!C7udDC#&l&=AD!5(JiDeP7%-PE_Fqfz zavj0TwFEEM0$x7VF{W$DQ~KI~$L?$G(n zaM=1R5AM_vK>^p1Uf|lm&HQ-Nzcc^j|Jn81tF7C2BkOl<-}S2(3plhD`Gm`?FW_Wa zz$vtVlW74bf7%6{Oba+U@CCcy8-RUs60PF+K&RcmFL0dJ6UsX#bXEIqia6oM=^Is1u-eIN~TM3|#vV+4}V7T3HVC_NX$3dD>X;sVH&a~!Z-RCKT z@>k_|O3#VKQ^=L_#NteHx;(LX8aW1?Se!#B;>6-S+78~u>Ab+xXYt`#oPT_AF+09^ zI+ATr7WE!qJOiJ0{9A&5fr~aLi<~x zj)6n8ngL&E)_y1RXEIi%T%dB+G{t;{mn}$jjXaS`nRLV>!IkTo_?zN=e=4&nb(rZ z6W8D^EAloi@-|((=_Bn{iZVUci)CKxMJ=K1qH>(SMw;)@A_{t3bDMwBt0!xd)U&*` zclQm8bKleR7o2g9R(`GJ>M~lEyMjtlvB%ntMg-ukQOdftbkpIokKF$r8LGSSBeo|^ zuL(=51Zl>cOdTaPJ*HyIn$>J+eeGH0&+Rx;rLwaqpfxBA<%6*oF_$$bG!_f^L^7l8&ZQV(KxQ z4i9?+s^q=KEbP9?${##y(N%5f-|*Ve$w|Fm-j5tgkw6lye$zLPw!G(>;NG2eH*;Nu zhncxXY5eFt$c49@DcLd zkeFu-MdZGn>*qyyW2TmG&`uFhe98SAbk6MDxUIrln6<(7QU%f?`L~{;o{OBk0^8F_ zW(w;)rSu@b-L}nuM+F|+jF=;_k8G|%tw?#P_N?^O zSGz_kc=DorW}N9F$=>{GO6sf4$Ewz8*pmib%&cr7>@`-ll0Jb=k1;hJY={aoN1F;K z*ESWFvo;mR4YLZH^H~*k4Mp8S!M7=Z>042MN~vI6R;X{|Gk}eCi~0`~^TD_JabG^- zeDL!w^1-pE%_q~_V-$RgKB35497Ne$d~eVv3?sJ$-<9NF`rzXI``|w$`OVM>N&Zy_ zFmPMP4U+tmj@#%hm)>9%e7pnpyf1e+kh!BqYP!7x2Km=n`yA%aqU{PiUEiQs@^2Xi z;}%mUP*`xvIYtBL?9S(~NXl{06E~oOJHKN1S%Gl~R7gv0tlh-?QwbyFr7#i66zqE6<;XE8s_y(oUkLMUv0=@RFZy`@X! zd-Iu@v28jr$^E(VYYrn?2QXAcy;XAi`PAVoHfks+wK;F~l-kgN|CBf7_x66=o1`D` z#owad%=@vJ`7zhLANjy}orbPSmn`!Q&av7&m=3#7rZLu)lIw5rJ2UjzanspKtz(MC zrqN-@9Bt}!3htELK+sYEZFj zr3wUykq8tZSP5=DJa{8hCr~RGf)W7DnW5&=yv&o_KT~mZa0@37aAuir+vps%*S0bD zZQfO^;c(7`hzwv+=iACDLwWxhasY$2Y0gm|+qC{O;ON5v=_>=FbIq7f&g8t1(4+%VWz_Y#A|K22VVnwP!Lv96?ZD3MQpE zv*4{gJnxkFyjTO|U10Ffp_yORe5}Wlk%jGL%qpG^1HX}%2(?1FaPG2 zyey=7Ecwcb(Z`>0V`qRw8806vOesEwT|N#7V9pWR;sad`W6rAxRIwadWXH;RP=8<1($zV!`@P8u0VzSzw+?Roh)bd_f0K$Ev`C9<{)U;oPA?pbh zHmrRr9Bk0w_fxQ|33$NPo>%@H13>FiOs&782_@qB3}#K4Kw-ftQ|tGl^9)Y7C?7mY zx>97sW2|6McxY6L(s)mbTm8FB%t+11?M0mOu7D+TLcOk=V-qtC*q*TwQxK81xOZt* zhU;Z8K{nAQ8Ol4N7Ogl2d~ag$m;n!5yemT~ouYS1AH;dZl2LHp)N&9T!ed#g?W#-Wbq1I5 zxW%p`0*E*U=LCJvb!#sLwVBSVJrC;JR)f??9YC;4v=5F|7erD}*+lI_N1nMq7bH0$ z`abOWFZBS_nvYG=cviuq{hFy)VJil10!0dZ27x-4cIJH~-PL061$I)xBb4AJp7a|O zz=wXS`a4N}L;Wk>d{D)|?t^IA=*vc&Zw|c`Q(PGZYbov_%GX8-U#GZE`h1-}sTLm; z|F8R?^M9jb1RC!^RZ0g5_Z>6o?JnSf4oIonI_?FU=~zvlm6T9NSw2UfO?JQ3;i1nj zAq%7HqY8v>0hs&)+TW`r#WF<*0UpZ~`pVS5GU+Q%|H`AU4DqF$(xwsEZr!Wk4-Q_( zRIuUM>P-~jm}0hyV`kZE5$1;_XG%+W@MM9 zE0(d@mNaikK*R|K47*8?6p!^JRp-2q3y4#^C1(}uNyWTCu|YiKFJ?_hEYzCuSdS^z zW6eiMP-M=Tyu>lZO`p7w{;roxM~;XbC^~(JNS^43E;g=%*l2wrQEEI%ql=-1aZ;nj zOhO|HahWld53hfTahldxjKpX$>v1#;Ue;qNRIKN-EroCO>`ZMwh5fmpYZS3SW(o>v z?oBQ4Blo4Ed6}+N-lDaNBxF7)bz($GO+L3NT9CyL z?waJo-vjTrpmNDupXP0Rfk!m&S<75AE?GZL0rQ6SxPs#CdcEF~7xea(vUc=FtS>TY z4f70IEQbN;pOi*_cH90OEjgg|=K!&z)~7I=@819=r|EFf4CNHW=eps%ju}ehdF4d5 z^;~4u^OSe9%X+j*k9-6TpI9DicURGZ6i_Vdd8O=J_N&>}o^0!>XtC5+Q>~|<{_4u4 zWwf4C%6ceMB!{(I-zKKSAz*1YxsYQ9I zJ=A66$J0~lt;z|H^#b27IZ9a|yX>siOHXM|!LFP=sh(XqN@-4N+1XTR2CNs+A2A6% zT^6M$weezVJ+9TGKZ)^1iGkeY5)fv$>Fpi{F7bmdiX61zx! zff%Ph(Hk&nkuL?Gz8gu1&q4*hY%oN=91`Mj67!e76(q#3h7cdz2qE5ALz&?#`x76S z-!`9@-k^|s!uM-Z6r6=5?}J(O_r5=qBo7bX#T^SsuwN{>_jjyBzK(VDxm9vM(VuE8f5!#4cSMj+x4fucmZ-BDwJ$c!?%rwFvzG$};Jon}(_vWPjB6V3_LCNVoqm`1=*}uqM zmPhfWIoVPkHbSJ&rT9S0UH9@qqHV0qI9~r*4+d<>$tDCq{SYmR?2*jWQA&!-%ooDM z_s&R4wV3%BazJUYQ%-rx`0O|r^MT#~(Sdf?se-c8P(zoUMh1$>7Y!He%BQ`Quhq6I zKXsQmbx)pRNi$$xEa?Wz0_u#ZWiL%+X^w)`jH@b#4{4+rIhxitC!!&>D!X!0dr%*d zJm-xX9pIA_QCAe4at&{OFejz!`T>%iMoQ*A!i1qmd4fK8(W8RK3+O(af!1)JA0 zN#heNnFZF^O)PbKc_72>LX1{HtD z&xx0Mk3P@QC*1ui4?)tIe&}urwgPb+ss|o9v>v^?i%B6_pHD4V&tpV(mDSI(QOr}Bo~nb$6a*yfF%Hy^)A*vj`?x> zQ>n7L!E;^e>I~1d5cb-?#Qb;j-a|>Fe5iE)2v~r1nZUo9UMyVw@#edF*~4QjIy+Zz zW)F-tVhQmM^O{b}yKY*gfgA}$#~}nUX#b75KR?od3i+yOiVb}fBN1=G8Ja3wNwXbe z$YfLLZU}IpP_4mh@^Kh(5a6bcC)8pr5M3R1H_6#2So@A8@75a?Fag_)RTg z^aD5)7R{%Nkqh`2mWt`xK2oG<+8dcKB9S?fx6H`fl*n7e0wv{XaVVUnz4A$+#heyh zK+KIIiCI%5T~I}tV!cXD{Llp^MbZUQrKk{0HptzAy_N+^5vYe53@_0TN!KMsv@0;T z=;$G`2y`|Fi@+C51D0+LSTHTv40{+IRV<{NMAB8bk~~Sbe-^4sa1D-HihF8QoOGI{ zZpH01Y8@`7A`9;%Bt;DD*mT3h= z(@d=|ii8>Si)NTwpXCWT1w}cgR&J03l_|*r z@?1MmbJqN#EZ24ddcG^CaS?;|rM|4I`Fy(WQKIYWx=Vo$qbS2Q2Ne|0FDi!q@hHz0 zp`PPg{toEeZNxHQkQvfv}Ux%B6SYfLVjy4B;SYgchJmxQo(iG86t+$f$vA6RLNF-d; zQY;OI;mUC6e>_ELTxm%rqEW*OI035f-gjRja;xtpcp*&Wlc?L|p;IuW{#@TSgh4spRN{*j zjn#`4X}d12SX^dhqK<_>MYQSgC#vNWW*?x71J%;p*ps?@WTX&2*`BKS8B#`giVEYS zqP3xbVb8?))Y#gF?YRLwnb|ohK1;NM2mq&HUA>x{h>m>(Qw3UuGnSC^iiu2Bsq>|# zuaRBr%j$Ca+(H4gUSCspQrze1b3X+K3ClqCt0LJiPkoi%pb5)We@kzFB5VXvuuA<9 zsLPjzHy?PFS%i72d@$C|_Tf^(DxVps%eRc)pbopuhl6faJ{O@WD8Z_HPf^eTOy&b( ztMVNsB=&oH3j?PGFQU)BE5KhQ6k;#o((fgF2f)mkpXSslg3{f3$@v)#P~o`o(0t4E zh`N!!_gbb$Ft@H3hdsrtUWbh(%-xt2=*4m4(LIp{#yV%~z)uZK)j!O;3oJJ0HPKIx z#WqOwWVa^&6;yvS^WTf=BkJ}D<+*pM(1mJ-PdFe|lCslGmWnFY{-D7RQzrKF3cmNR zfN$qJ)iXi|3yr90@!HPSBgMY#n>lX3ip<{941pB%&nfYrnX#XQzWdfXEinq&W+cXW zOLJmt@|Nbsexk(m#1h0-Bn;PET9{C(I57)oKw=cPkY}KYC5b7TwXOz=W@1;t*lmY# z9D5>{-atYhSMQ~_Qi|I^Z*V=>u0pcdu6ilJg>l=}7brMM!MhZMDEJV84{Ypq-xLb4 z4Y&I&6u>`XJNGOofexYpItcDr(9ZnJI2K<~D#g0-?Jet7&Uo&P@I)}E(ZxE-$q4v8 zZ+IFgT`;Ulx2JxJa{T!b99B+PGD#pgVaXDY>Ecl=9>NvSk`D_rBVD6eIqGigWqqCS zh#Cs+mnO+e&JyIJ)l`iuNaa7k8&Cc>^&Uw+M|cGOcA$RC333HtAu3d4=I`DjVK;gH z%FVi&u;slyBQ*NJHz_pO@X&fpHA(VGbs_~It2Jb|gD=2n=7%W8s}gp211x#0eIoPY z8WN30>KvL*B1cP2wXEIC{P23gW@oDD&%=`uhg3C6M(N zC_M$AbuMoHZ10}Q+YE}n_x1g&n=(9~O_qaNF^~-!u9@kA$Y(L~y78(D$zB0o(5tQ> z6XPcOe2_jNzQR8P;t%dU@YwPp{$TL-#KAwr8H6&j=)1&<0}H&WPY^hXVK`HaHFFYp( z0;IL4Yq(Oh+GFj}%8^xYrWrcY+)D)?!&^gNBe>DHouuNoI^G_}bJuk6Ibc>}Xjv4b3tYp}F*P5Q%mCUDTnG;6Fx4FdQ4f#@Wesg5B6_n?!Wty_P#;L<->r$%H7QsfAek=3QC6dtmlgN_F9^&*rGlid5ysm8H%Zz0NEn{fk65`zvgG ziQq0Ag@rhOM@!CNZ79cemy1PA1vjGYDmzUh*$CAJigVNcf{AYh`LeB0pOczqs4vmm zX$sC#mH^*#aFJOJdkA^GXmjT+&QwnGE>c4R^PruTti6Ky*YJrhQ+`X@7iIAzxW_Ij zIJ4sh<=5G#lwYL&9L%(o=gnwRs`I>st-|KqbzSyRAB2XL+%qLIwF=qY*V5Wepb*Y*Q4i8IxFkXe z2ptbRJ&wZ5vvgVLTU<-l zbePcs(GDc^((b(L!Y`@7{fmZ`~NLc(?N33TdRV3 zdR$#fd>ELq+o5W6e?{FTHGM;EmYQa&`w3(qRqRv0FS#FA4@>eNNld>({UsIq4Hbis zk)!@6RK)5BNcAzK`o<#FcP&y$e$Vm2Xg=LHk5bK)ip}=84~M>Td|Obn?_tUPW#4W} zMU!`s$G|?{SJ4_o8pM5@;(kCSUZMJqqFC@0I_^6|+L&_`fH4GqUc~(C_~wmJV1>Kc zU%NQ>Qq1O^q?G1)@|ud4yZ(IKvVc7gzJ$-0U?H$opeVJGFX##t8scrNy@&Z9(6$Pl zHPCjZr{tn?=7hETV{m=6BXB4~+lXf2f>L1Zc|_-K6gPRvUOuCgUG$`MjZk2NecLlu z-`GESO#l?YT`z!MfkM*nZlQ=FMX#EVXDyA#lBZ;JYSEnFFG5CTT&gWYv8JPH`cSMr z1*i6mX}yHQ6{4fCrk14_q%^{BEl)9al700mBc&c2!kK)H>nQwfIy zaWoQ<-KRexKe`VMTt zm-kvvbIPj_I*BE?&fP?a_xnd#)141_>Zf~LXW*ybjiP(4XCpZ|J>Aypy3*_FiA2Uy z(`D|Gvr#%r6xCV4IhTo%>^7~VLJ~#(w5~}fEJ94Mo*^*-4PO)u^D*cSJ>If6Xx+(6 z=lxOykp-S;aMrmSPcv85CF|KV*XHKNGi>*U$g~v~SkE|bRvM!W9%OZw-VNp5HN<(_ zy0Wzb?uMlXDD%XmYo12{1o8d6fzL12$$()QOsz)<3L^`28y3hS1doUS#6_OtmYhg7 z+uGE!E8Y_LmI&_BTXrnM|LeU1xw?<$3+HoE;1kpn`B8o7a5SAN5Iu?fE3`z z;F=81Pv08)dgzjuVwv9D)4M0LpqqS8mC~p}Sc4bM*$jK_QWRB~(MiFp(Ydz0SILf-YEzI`3T;q1@> zMU}VXPWr@kQ9tV_rni;!wuTZ=^OlYW5l45oZV0&$bf66<`IznNfR61m9WPV9*AW*w zExDiU_^ssrTIfIQ{_@brcK^mhBpV(ge)$lT+xrgPD#@=NnuX6WgwhRR*d{g{+DHl8 zQPm+>B=#Lb$5$MBmP$MiR{78`=<{v*{2k|zqDV&{N)_Kf)o7G;~ba; zrw8rzNJ$0;hKu_~o}*NGRe8&FxFn5^9FbqOaB6EGW$9h=^u`er)CpKg2|aeD(HqYe@dQP8(xQH$ zymMNeN)Ng?TXaz|9wTBL&kxwF$L#cBk4OM%acKUdaL=^SWOfF zor7QRH;Ds!O2x{2ldihFTE#(|cJ&a+jJR3yb@gXRVD10H{1uvXa$ffc#aTu%yn-{P zmL?jV_6=6#HErD1{>H7P7My9kUiq6|ThH?Bk9Xg_7QP<=tGnR)d;6}=t^mFkRN0BuTyc# zqz14+DSk+`z%-IY=j`^WZ{X+$%r8g)z1c^nL-ye)tNfbU zi#OKpVqpDAwOO`eFF&QDPu!F=SE+l~0_G}lFUVqWEZT)b8w+#)0*;z50!K!-B(mt9 z+DV?R4V+O=P2HM-O?plnvNl=wY48qBEdXO5xfy>`1+815uBXq<{Gf`};D3+NJ>_op5)+mU-Go)llj-!`H>%DELO)#!00 z++{M+I=Rk>T4KmcLSQwBNY3OPq;x(;&ENC(o2iNZ1D0_(*+SI0+@r*51x?9mZ zyTWtxPctzO5JR(%KY5un*4FdVDq%qPHl863(E0}u;X31OJSo1&%Zl@sq zQ2#nf!dc81Z4D4a4P__cb?&jEb~q@W!-yJJq0W=u#v{B=RHm4B$8;D97xJ>>FX*FMBT(>Y2~aUdKZG%)Yq(+E8N<4h%4Ndz3ya8;$!k3W#n2fWe3~LfkxL|< zMAFHGq?6l(+_TK(kEl~_pylO$e`27bYnnnO6g%Lui@eu@ABHaTDQ=0_L&SE4dN&0i z6)S`#0(5qTx|eX(L3;Zh?QdARZXx6}nraMUwawi?B_<4w-f)4mG+|n^lYRc~bbMuvssi>s`7!-#V zmNEZ!!fxCKstW>eN{I4gKYnsF(DIO+BzSQDZ`F8U>kYV}84RTW@RGp+b@)8wS3HDz^z}jzRe)xQ9(5R8og5!Heipl|B z%_nMIz21d7hS?>}xa39%B|J?;Wi!8{Py%Lx-zYoYoo}ZtYFaa{X30s*`az34)A| zQ?dJ=RBxg265CDZhMrcJN%Ak$`{}cW=3pnEgZ<23$F~OtB@Qn^iS@`t7?AbDy<%6C zr9pwXAtJmBTsb204P#&G2!%3fOB%cAw`p-v<)T5aJQ{_0JL^^tOx=R?&izK&wJj2H*4@<_9$wklmy7A7)*lqkxCc7$-Z8neKl|BQCYo@+c+S;qIUA>K1){ow;KP3ODD z=*6CSg|PT*YqX_dq+D~OYJTqxcCnXqy?lMVWE)vQzeH9a0P9sxINM97;#R1;r6wO~ zEHJ_;%(=&4A7E3DNV?aBU27LYHgI;#=^9V$9N^5CY4!v^3zI0edtK zI|9~T&;0PorA5N->G?7563!k!V%O%3cWJRcf!)iRKW4l0Ar_I!S*#@Q(2cx9@zLl| z8s(hc*)<&HdVvyN(^y;d<{Ih^-wt5nSE$Q*Z*F4#htZpe6cDi6GtaVVKdHSeAWu$@awVx>ea#ZHZJ?vh#5B#Gu|$Y`&V z`m^9vuQdDnRWy}!bIUkN+VXU@Jg|Nu%i~9DTJ>2P&jaoJMdKb|o-1Bkeq4vOi>9Ds zf)8(1;IsHm33US;4Fe6t{58@FK7j=Fo8SNg&rvE^+^?wbAda-R&!kB4%eUqhDpVy*kYOPO>SuY?qRt8pj}XAb1E*u4h7tab1tQRTmS8@owx@syo| zJf$>3Mih%B%@g%>C`>fNpT`>L zqDwohf#${l?t&`g^{(-jK%VMOh4YRkbC8sS^Jn;XB^yUqNwQH&dQwX+Wd91XQuYZ= z!NB85;u0=CG!xo1Q1wo|`mLGl4H67d9i4ZIKD zJl1}S`J4D6VBughwRLLG=lDWC!@C7r*R?nSYIGKKZ9?=3vpS4JG2Yro=My~R?I|rz zF}F9LG>&0r`mN+lTDCOLq67TaITVEMNn~#yJxwo2c!5`Oz=XKIIZcr!;&bvcosk-! zqZ-NI52*b*a0uX)4E|;fzybtd1e#(*a3?~)l$GhJi;P%NT8T7eb-FTHc^z-5#g8G( zF5XMAwsd8sQOC>Hv`gj}4bIsB&gK(-uZzPg<5~c>}nFEc(`Ud&~$F z+Z2&sF&FbXfq%6Ku<~|Yc1Po^@2y^yOdU&X9gg`drgJ6xh!{1KD6@}TpZ1a0Q3wk}C0x&xnK|Y(dRFkhFc|3ULeFPy9(@2^^7*>+I=P-oB-ofi)$vp zL2EbG!1OrQ36{tRX2uw~ug)?}&zc3|$V<5!a#+Nx$#R=0VRp{cGZwpU(O(PUP_LC) z%1Bc^)6g{vVM-V7z?g}nx-$(fv&f;?a3((riw#;q#a0{}ZJt!D0S>NCr21IbU8p6+ zb>}Cm#f;*-3(79zrN#M1T%4szAt0R%TyPWj{#kC1&JS)3oQ!6g8{CXf6u&BP4ASF0 z=d$$@1+~RE86273m|FlX(-95*TqgIA{7~}lO7pt-5owwm#F+ZcNvjQu$Q6E=Xn)jX;!QH_6O?L~rcj0_2 z)r;&L-iaYL;roQU0qqcCq65!_Rv)7XymIVugFYVR*T588(F3}YiDQa)XlH7pH3=WB zz1F|E8~=t*e~Y%aJ`)02m|DDubnm)Nj1~Qepbh`?Myi$2Mii^QOsZr=A1KLvS*37&Uz#7_y! zcMba~;R#?OG`|XnNVH~?o<=Oc2{HQi{x=cDE4qmfO0#PU7UOZVldv*JCbZEMvs*FS>B2tby2NuCO|54* zbkG6#H~AZ`K$xJE8r`469Rlg8mK?eq?u$P_5^CgF2q!eb8n``WXCXJPRLs+CIDv7y z4L1fTVA81--s>)*94E^H{&&-rE*|(fS&ZoHMy&{a6yuIP$^opg0c%HEehaQLIHg$f zpyLOGm%Kss@(gE?K@7|QmnfXbho>35>1;-2tWt6|b%oty14m-y?>3Ju-QD;hP#ZiE zF(75-TG6l3bVL!8ONUK4gj|g;o}{4^?MlPp$E_b2x@Ph$H|kk935yiacd_n(YyAKd zaLG;cKi0L#TlSpZj$(a|w*YB7n@;fa8G{GQ2Q~@ehx#_uw!o%(e&P=9SU+`F8(T4!TR$2N z5^4QNBe@!8q^o^_u_D;bifw^^eORhK*bM}2n%;uxuuZ>>s%v~dk&$7gxW<6AMlTRV zf<|=aKSq=2td7G^U_P{!0tlK8e}Tg}r#D0$B+7fRlc_L1C~z(S7gPkaK+@Fj=>+$D z^--y*fILcXCSz=g`b)Ym1rD6k10Mj*!1+@??)w5Erpa(W?SL+>jeK&H4%l_uIu5`C zwBtoo)o}vOpB?WLPMU($L-Po2t*2lMVWK+1U3)0OWA}gI(3k1$8}tTU^WN~!2%nvz zd~jV|aOfOrVeL?Zw0tlE>&FDv=*lGo0ubRl>o$6uDR7idc5OPz9Z!?%Q2iQ{D0#Ks2kefO*=@dM`4GD%+ ze=BDfs~P^oJMK&qo0aZFay{~nGh7TugJ~InK3cq>SZ{H z>LvKy%YtcYB~~Sxg1W$%hiS( z+155sYb_Q3wcaIO1Dtr3aoJ^i=?bJYPiqC`(7Fu5%W&y+Wa9bNb9&m5w+QMY3t$bt ze-Pi%%E@}bYxA^LQJuW)$fW#6;jz32Hr@q=XO47fr7^XvO`DP@Xi8{|+LC#n;JrM` zGp`|ZCK3d2ikihpP$)85J~eyN3w}t2O{Fkj0Lq5D>0VGTF;rd^KL-CN^{;k+6Ct2hAJ7SIULtV~IK}HLgg(dz zh54gC{A}?XJ_j*gP72^1zu5OIloxcgc7^W(EFyG=ows8G1yFDAqnqnibX22Q2Uw=p zJNBVO$2TPRS395uYY}Md$5NBI;~o5LF>A-g7C88W&AS2$Xx*&ff!XA=P`3%Wg&R8x zby1(2P;M-OMS-S)^sHiTwaX!4AX(;mUyl=~VxVOfHF--lJ)Zc~4r>DMj3(75ovT!D zqc>P?`8m3!q`tAi2?Yr*e#~+U^FPXajC(91-a`I8xLQDX!GO+wR&N~5I-9OU6!fG> zca6a+N9@jYQfg21kdo5SklKJ>HNZ+kTLR1SC~Eu~GM$mip6k}BYrE(sSZK&wn13(t z3!N>ZyH7Zby-fNZ{syYGD?N2rhS#_wyC-sk6vWaSBwT}TgvL&rGyavT?b5E#;7Y); z*~j1d7*{g=hGie;sqgX{MMlk0T0bU*j-~@oqoeXqRNRc0+Q3>l12PD9>1w-W>HPu_IfRI21SXYs1^iw^V95`qr?#*6FCJ)?1j| zr-s<}#po~!?RSOrQhXp1;+JgSveZ%U)EbKnx}>t&rRR;*vAC`rGrdWVpF}OMa#WYs zI%bws*4LxYdJ>-yEeVf`nuqliUYRz7u^o+_LQ7S7eSM^^vD6GoEU9+r-85-YvkAj!7aDo6eo<*@D*lbg%34&qrlfRup_QI#__VLFS@}0! z=zH1167h`}`d+zwRjF0WKT3QTEm^9^3@==~a(S7}xZb|BG!_4buV1okiG6(%>nz*PF$LG22VGm)AKLx|}sd4kyhoGi+gdM`FI10ir0}{qooy zynJ3xzvU%6Af?_}SMI2G+A8ZdRpMv14O6u$QuHcjmQmpjpO`42!QF<3YKvO zn_}1eR(9h+>!*Pc8g!4)3XKwlvRM{yaQ3`@d#nFo9ZEWkTFKG+ zIES=GeAhF>bz0$cQTS#yx!=C7pEWmk-koUeHB3h4V43onp@?-3TF*Sx^W5O|%5;y{mu`5L9pMwn6+_}vo7nQ zhw5sRE~J@tE@q29QSGIb&X|o)%{V@{i5VJM=UpuS6V+tdT3)@Sl4{t&3>mC*4YPlu z@(b(h9b2l!K<5g2W7g=3*$%{352D9$+r6x_gcS``x3(W*X6;T;O|wo-&322c-dVF< z`^K_OGO$iUN`r0^bJoTRu?iTRoplznyOR_ss;PF0DXHLNnw6)G$-aE!w#rSw(P%f1 z4TzVuaS&lB1)Ibtwg%k=kJLGr%}Ubx;!0-;2!dc;v^BYQhy}~)D>ar!D>#mI&S5!8 zT3k|3BQAB;)K&5UW1dE^&Uq{^NjU%@AEV=Ql}oBCDjTRx2Q%EjI+wD=$;&RnZ^&+` zt8rEHMrY5`Cb`VvB%ThdT~k$4$Ki1uTb;bRn1R92Q|eoaXCT&;$aZIbQW!;7;$alaE<>Cd0bP=~ zS*MFtCm+7X!_)ZZ=4wzj75&J{9SSevoR#Is$)a#pe%4fs6Xug4Y-7}KE3X6A)7Kst z;QApJEUl>nYOiu_=QtH}dmHO~fUQYBQYu*Jr~{6I(8AX{VTh5P5SNn~?q{7gc5jjf zEalauwN(zMv$AewWj%I1wNTeEYo3lC#lE%GfSqLq>$Q&ejeOWy@gg4(2H6=%jONO7O}x&E5Ozx9r9Y%`5gvpo}KT}q`vYiAOVY$GLodNwATzubmeN$x#VQG zKWR0FT-NrLc(R5^~}NorhJRaNtFQOzb7mubOa&K!zfX~5BLnlik=>tcG{S%ovqm*Tvidp2$>)TueSsBE@F2jtrucQb$J!&Xdk^&1BMEsa5tMj^h)CQ%|BB^ zJUQ2D*pO5bUsAoJyvjjamt&~hcrCuR(EVtmYDi{Wh4oC1_LtU_R}|INY%c}7TE3l{ zY3OHPiBwd&76fN7h|UK@-<2c_AljBBOCHBfgEj<-_mRQ3PX1hU_)mbC-OQC_v4w09 zvvV8{$6Q@oD^xu3=A!7_lX?*f7NHx8k-_G29gi3DbtlYbgEK7}_Yu=Ruq)=smXt#; zE#FaTuNmUvIY1W&o6~u)mSAsWvs?S^ZxO7pCIy5$avCY4tZc2Q0si#QbTj z?KAzGr0k2z>npW->@be%mb2~LqSTh2nxG|5%x~s9J*p*7z&(AlS5#o$basz#ui5ch zJYC7Vtfr!}hHZ@>m=h7;3wKtRZ+C23SXWoRGplxxcBVqxlVCFGr=@K$U)adj zYpJZN0^h+^!gYPBwevhns-etqBpPxCthKtD$h@nrzJ{ZCP=@VEN4~=Wa{)K~Sa(2C zNt0PK*eGYm?~S z);t_>FA=3!+fAv~WIc_T;s#p@=1f{m<)#OP{@Jn>uMxp!uv&BDy%e~|2rLAV@`?Dc z@35;X%j+Uj%;9NhW$cyPYc1uQwra9hNwU##)V-wMy1mx9lbRW9RF34URkOX;<*Zz3 zU%Gtr=6cu#Xd|$)q@8e8JC|1OsH`ez?r2s6h zt5{fFA@C00kjeH-U7o)9Q{zbd?05?l+&ZWM0+6X}=TQ5!tg->#1F<+a!TBQ=uhM%` zw9DEuQxl9Z14B>gCL&W2BcPCw9qh?u-LF|#v7;O)tU}0%AYBjh1xye{BMh|27GYHy z=I8*#*=|^wW3}nAAM4l8j1`8voC>Z;?0TG&55_8gGlcvuODm!lJ!7e z_SikYJNe9zd29*%AFXv@cK^xdKYi<;X7B^Fp5Tfz(9|buIVlST6C-zq(K#q^T?uXr z{WLAidAj0o(4__0n40>0(uEW?68D{KM1!`LxS!$1saHwVsQlgTbIK)2x&p;oD zqlWe4oo?z9M;K{-A@?j8!j=_NP31n#RQErX$w_IAXg*ofiJnpY03q<1WBAB3M?zh)yOn9&jHVtgs(7LVpFAP>yUD96Ple{fZ>+;(AA@n=JJ*U4J z5Oocq=gE0G&DKFKru+yp5{ZdUqx}z2=n!J6tG%;$iDmh+_3PQjq~LvLKMs`()gws?;Ig|F(N zw87O&5r?9|TImDcc3zhDu%nJ_Iqo61XNL|c*>Vrs+-%*3RaWiT1d#!jMr@j-n()FJ z%qo$>h*NeQtSbpt*#6g&0H4+)S+^Z^eZkaTQ&Z)1)KbHXz*)drK_>{4s)4Dq)T~zg zzprK1HY_!{l%Tb4ZNLFe?iHG}0|V{6%Ao)Tjr9LQGjVRCgMs@%-neYjP)383N_CJe zYB$!D*HzG|?{cTGOyueZ$l_JAvt~QXw-ha3S+dY>wOgjL6-gV>kPEpG)56p>^rf>R zi)Il>YQQb55&2^!l;pL}8Dd8{3oJ-D?3*k(b=5jUoO5Nxau?rt1FlKJQLcWra7=X? zd3Asy%uD_5(EHC7#sk23?U0>d`q%l?)a1rnpCgKBN$|7fbq|vAjeX-2^mm1;vTmof zs*7v~!`!Fg@(NtnIE;N75lKsoqq_!H>M& z%Ih|5O;&Tm0F*ts*g;o1=myJVt<(VnDMRSIgVWz+wK-?i&D9mn!YM1!&K9+hQ!lwJ z8?_OUM^AjUZg>ssDQ|GF$`*Z-xHYXiEx!@5QBQZDG6b$T}mz?&Oz`@25l$u--rB%3d zND|=?>;}BP7?z=b=K+>nj;4wd7=;1q`d(5%qq@ZT8@|CH*}0aPpOr=Ot1 zu?PA7C%f9O%5b0o3~lx3B!@Wv-$&AlqN^eBX|#H%YiJN!o^)Xt9H>I4Q08YczS@y}BG;u34&lCqMT#G5tx;qrkvgn!#$n{f!VuM|lC z)6>2gNx@Kfvk(}7PkkbJcAO&PE_|!kaM0=Bl<*0d_q-?@lAARZw>M~fc&q~nYnz># zT)rZIP%)ZC<-$d`W(Y6qz@&7O=KU?N*8sBjOezL z8QL&ENzEAWt1GO*6&e-Ar0x`0st)F?qw&;kKPgF28an7~LT z8&DJiNyx;IiOD3!3pi&ID(QjN_R*?UYT9Qe*w&||+N!m;sVI1RTD-h1R-e{BJu?BZ zEv*#N)|&Uf_CDv#eJ0TN{l4G#{hla$&e@l>_g;JLwbovH?R}#Bz*9|luy$wcMInjc zf(}ZXyV`D-wiv=((p41zrrX1(Ygr<8?c+9S({7)`{JY%M0SR3Oj?>mjw?tB#y4mhD zREL7XKUC}v$ZjO}i-~3T@`@Evel)(L_yp(fo3hgRK~Rf?OMS$#?Q~uTC}Z4+1gk%Z z9@+a*)FlV-%<7`81$nt;M7fx23*v|dZH(T&Nu^81PeOD1Cf3b|%bgvv1d5%Zw5N=W zhKy#jDn&csS+Y7kaf#_iH$IJ~wzPZzO|Pl%SwojzVW$HssT}5XMt&4qg&pqh#(%}g zA9Gz#qVd)5c8Va1bW13-j1H=iwjOp+piYfiJYr5w$V%5Xc`hFoL$L%V_+U!uPC3a! z35|kXooOA{WNB$Q)-O+?dOH$a2%uj@fbEMUR*7_GsmQu?BXz2Jl2y}?hMXg+ktX0& zl_c#-&z7|bqVmlKfZ(4I00W((NsRTIYLv>a;{LOf-i=?vaReL@i*RWD*rmd9}$6Jp#cx9+qa9cEcqTN@4B1+lk= z#)g`tzG`xVgQw3rbP@U4&9yu}illu*&6jHaT&~Rlj@>^sAc-gm{v7T5fB1 zwPCw4KFM6hV>->qN_V1lb%zr!LG`U`YgUzp-P{*fm6gJICgw)exN$cNbhE8#kS(&t zs;db%ARtQ;vqw^sTxo@usL15PsT8$%ve%V7T^n<;M@G{r8d$l3NIVtHc%1>g+j-lWZ-cX@j|XqjQnC9a!FC zn*>WqFySsvtFGGX#ldx{+2e@lmShTxaf1=teM45djavQ6A##3`;Ns{qj6f$#r-H1Z z`-W)UDaj=6wjp_mgEJ+iI9WQalxhh}g|n$7txL?BM);v&G{Y-j^VolqaZPK@I2-m} zmxlbLy^T&$5h5iy?_h?7o^=-g&kP93Y5}e^mM3)uRuCad%)AJNYqRiJEgfH z9pv;!2WoG3wx^voY5jnOm3?h^O$83pV(4 zcdX#%iqWVV>8u9wUOUnZ#84(($EIS)Y4rk8Nm>qW2iL(ILlabphKfe=gSm;!v((=80emDf04(JA<@zF_N4yS+8qI|M(I zh6OKg#Oh7XHR?=vT3gI$PZg&{cPUvfWS!{WG z7;ZP0Jik$>!8&tHcj%XxX?SuZ*AAU&PjTT;R^)J`+x-adjv1|%*kQY0%SvaXtLUPG zJ@V8ql}b5z(@hBi7C!Ts5>z@l3Bx#C;T_NnXvMxvI+QzI$!^d|doES(97s*iJh*@@ zj6G@Q1{vs~{Zk{-p~so{gBl5s`*bAnoK2@*hc9y+4W*^!AjZXwblnkkFPTGcIxi^$ zj$D39L?u}^rrn;3@GzBrhN59f7KJHk?DaHbD;C~l-;fPXVn|A4R!O2zUS;%IIu^&W z0Ec0S6cxu__K5F!z-AToJr+B@sn#mbqd*b-Rc1OyfG;7s#a>bjn@l&^)-=MgVbuQE zuC!h(65ePi)D?SSZyY-o3)|6vN^3i*jk)IX>~y4b1}nOyRnk-J@Q3XK&QV>(p57&k z>N4nev@p}DvQqF=;fUbfa7w6e6*dLxP?8}6~6yfGq66fA-w5Btb3jmpWjfSRa(d`*0wtIdu3oDyxw8phg55fxzU8ynlq z4sDYi`VPA|B9B96vSB3Up|apPa3@}Hw(vAZvyC+06vrW`=((4QMV*_n#J#w#c#@r@ofwjl$@71 zr4e^#-;BYD>DW7J3_uC6YLk8fkio`b&x8j8Ql1}!nc{QN+Dobm#a^Ghd$ia%Yi(}# zprHOe3Ocm2*f{%C*$7(^KIw}|WTeZ_-07Jk_YY&QA5GPk@WQ1%U0Y(!Z>hjWEbk2m zVY7!3A9aio9jPIjUt)||My4u-RX@%5?$`~Elx0TwMana1e8j>zW?wy3Lu48`8yQz| zQeToNvt*oqJtO8Tpl1#p2Kg8hSSlFhci^K((t;Ixy-TcJKNT3EU`M!S%B|?c7C|T5 zG5VPn3pnxZ6^ScKg6BA0B2~v?ECZL1zU3ynykD}*F|oV)$*Z+VV_ekd9rg5pViJ4ZWC~&BjQ!?Jsy2~EFFee{ zTVJ?H&Al+z7Lf)9JUhCb_{^4ZKe_;^qj}UsE$D*$3lc$zvG*izj}45;M6z@AgKU$x z@*5sOQJXHK=I~e~3QD81_KmKEdNn7Ut)stS||K6(O{&8os_$d2^wIwJ;B z-Pk0IJl(O^2`2A~80ZRTM`!#q2sYFJRsiPRl?3r{Mv z##$GXHw#lB$wHfsbUEp;1QzSKNJii7Hl@zDq|}+iW=GF~#Kml8xu;~gA7;e(swF8G zp+2Pieazk*%BST}&W5E*ScXy#kYQMTMi#QuF_CDi z8T13!HrkAy44Mi{E9vpo{HEB!edlQCvQD6gy zMxrw7iHRZOEO}Js2%JHyiB!p;cPwCYQ)UmdN|70~mK3&dA;2&;M(|fEME|9%MO>jP z?N)Mvr5JU2->xK-a1rbsruvKvj*2C1wV*=rg^v~Rj_PpBi*Qq69lcRT1<(+4seqB{ zNB<=pN_49@y5)L9o5((Le%8F?&45D|Y|?jyQPfS}A4-M=J$EcxyJ>lnu8eUG?!FXR zUG(v4ESI-5mf=xOk5pJ`$M;8))azcb;F{<(ffEeMmB|}GO1GGk^00uUvJ}`e^BZ$m zTmqD&MRIW^wgAMrKzV_h@Jvflq73@`7MO%_7)ai5+0lMg)!J2MIJ96(-Vt(+7Pl5i z>=|3WCcmR}d^;!wDlnnxEU4UeHfl`41J*R&(FIx(NS1g@K}W($V{k6);gq#Isf-iG z_2@B+;7L#>W19oJUoy-gw_G+z1SBOB%#R7=yOs9)iH}_g zldOv^OHmFNUg$`Xtxn9%+3m?eNWEDHVw`x9h%ns!@FA(_mg~7Y5^eybA`w}cV}+e4 zO@3K69W~Q;Xz81F4e=}lX2PThrLP@=Ozid*l1x(2t#QN}cbentSOSkJ*;CkAh4OaO z7a@~11FVU(51B8;Q~>QoEZXU;zTAT}2p-?H4yz}~EF8ThR4C2KY7g&Obkm0@qq6Cc ziNi<_TYW-xW4A47PDvKnHpKVR+-3o~$u*XnM}XI}>m$(}M;=C2DSsWVE*z|jyeS|4 zX$f0S_vm)gl_!J;94r=oE@6b}iQ@PLxjA94YkvwzZau$#)>*(@Y}}DA{azMEw?lZn z)Z<$%-Hm$L@Vza#wx#1L!(UUDUXreKtm6z|(26Kw-!W?8i}>Ox&_F4&mABP{q3D}C zu|yS-ucFn3Gj(nHIs{ub$?7#SlDMsPx^ecrzEiBWlT(g9!H9*U2KtbjULaX}K;e?DnV5P>2W*#Hyv95opR`D7+?w)XMCu z%ZYIMNLAP5=$;9$6LaZ(N;-*L!e+^(x8PGNwry>7cDnIlQn82;qg#y#v(#z1_P&T+ zAWMnGM7UHlo!G+h%G=EX!?M6kHbXQfLDsNg=WjCaZ_y+gl0X7Xcj&?z=E@24{FG*%#*8J#PP!4aA58VnEhCMH`1@}y5xI9-~KOh8_I^BRA< zKg}$R0i^K*(VY4pP^h`Tm) zpDa_Ji8OsB5pK9qGM$8iIXs>kM0eftR%D!mj;)bk+nq=!b9Tbg=64Kw78|QZq4Fe# zC31%~CEWPOVrffs2V-S)$8fZZ=`eDLhRMN{zIgFj%+__Yu{B-zXlFOelZvriP%ukn zgR`O)gT`_J5pH~970hyI2_f)9n3atJi!$o_h5N=lprX0S= zy+Tu4k&awK6*oAUw@4N$WarC@4ALxvY)sb1o_;-)a-2TpuSC^`C!>@l7;$plWSb_!5B zYmkg+Zi0&S3rT42fAwbAg5_=A*x+nPky+2#VU9JtYE3`KxNm`(YafHURv6`H!$ua5 z3!_P3PDVyue@{esMsCSDr4rO3Tr*usum-<_%_*uij+Wxe0Nu|CXI_%FGI6p1@Gj(QW(cl0QQ*IDhRQ?5ZAbX>u?MdrZPRczlQ7}IF| zoo^)K5_X7p*1Z@7PB#BvN6vjAV@j2Y~m-3mr-Euvbaj6G9boE8pjDrd$$uy{Mat& zSw*c8d3%s~fZ6h6qnxERF6$@X~r;2>Tw8J)p#d%Msf2IK4ppFBsD(tEyvBdP< zoqlXa2J@H-L=f1YxiX>Nl(*5hAZZYjjY@O}fHsjieaG2AD+|C{OJtih$x*uvpDmzm zk_ucX0M{yE$IV0<{Y~iHRu5x1q%J<8-I{;|v#?C+1^%*eB}P-G=o5g9UAFp89DB>eqh3mC?Wsy-u(7qSE{q(NVtct3*NRRl-kEIcc1c8`YDf>qJ%OIOw6va?Zkc*j9r!HVPzb1x$C@YA^%qqtUGaEpo zsc8($%tw`=mFrLjKh5&zOY-N(;>V~7G&e&3^e%aj=ouNY7(~eSaxu`^+W9oH7Xh2v zb%g(NM_{VfOi%6f^d)-w3O)Upo?fP>4~aDXOu;Y(BQzXiDagmzv4PDjFh*xi6G75) z?g{IwXp7OniOro3Y;4>vuLbR#(AN$;IDq+qA{H1Y@jo^{2LGL)1tAKujX@iyO%sKF&jMg#V$kwT zD#4hG5hGqpe|gQFqT$ZFfux+~P8t#1=Cl>$LXY-FeDAq!>-gqQa-0`1YuVJw<@iJ$v-5GY~hnLa--2 zhFX(f5q8=`^)851@mGj&VuenTSdqeaT~8@ON=}72pY+%{h|PsMK~7twf$7onL}pXG z=&|!PAv^lS*l@YwA zT3m>MPv&&c^;9r_bkM3?&rpR7xfOIvLR4?65j{~vo0li(1?))4si27}8llHJL~ONj z6h)6E5}#Q?Jk{0FC@=Z~|B)5xh!&h-1M-c-fRS{_loETB07%s!x&uTYGBzVNw?fS8 z$msTPwAcdEbN3*+prQ<$Vq0l=p-aoEQ3teW_D@W(YBqxvgGg)G$jHt3T@Ll_QT&qn z_6zZQE~^8(TglF2yTtEIb_l$L$#;0Dd-2-vhI9f4)qQ;xuLZNDQ>rhU&5SgAel2&~e6I4|;r9_30`=ozkjEA%SG z{T9K{I7eXo(7D`OI0RLje>7b@LdX^%;pUJjDlAw zc$0#6Dfk@)e{uxM*}!MB0y!g~sWVsXFQr0koNn+CAee<^5Ua$Wz8=VAH;C69to>Yf z3NKM3XUxh$WXoW=Q4{TKmk*y6&?ZJtAX#xf8x!3`y<&@@+30Kl8glG{R!_-e6U1dr zVltz-dbEY*ifEjl5|Np#Sb(;yz&edw)su9bga#&ChMm?u5?cjE5rYTPN^CU6@%486 z<%AuAWuZKl9o=W~h6R^Iw%Ox}@=`&EOMc_Z?AQdk%eD$52wzER=k=nZiP5J7leAE% zP_(*pqZiw#^u40=1gSL2Hb}*TB^@0pB$nT0wl(1x&_nW8q6T`60x^Y^IA z6HqTnE+~JIhYm;GOA@YB){XmkW%C=1vT?J7?ftV@2F=i>5M!N1j0Q{2bG2%Ez10!m zS{r7))=jU(UWl!&)iBL-wV%?O=`CVhn9{l0KZzk(VIc`#1_lzuYPc%64je7Gl@eUY z65I|h5>&z9f`b(IZHoI5#l7YTYzqFKUjNLM?ob9&Lo+BS1|tmFkq`p&nH{>r5!f2q zNla=F#XUw(|3NJ5B?{i<%AKJSU=|uLF04|p)_c#r3}em`a37#I$5ru3M1C3$m)^*CPe8&h$9n@xt^swR`*Y{Y8}hQN{y2;fHQ}&&HcdA?2AL zlZU2s4)Pc(BISWn8wDo^Q#IF|dX%FeCI>YukL85bW6D&7O!P0J&7KKtys6Mqo~jr? zBlw#L2Psn_q)gBTquN7pRyHB#ZY?r?u@U-bR7kurpA{IaH&Xp15YW@(z)pg<70vSL zEVBnw94zi}`SX_ip;Zwgr)GQUfI`BN)@?vX#=6h8>WpFO@eu+EG(HDx^;t@}&_5w=H0&P$;o zQ>hIJAwvj|zDyZ_1s#!l?Lw){)xfoKw8_NIK0_?-YDb_@TMLCs+vo^fq}>a}i*&t< zwXcHTXoHTxEbSmvFzx4#KoK#(3$-9we+r1>&2$822Ny%h3c{LnNst3x@McHg{NQbl zz@@=^C`+#+Femr~J?(P@E(;zY9qx5U;PPOQm?HSs+)xg5xX_iv)4;#xg=$GbyTuW> zB18Z}--N;!dfE~AOz1y}3qnQA4ZTOfe^YRbf)f;+rr=WqLt`k&qhK-x=TdMH1#>9) z3a7 zG5vYyA8c-PFIYgAl$TYbjesRL~<}+<7@TQ?p z7icATVgnbl0C*TRVsh93CpX~`2NKk+XL+=cfH-)K1tugmF;MM33h?(${G*lI|J#3) z*|`ZWr7Oj@+3k%TZ0~*{&nM7AX(FwWCeZR|0+vV0{V-iCy=Q8=R<=hjkGOK5*1)}& zYt7s{S8L_U-C8^M&ePmnxku~a-YaOfd{*;trH9moyR=Y4HG>$5b<8B(qFR_7@EC5}D5H~J)+YLNV zPpC){W+MkGU%Qa{1)zeFiubdd) z_lfaMBLQ|U7@zVA7@v0mF}{UheBNSWe8<4}yoJR{IfGKF|VO`JMI>S6lU(^w8|KP! z?Gvti6l7evE||@gYl0T8{2`dfmAA3`sJsIuRQY3YI#+%foXM461q-=yBv{0iUkB%K z- z!2rJ}_!7T2_zK@1e1q=@9_IH2-{Brl@Cf(x1>fhMoxu;d=d(eLd+rY&<(^%^kGQ8l zc!GOA7d**5YH*l)b_YMH^KYT)-1A^) zCigrPD&(Fohl;r8;m{oJc_cKCdmatV=bo>G7IDvGAz&y%56?)gTjoqN6+a&ynO zLOtB`ROnvr`Hzr?d%hj&jc`o!M_dFkZihF(#+Rr^Ngucf;KMXz3JwFQlhbI)5t1>E!Y&~)y3XJ{t({I8)x?)l|V z5%>IRXb$%r8JfpE@4|fS`Ss8u?s;!$3HQ7|w3K`PYskhuzkyN4^V^{+?)hM-ntOgX zWapm$9&&Kc?}yfLk2bV{dxAroxF~Hwi!-K?cYb4pQS0-yS zxl*7Na%GBE#FeQeLZ(B6C^MitE9Ys8xN<(+ca#fAgj@&_q7*`eC>Lo}T)7w`M46@8 zxl#o89c4C&5OUv9;J(9^IV3_ZBN1{riIBM@Lc|*A3WyNpN{A5UGbBRhLxd;`AVQRd z5Ftu2M2ND8M95-@5alWoAy<BX1Jy|?(VO?a~p-QK_kyeu%4vEC0eI&$0>uXJCt(p`Ie+qG2|ck`|)OW&*AmTRkW zI1uG?rF%1#PQ+8 z@iLL^ox*%Yj=(hE^%THtHq8gTPGbWz7(!Ot)Z23obLXf>)Wd3N!ANaB`Z8c=0T5Fy zSO0?egDQDSFafVG;GUx%q-N}@wNQn-YRBUzXICxSX3O1GnyVfV8G#b@m*Hn>hK7BJ zET5HoZ}lyqW?=A3*ueQ20T^@Z)Q@h88-iVRBkB>1frQxKnXMjB^GDXKQ(u2NW5hnc zztr+P?E&=aK3B>5Q+LZB~c-@5%4qn5Sl`w!HpI%fZruj6tp`i1@i>Pb)t^UZ?=Q8hrS^liOW8r&?4KXB z741G$T4q5(YGp(tPV{#!>A$VuP=$bcu(XG%2Rz$HSUf!)06ne0^#CLLG22_o2xYhU zE(BR=Fnx~r)^RVRxiLe-e)Zpc_jBd%+Ls-H8@2Cpz*rVIk1KQzhA{NKFP#Lf z{kp&W6Se#kS+ysC-k-8f%(nKu2{iP*XWjWKev1SB<-^FN+GLZ|BRB4R@7{|`i%-c& z^_Q}^eUVx|EFO`+;>NDuMUF*(`N`ryUGv+3*Dx}_p}$ZqKPijZY--&n>Y9AjGIEo} zh<(8VB9ZBJ!*BlmuB_g-SXSL|){6jL9JqP?`iB1e=3fJtQQ*Kays+aZs7dy`h=!twp-NTtq1mJXTrZ@L>TpNqC$IbV!laWd%kP9@}SSom4|${aK)uLxi`ayy~j-L zE_h;K%)ME@y$H0g)9XI&&DIXm)6WUl?{M#CA7q}(_ZQgT*}w%Xa6SeeQ@K=PZD(M| z7`1fHEeCpE86nm+mifk^J~l9o1ulrmX?-FPl_ie_E`*?X)>g1617kRg1qvA}u@}0h z_uC6k*`{Q;tr(o*S6}!P{SsZ370lqumj%z-#M3g6iLaA<1h*lllT(6Z`MUYf^69z_O1W$LOe?OCmF5 zmF8KW<(AVQ{-6PgkDmUUdNe*cC!E}$U8L5|S6>#;)x(}07G`}opx!uNMD&*xS=8Ej zYIc#Co+H!e8R^-o19_xK!eh3~IPBSx!@Bd-UztS_V`1I7h|%jBF1aVqJrAAH+jq(~ zGox$5snT0ByT(bTx=ByLg#S_iy@6o6RmwF`{~@o;75P< z-NBVRd|*ZGzAqBZeVv}bid?=IDB%ry`Y(F=BTD!!2!vDcCE)>GT5`f&voqkHtk&ub zs5Vu~6{s!Ocu`NRD(Rf#wj*OCN^KEXd;=Pj@v3t( zYi5_#h^n0<`zNb4&@LK&CG(A^M#2jGpl?0}%jtDB1=u)i@nPe?#djB1w)i}FVgu*0 zz#LRoHNWId%#1ht?eoBQ1b8|3pqPOT6tci&jO~02oq9{Hnpg5BCJ*w?kIp+)lHSjTzZk8l`9rMj4G+qxL zkL6^7ON?2XmLq15RkwRSPgU{5s6Y+w@$T!E76k!x$K z-8b#3txnh_orWglca1$&IxPcwm?WLv=U*a4p=)|eh8`oTs;xpz|Mm(nMeG|&U;*_9 zULcsi6jX?+!CB#o0|rR^?=O3n7Ww_BY*TI4hhE$TR^ymuCridp)}+JKM?FM z0_}S-jW6KdeBVL}Dk#7Vp6!DJ&{*F#dhO=kF}@u{n0q1U*}wxVP%MxDBEtFu^JS%4 zZc%L~B&YDu&E{&IC2RS~Ea(=NlJ?;p=vl%Ha}OY6$@1ZQvx@`A7wykD4zwX7T94R| zcFe>tLFzbJa&WsWvfM!52 z+lT%B{_+zC%a23*IbmpJI|J6wUk0M2sC5=YZI%PlU;YuI-?Npycf}@T`OT}MbN%HS zV#Q$bJ(?_7b5nxjTG}1-sk|WzMjEW#4GBeLTl5uNf;VQIr z_kF0t)?fZX@q3iB-J(`3QVSl@=|mkqRk_6DzNhX~Y)jT^D<+=7l_o*kDn=Ky10c~t&= zdIOrhcNfhfOp$B3^02l7e#g*?y<2D@+v3CCdxOtSPoLw;Bfbae=~1ry#pkD|Z_?9q zlyHau9K#&*V+mU9zmO}B`mqFU@mF)D-46>xi~q~W=O08q|I3swMEU-Ke1k;(gRu3q zvw>?_V6kA9LJyVX+|>L0m&T%2r~(lw+Id_K*5h& z%pp_h1gLArB?7e+u_F&JL6`^>SoqZhDiY3XqDj+q9td6eZS}KnP=mhw3PK@$(4KMB zp6LkKz-ks)BHDA)uDl}Z-odh>3`3rvVc!%0FLdlFD;i@r!0J7HiU2?SBtjAr8M@2` ze0bNi_%0XlVPv^R!f)!A73e?!d%5WJ1=i=Xi()~SiJsdKhM{7O&_a4WT75kWTuV@I zYLZ%tIV5k1=ALIX^n~>}R0wdW&PxR3dKQ42ps~_jtNvLqPJ!q0(-CN1C@Q$b z`W(d09>~5u<)>p&vWTjBcs@c*n$r>D1_&3@{TfkY3k%>_ps4W#ky)2z&-Rmke^YS|GSm8|+>y=n{QD5{3VuSKY-Y+wruz%g0q z6erMA(kaA*DJ|-!C5|~rH;X~KoP(ya?Fb5FU?Nwo%`Sdb{pA*EAd|6Nj8rwtg{momM5>xH#`-gT-AHlV8B;gKD) zh;k2=3ripn{4y5ba($%|hUJ1B1GQteY=rxKWD3_A&V$JQ_A0<`Q!C}_UFxA|~F zzQ>MG#-ocf)kCCGw!#i^k(r?&VFve1fu5EOtN|*s_v{jD&%?9;pi69^*t`Si>gys? z?@LAcFfG?vH00~YzKvY@itjr#LNCw=y-iQQp(pH?ob;XI-bsG!jC}0BjDqX9_ZEQ&^w^&-QgY-?}$TeNplck=d6EUs7SguS659&z%n@%Ube9 z6Pk7gwr4l>zMj!CDe67D<(#PZO!WYoMt@|Y&UOSU$&5eQcOC^eRy3InT$2^3GN|(? ztVBnF&^N3h6voi1451;^}s3&pJqR66iVv6pNMf0tFc~D>Xl(!S}%(fdd_!Uz3?Q=k>m?1XO zq~ArO^wbCq+kLLu)lS`*ODhVL&~;Mv$nmMxpWbPi+Xa<0D*jYCOnmoHq-{<~d0|&O zF_hcs{cX|bQ?@0A?ptIbgd(n_eddm;*r>woi^HW;-GWS#B1f zju)u6!c1l?e)VZ>Aha2{077kU)5`V=NM9hK3N$%Ut;@@T-Au=WQEH(q1yi$7x+Q1B zjKkVj)JbxOY<=iheAC?QqDPBgocp5O zKvB<8S6WJbcHay^?P__$TmTd4C9hcb{6`pGn1HDF>xvH9il8Hp9I_SCkHNrAj0+y5 zH@apM^;fhS#5XUJg62n4(D569s7&`Y=x2TuErX8o4LU+%p2R!&kp+kZKXT}3QxEjM zG+lBMR*1&azJ>??Z1@MJ!+;9RsaKhAI`>{A6gD6H$s6fFO_c^CT$OJV@q!kLgF&lG z+d*+p(gZw63BMra9!mRTez1hc{g5RO`LCt`wws6i>nYeq!Mzl~{J7T-^W#JQN9gIh zfa3o-R@?sL0Odc$mA?*x-?W6TPx3yC&1Uh-AxW!bRjJ2PJjQkg<|ZJ>LW3L5(HLKY@|& zJ(YHnXcZ6ZPaSYbxPHjDlY%d!SK{Q)N?l2OgZlIr+8KFI9jhKz{=N^Rcc0tZ_c|tr z^7pUPo6Xwy6DWVZe=of6d~4sI;Z@%I;TUV*Ye?(;Fw5Gv9Dd8kK1QH?IDj~7-^Dmr zw9?v_jaD6dPsIPbh`$ZAf%xrEgz)n#`LhC%$G%Uw{+@xy`&OWCkgW1|I=_401=hab z!!2F;@JRvTD~LnUDO5%gkS8O?+V>6AsC@Vbk>>_9@K}RL%tStGUj`bFLLX3%J{-B%{Q}#+67rW!sR^HCtqgGIZ)DJAEVR z*dNeVk^9z6>&_44ix{Jrd@su+Pfr0F^SpdP^{U-1s*uLTV^0_flAWrFj=n~7a#SlM*LUW#g$j66JmY7bQi7J0B@K09^?pk0@RNcXI?4LP2VhD9L+ z?SKW;<6a$ZV>?8L4e`A_i`b5<@gVx3zM|)Vgsn!7X-1BTcu-+h*YiD-aqPFC$~sX9 z)*@@)F3_5Ky)r_R!EI4SsI6Vy$PX&Zc5kENHcM~MoRJ;%1`xY}KPezj3dI+dVw>|M zOl%PX(KcO-QzQ>j5%=B9r!y!o8ruxzqaqK@szS5S2e$Vv=3fTWxF7b(O@1fzGXI_Q zgtLSX`d=ZY`W^*vrTMD=Z^RSF5Ko*4MQ{*@gDMAc81bS(7rnw}UO6~Kukft-cwY{l z_TkWCdHG<{-@02IS4b3XCq{u?SEoj6ZN6c?O>z_V`cIuGZxgz=N)?=4gy~T zB&O=yx%vpfYMtM;+CX)G`Oz0heyQb0#gP5L4o`s>)*(O)i1Oj-;7Tu`b>SSF668>> zCoa~tC;@#5upmt|PdrlR>)zQ=ovZ4`0wM|OZ1`j*Fz(^E>aGvp|O2!gF?KGd7 zdaE`R8d(HLxqK2DNk!iAR?qf7GIx{SJHtRstVU!OxEO;~@_jIy(`2)_ucb9J^ib-7 z`YS!xl1%r-h@#qvFhy*9ZAJ<~g<*J6#>M4-wD$E_h%Qp`N^HIvyz)k5jvebgX?W$d zv=v~Y8x?#Ai6=UjEn2zr*LaVd0cTqIS5RJO(E_Z#1fPf1U&=nqCK01Pwl<~-fMb1b z3fueNj5g(-(4eC@X-gR!oZvEYKki}D5g(^JBEE*e_XB$U3B4Ym*ATdj*j)Dg4QiSn z{#O_I7oe>F23o4pCZB(&BXFhv5wIWs+w}T7@F4%Ecp8NDuX2#AJA>HNJ~?93ht=_sarqq0Q{1B%B+1i;$fuglaa3BJ%A+gU^AqTE}~Ow%LD*j+vu4t z-#k6l;K=tA1BlK*A_v=zViE~JH<#F_cV5%NUiv(fVJIIOqdaAD?PO6Hi78;&&ll zcOHvY`gCpuvG#S~(%o^zCg9w<^Cg|L_E>uFD;n8RNa%~Bgt|A#-lVHWG;zYT)Q9q< z*4Yu{*saf1NJEm&NwX!}p(%L$)&yV;{X!NJbxV0dzC1#Frk>$p@RAN-9U#Nm^$<{} zmT$<1gQ3Cvi$E1173{NdxDrZSl7-B`t01F8HtA|7GwvuNHhnP)!n8@OI?wlg&i0-X9ku5$bVSgo4X^U?S8QtLM zH`V9l%6EJ?jX2HsAootEO|6G~uTwzklkcwx{Fz+o@K2&AaH9eL<@B_ag5~rIP!IVV zDCnW!a}+#42?O->3DaLtZtAa~;kG_!%WJ~g1g|;Rfy4OQ zv%iKZVJLgY-a!7|9obpdoiEaGL0$={Mirn1jF%quhVU{xEF2PPJsvfd{%$P8t7TP^h&a>{Mm4;|GY-73y8gU$xR+@S@ZTKfL z2#r_Y4cc6&bA1m`>SLJ1zF*T5{7D=A=g_k7BG|@)3o+UK<;dqJe&t_>eEt?v zu)67Wkn}Auik9FzT-ip;$2R|8@rtu&%C^BgyoPX^VjJzNw+&)}*fO|`Qg5QTn}{1xgA!GCaN2vAlIjv!&E1W)3w$r~fLA3$N2t_k|Yl3NA(ZlTzD zAeO}juF4ElN7}ux9eaW{Ddny#xyc@~iLur9F!t2E4{FoL?R`+ITK)lfp_JyqJ%i}5 zloVmuE0(IAmi~$|m6sWkZ8+NT_#<*Wo&}PXj>k`rQ_DY+aBs|0*B0Q7WNo_{YpZ$z z11|kxC=oKP2wSscvk@=z)mMt&Dh>z`dbD`HnMlc@H$T$eKShrN#bWZfF(cFy$KmCO zE~*Dl!`og5^}d})*C#i*vMLMgx@EHXt@Ux$(isBFdg?+|g1WEQ(+t6q3Ix+ z5K+CJcTbI`8~$hOSyN}jm|RwV^i=5-8+M?`FMm$P@p1A&uuambG`y>II^Ij=)HWTt z1ryc>#c<@7Tir0E?q|MBK&a$R`mt{lBww^+cD1jcd&l{3n@N`MDekqv`PF-|?+CpH zsn{RMr2RKMK~Q6nGM_77_m@ymihTan$mg%8)E4CPchS=>O8qirCYkDoO#Lf~?63QO zOmQ^B{BI&|5GL~dgX6jK-4M*&)r0UmyKS%tZs2TS0Sna7<`r$hH}&4Q!9wnA!mD4` zy=pBwtv2;;*>Kv}Tc66k*OTY$4dR-ERgqInj5^E)^YtMj-&OkDNAuM%h;Ygk6VccL)V&d}Aue$A1FVFz!R-usuIj7c9KlYT-s%47Exo8X1E!_-QIRY~48o zApnO;D9IM-uBG6$JwSSGTH6AUsBy@7Hz(eJt?o_of#JJ0dllgsYR z^W2le?i}m62VUphy3#?J+AWl+`!XyYAe7;(C`qo${{U!Y4R}i%B zOGWmR@Z*ckeljXM)qvHBs>jl=M;0X2a!7r*ol7D*#kB z<-dpRy>~YhWj}VRbSkzn8!!;P_c)lf?`Ax``^R^&C;97RkNx(8uiib@+E)=1*E#-B zwoqb^kHZ5J8RdWXcLHGXAB-M}4T^hb)8+)2jeaD@wq(|r=TStRLSf0_u8eZ*O8jQ$ zAFQAMEdcem%C;T37XUXDSssf5X5IZmJ$P9yMx-#_HXp6WvoBHkuHu@65~sJ;GM=*h*sjeas-v4KM@P#b-Ov>Ek*WwX2S zZ$JLgAF0A{@BNqmuw#Zl>Jk0TlRpS)0^)BD{!OT$4^b_{UBYy~ubx0T$5+@pT)F6k7I5fiP$OG*X6H{G+9Yc06z;XL zfv>PYozB9CYkBdj{WYKT!}M~5G{s#&iPRMLfZ25ot9+gyI!e1eoK_)YRfux^)SIc! zbRZWZdjs=etQTcBtibpXEaKVwFc?@G$+4P5k>M$w%hFxq6mTO`)g9>x$ zxT7-YJ0B#>25<}oH}i?DY4Eh@gQ#-PR{d06TWCzKwKL5p!_QD%n{UP~QA?MQUX1N^ z;~xJGW}dku9HQ+wIqR{0bb(6R@wP<%;!=c(0aM=v$zJ2_Uz~R zpax#ydkVMR`JN$x^b>K0*2V%LKsbAn8&)}d*=!^8GCIb7`igtp@eB87zz0Bh2@FSR zOxf79KuR$M;s_uXV?g=oEm^dxfD+E`wcV@FQ<=Bs8c8;#X@?fqw1;= zJ*zB9o4BM_d{59J0TXm40>tlpT*iBPPBZsX)P~a*aGBpP($`~^W0p{B(SWDdlez6d za4wiNBU^O5;ta})I?x%}Lfx8Z@OsfrnF3NNl*$?#Kr`81oFh6HNYZ9G0_(NQ5cqIT zy~S70l_sC!2(0sco?f5g%0AzZ>Gd^w{Ub{FPJ%59ZS7`#s^h@x+FdmxyXsDpsR=Gk zb$R+S4(^Qq7`ZPWlNQ4z@BladH%?S4N)cbu%7aKTs-}9ekf~rP&YPz7{{u{DU`r zo&|0Ze5LoLBZj-F;pe?)KWR&Q?k%$T@h5ypb+Q+&mP?tIOeA0dA!oWwXwkQFL;?lE zlS-tGxQnvsUpS_BIODv4t@oulk~P3%_8e;HJPNS7JV~dfr4b zf!V;fSYShZ51@zi4rkt%+dKT}y*MQ{H5-0Nf#W@TGoyRf3y*8+1$Ip$dm-BUf1!JH z4#0==8!f(7)IA4vZ!^8ZEBK@j%F6j9JTCCTfQ;_J|MOPpg!V%Cf8zNzdY&VnH_~$z zo;PH`mLu0b@5|XlYh3EJzKs>pEn-2m2-3T%4(~kGRj_u?b7H8se?%j_@z{fy6MO1D zf}Fw3pMRHbpn{{2v8U1 z7pV7BeRKlqe$vZfLOMPx8d$EG1=7RF6gfWN7JJIyH*7F^d&)lmr+hzLGlmas)mN@| zoi2ZP7uY79S96b5UMKae7PgRxY+~T`$%mYD68(G{pNna2w1A#4NEi6-CL0DU5*Of9 z4W2@jaGZ_-WApVS)<)i`e#o~A{1D(L{j)Kwek`s|!fDTYAseXA3e@XE_7R4Rl%0>D z@6geI9Hp;ot^!s}UPb%w7K)t}8LPI#-)>l)vW^gFpR%X!6&)%+K?eBaaCA5knVSH?h){#`F{1Y|z_52^u&)0TDdofcXnRtaI7Dbx zM-6L-nLes-R8+2rN1Fhx4y$Y9?4qvX!A4heQb!k2yAe%Q3SSbZmus{QM;G@JIp1m&BxE!{g+x3he$w)h=Vl zCydQW7@KQ~!^=UnsHzFO2&bt^X-<(swD+|UVj|=dpv%JlWtLlwa+s`ge4=&rqjJt3 z0GjA;@0bj-Zq#e@DJVzaTg$x_aQj36SJx+fK6?5Nsh`juD}1k#4hkD(h41&Ie&UO3 z75*$Jo&Iy^=>n*l{$hH9&;NKFn8Oq2-ZB10dcx}Hc0V}6Hvebo^^4rw>VJ%Tb6}{% z>-V`g*Z&ed{epY*{2D#|2|BI+Q#=ihhdMhrot~fyUoi;q6@yjKbK!r1C-9mI_+KFK zQ}9Cy-lPEDWfdXpP}c984A!%+2=aUgI|%jrum*NwH`3d-Z-tQY%UNKvT&Yc(aIJZ- z(UsROAa+52K&kkfoczWh{rcR^+J(5?S%XNfVgq$7uto4NSo=J8;{un9@i5YHQa!wW z?we|jMO`th=6z8pmnTZ{9#}mjQ3`z|Gu2kYZJpc_yDk#7oh4psTRtQ*g zZ-xUFe9njGW`6|_8Ox7K6@R216Z>ZPn~OxXz$=<_Vs3!~I2m@zB{Y2~RnYYNSfEi> z$ko@>BRgN+fjuZo6pRxDqsyXp56><5tok!Se(Ed7FBx4vh_Qi53Jgs9OTO%vw$dpVc)M7@%O6jLuThQ?R{&BHx~C*W0Zw zeW3r=;nG!I`+K*;pMiY6q3RmRCwiAYkaOQyw30!RBdKP1Zj~%~Le1XwLpU7bUXXu? z8c$r7{W-9X5lmOPw#HHy20UTs#b|sfE3j4X_+b)_@Acn$0yX#7ob0c|W|P=Bx*H@b z#UtH=^M3F44`8>Ofk^T6MEOYXt0OOfebCWBxo6a0{*GFc2bLHS1N?s=MnW9A&YN3) z(gxLG7%~&M4PW)t`%*T}0_v$J5~bp-aTnp51J_YBS^xw>Sm6+VHYP$;5_IOM2zw#5 zOztG=gQn%z!Nt@1ow|37=AwZwtG6COt)HiBj-qjcRkqQ=@<=F4qOxJN@3^*n#C~S*O>JS3vm|^N{c;(470AAlh;)eRhL0cYQY!h+6yx3-cOb)h6^p& zCo02kz^A4FRWM8TVB zF0##T_CYwgd;yxp?@{m(1yF?B{g>j&kF%*;{3{)S20vChP2_cRhyO)Nz<%}?I-1fr zXu<0s^z1EzWXK&XrMOypQYr9L0PmcK22Uf{H;Dquw-2t3d+A#QTZaI7^S-Uz+dkAz zPhEK0caIQ&5LC^yL?l-$slUnl0PbC+R=~<(-f%$19BhYU?o)`_G!c7XvVcG+9nq5D#tH8It4O{4&^R(Ls8MmagiP1c)r!LYaj}UrH8qo%}0S)Z+#5gVsg zcS#=EXF-KfrBgGq^njcX~A0QgZo;gZ-9dDQE-?7=)7aGrGlrA zxp#sOO6eVb`1E%8vG-l+zksyb%jjtV_fEtj7EjQAt^SSl1jV<~50$#o|0o6jK|z3m zww(Dk!Rh(4pG~vDW}&6zm%EYJ!me+5P3x*Rh;hfz*k*@izlJe%o2zJVM@(q*UfEvX$#$K`*uPIpl7sD<#yo6tSz~K^v$N1%-?^o} zwU8GVUpb%cvM|p-;C^LeY*!&3bCQ6#(zO|=*fkSbEKynHx7+mODRIfNSOLY$kFsA` zgDoUI`}Bxtq}I8et(}pO^b+*3^r(2grS(dDc-!6KGV`A!YDRp8t7Dsyd9o4dXl~pV z52CW89{AW|7PvPH0Qd2MYL{*5at zT3)-prgm&uIsIDj>R4|t7w>qW_f^YkTVUv$SE9qkJjV03K86ty|@Czdgl?Z1Rwm=xB=1y>bcvjF{AIfm^o zN|$jV%cvX$ykfxX11{25yhzVj!>W?DFd5mE*0*j6Q{tM&Ep9?YH_PZ|`)_7-XAG}s z^A>l9#O0!BT!OMqbUN7nR6~=jvBX}&jnx~vf$g`klCx-NgKQ}Jx^fhQb`8tSPn&Jg z{P`?%6Wf0`>pbHI;oEF?%I|b8>*#RZDRDTTaA@#*n`61ty{*38wQzxyFt$3Un`8_T zD!w5NGI#~_;$SsrRe6=G<8BkXqWZ;EWu+T80_+o+XAhP)c7z8l8tiWYww&3{xV1H| zuFl5l#`^9?od#C3{ae^=XAEamseoj6bvj)5hJ9n3ApVY?1=r~K6~ff!5Z`fT8MhE; zUv|c|*zt949RqrE>)HOz?A9{}gzvMtSG3XH=~8VHsf58`;fgQBhSGHysNXC#XfHvy5W4-^E(axQ5aiIRw#q!e;0RY0)>C z?Z1t!KjRwL*4)^5SDEY1wzL(1rZ%v(XIDX8`)I(Q&AeD!Q@y;)?RK@L>ciR^G1plJ z`mmYZau$8iD@ooDvkC=HvS(R|Xk?8`a<+~Hb0u8KWN4c8Y{OafL{zf2xv{Y|?T}o+ z_MhE+Tvcie%I4P6*4vX+WOipPRkZOeIuoua1qGYcC>KEHG@J$VU0Y*TG#Uz63ko>f zYR-TLK1MZXH|1BA3b~YQTuhdX3T|QiEXJkQdAAVHiHO*Q4)QuX=&7cN3H|c3ogmN% z^e=m-#SiHA?BobP13BVk4QD(8(F@85y?n8*+~^hoQXaRpyV{`r;A8b?C$pmKiWIbI z@n>QCJJ_~=v3E2%>4VO2{*@IjNg$WaEGz?+wz9^5xzdy~Xl;#1hEh|AIrJHJdD7}C zV25dS-d*o@x`YVEiexd{zktn4TEdBikTbT-Rc!aAtVk@I7hV&!>W%%(ld;%dC|1e4 z^Rljpt|>OZO?-eI#y2scU2Ijl>5V$Ej^uV?Xm zor-6d-PKmV&AA1Oruw^zQq?ot>0*wwHMMkgy2ZeiHnz4J>tQH*G0k^~JS*CoTpdof zWRy_b+MI1$OS?KcU1EvsbjA{XJmMR=ot?V+T;8oOP+X~cx$vs!mP$Uhrm?=$)yA$% z*U;8h*A{%pdqtbOu>(X|R`0H-FWA$h>T58{Gx1qGm-Q~QitsUi@&i3viS9^Et z8a=MBWQyo&MR}8Q4Dh1(>Nl2MHs(9IU5X>DnJTWz!JPV<}SvJ~5WQC4}n zlFjvPTSYfhjRbS21=G0Yc6)sXhPAXAkK!BZ)QVJ4i|0p=ufF~_%d>^)YHe+-?=VqJ z)wxDj#?iQ~y|jKyv*>tttdSvpL|m<1+uBximTzl!-<7Uc&h(=NA=Tw>T;r%--PF|C zDEPc9)c;NI)-q>PQzKBlCGEJm7qHrNICKFlcXm2Aw_^DX<7^ZSfH^c) zRj6>bHsS!t?nzlaX#s+TYy-?Pz^nnAC;TKaR&>Db>g;fL)wfD>xdXCXu+{}j^akjL z7-9&qi}tuNFKg6mu5vasG`1PzQ_)(#HSHKxj<#3vu$Q&Ak~9;T>t?Re19C&#ggAG# zRXf%QBqhOy)44ECbz9cz+}gH{RJXIK5R>WbEBw-Edsq!3Sg{TJNXyz9B;OGeGTm5o zEHVZ^TEs7m-V%Tx9#udDGMn8s+BU6f?1|yIu_{vpUnSUS*)H2%n05VVtCy;OnX^M` zNP=b{xacY8WwduUd_Of0)bF=ZvH zsa!hZNi}B^qmrc+r8@1TtD4PEi+=ExEsd?^4NiUYGOe=XLJ1>#s;)Vq8@G?9`r7tZ zryKllO=Bm_UZe*IzMOa`DP4_h_k~&Mu_qUHlF`MkPNgzM&0x2uZ$7OZu>G|~7->^A zF0BxZ*?*2#&0J!k(f@Z;}iW*3;=Q(2=&)E7PM0i^}@;&QY{`6r?9%jiTl0 zbvl=n^yc6>uQJUdB;bn|>YT$c^~u+b`s>Dx3&KKv!F)*hj?rKOszn*u^mT&ySL;l- zUMIRd@bp zG9iR#qC8bZ)SxJcgaD#qO~`|!CJ@Qw0BVad8DKPI;$#wo&wC40>A}`)^=o?&F-_ zdHv4s{9fmGs48m)WQX=vF0tl{PY%zi%_K)sbJlDsOelC1Xn=yKsW;hkYNGS=Be^gv0wqi&;ak5LSV6n zlxSJWoJx`u?6skR5QoS5L#@r0S+;e zFPTzb5dR^FfDnO2sXV36y*|o|-EaGCz5~0EDbyg;^ECNacZe38O zPnA&AP?r{1QNaSsB|HFosCsp^Dnp#JziUmun0Tb)%*i~f43 zKeuDCrJ1ct_Zx8Z$QEl#o~QB|nqV`PDQenauqxg9oCM$#{A@?)7E->k&z(ShukH7T zw$*oIBTSEteT8YeLnp88ez3s$2~REC>u~X9)}(NjS@BxBCY~At4K_(-Otz6D&3WvU zOTGU@Izsxz{bjpBt&wgF!m?9{eI9mRw)-k?K-t8mp3aa!zmD!U*v>nZYsjF>%B~hW zXorJsuvF1@^2mr3Q2BsZe^t=Jm7!&}qFGpnsxs|tNu{FLhY^OcI;lQd2JMkrQwl$0 zshpQ+j)Sg-E`mz$y2v(y%uz5?^hL}`X<{S1?RG+%>65l$33j0Ec zgj&GhoN85bay@PhC~Wk|4OneVQ1fXtdVp0xCt%gc*q)Mu8VH-#O7!aH5Ilvf3g)B; zxfv-eOsl4DKOLlT{as^BtQ8@J*6`apOvH&%~smn7yWoNDRZw0b6go0bs zoZs@z%>wjYYyHqVgw{0I+Y1Z^G))Q$iQ6{cs+Rh?ruLQ~Q=9WaIHZ4D-nyyRzBX)- zJN30sYqYkEQ*;1_YT4;;aN-be3Nb<3v!KIGKt#sX~O7_RnB^7f) zvAWfR5)SouCb#NQBl2NiUN2*nuWnt?7uf0#CDCS}0CFQX#aZ-uak>Bq9jdi!k$6(< z98b5TWSinRT<5!xG{u`WV?$UrO^3`4|25ml(jO?;ssg@FIotqlLoOJun;HS-DM<{< zbC}3HaM8-_Y>i^5Bp%dm>F>EkD!wEcvCR(|QWXHIZ1@c@t)y7(vy8M%Wj=>W zc4aVx6}k=-r>752ww8bHZbeg&QMjrI>wuCPrmM{?jhM(!*D42;AXcYqy^9N|k#J}b z-AGj$P#kC=N>kDS0Bq?PTuag?EFEA; zedL0 ze77GOLm>had=MqoE+?5Or&6%LPwBWOb4$f>iWgO`U9#4uWtcBqv=SH>Y8g;y*X3@SHD+3h_U z&B(=yT9e-0W=Bd_J^UW-bP(&F?K4o$m(D?uql8| zuF0Lrmf21$PcoR2wIAN`;&Wa(VmzkP8QzSjQ(1&@O(+06ZAUk4PZz?1m`YYurHd3j z=wujRn+r*r$*#Jn8(laT5@WKW)nqkDN<752P)qVLYT0O?&W#1xBZV}I238tSB^Aoq zsC*>IWkV76(%HU_&e*OD)CI(h{74}+Bld&@Iq48FnBA>O>2UBQcCx8FTLg4RdZT@j zm^pHPzKa8kOHYKWl9> zA`3Cs`a8OW+$quIWELxwqT))al;o;#GKDBzV%GZMhlbHiO1{pvf0uDpYBNrTytgZ+ zfYi6qE=uYTJJ^Hl{7n8UX*Xsne&EEl>8eFS=+f8Hs)Dltw@63$fdcA5rwBr+VyhpN zgP<}o$*JnBQ~5|%xRMkCvz3Nb=z)c+KrL-XS9Lvn1v27xWz8YLv$RN5O(;9%nyiAX z-a62BOQ2UdZIph%Ld!0KR$26jD&}TARG~#%IM|yBYd|Tx!5iSurX>HYVurQ#!)2^5 zOlskJL@tnVIkOaCr{+K5(t82r{O|=gW3h3T9~Fwg0-^E}Xz%G0@nQ;`A($XVN{yK$ zQgzspx4Fl5m{1{x%tJc`R+8n`Vk!&EB~@m$7E^}|D3z18(E@(7ss3#PN?$LTQLe|r zDABRm5se2`0!eO*GnhY>*(Qh8i`Hw`%%m#>?_;tEt1~xB5eUfj-)>!7f!`?@B+U zH#yhjgjQ|JEp%5gQ+0POX)k1%D#Rf1S46LsUfME~O1}@WIV+FKl!Cou2&^J$zF)Ax z7I9Q<`XyqToE+}lVL-Wy3!Ac1heNgJGHD&NYb|?+?f#xurK4-;r;R;w*DvMDJ9$-8 zf`BFWJmv+ZS|_0xw*uXf#9kvE$^$`H-L0yR01uV}$>}*87huBJqnI0{pa=I)l~8q0 zU=S`D6v-$V$C?=HoZEduE}pY0_PPmyMWju?5KL225Yk?e0+hlXQS;m!dR0zpHXP|j zDm%(WY*d=3QanthpP{Iji%^&s$G)!^TcKMZ{f4Y?PMu3`F2aSOTxPdfIu>V=0H3Xh z6a`CTQPPEFdBt2PSJKd@H8AM!mitk#2L8NAh2%InBrI%6j-88Irrtxn)(^*qg8i|B zN-q`(uMi9kW`^?5TGxeOTL>tn?xYQFVvyj~y8gUKrBYb61w%?9HBgm~8j>s_juB;c zq9v(avM9NYzlFk7v9faEL5%#s?I2zq-9o1z(Wo%#!0{^+%Vse1kcg}SMv%_&bo_^Y zp^Va5!OqQ?x0ag|Mh<>a)LuFaN9LI)^PHDeij{}FQPOifzfSqsG%1R>w`d23d35u#$r6-baZD}s&Y6X?y8R?k> zV8RN#nw|ctZEFW&V^Iiy>;0D$(WQ$*ka)^Dwsy;b&J5{L07yO>A_$_0lMR+|qV zG4MW%v&hH7=L6D3p} zg{l;eL&>tibmfbGIZstFC-fz}aa@}K3<+)(K@^*yfC`0WtPyIu+gU*9K(@QYs}}^+ zj_jBZU?>n?O#gCX5IUhMz4lgRHwi~_w|1RSG|vU@O@ZzVhm~zh4$1(9ighnlq?UTA z)I1P(Re5ZerF-%u4R+qL*5BKWz$JCKlruYD&-S{L#BgQh<>_@2{ffa|=w1P=JF-w| z*Oe0_PwzSDZSo`bYq|9Vr6#c`2vL#L6P9$HXPP5Nou3ZBqI?j>s6wIMWsU1r*C99q zj9G3){rPO*CFCFMR*YchB{qm*vJBri@5e0%8Q6s9yBtVZ!PVS>xQRBSoQe z10fj>3yAG8Asb+;i`{5(DCdr{eCQzy^EI^+mi1dxEDu5!sD);`Kj6(jQ4t+jL{{(0 zrj8ZveUqYgg~N?o4+^vdI<~epNp#FJKWukk;rJ+2jNr)g(C4ILg+LFm3*4&#+Ds>1 z$#i+99A-q)wgR{E0K3o~sBM&#Q0KZQlp`yXW%33Va*^O5twGkr9Uv8OTCb+0lqiU} zH#C);44GY(lEF%9jlw_{8rn%EyWYMv+{ZGJDkWV*cM$$xGIXRkCm?6P zqg*>7@f&RcY#8e9=(C8%c`kjN>iFh{UDhO|d5c<5U~&tnTb(frQiX*eotc-j&PYi6 zU&xJ#M%Hn1O4}VHoSZTzvfVv^kkXAo2EB+|Nvk{LI_st#&6Ivpim(Y63cW^SlUX9odiqgCC$jT@Jy1jAkoY|TRisP}~})*8`FvTgSP zx$OoZYo%f~+kH(ICt;@tGD^itiy!KqP60i@&dg|VQsz&pf^L^W|2b@yR1>(Y(6)}? z4w-y|2ZpEg&J?neT^|?TE)P~&$0f+I&a*;E!NFNCDg6M;r06LPMbD(l8hI9;q$$*9 z3^TyeZgROkuL;SxvXW)R5|z~(l2xz9{#vo^!WV4zZ%RMsa)3=q??U19@94O;Dg>xky%xd1xJ;NIfvu6-O3>WQo~C#}+%SxmTji+a5Wxt{JLH^C-7u$$Bmq^~|3>c`_;G>9ypDLUJY# zA5G2Kq$pD$cRKg%VrQWpZ29`9T7VK|y9D%;%(Z*_1%TDH`9Q#xB5xfEWMa=zfN{q9 zemT~&y|CZsEtsaJbEs@m2reL7a@>;Hq^{4sr&7?`U2c-3J0B3o(Lplw?n$lIwf9N~ z(qr>PHI&&B#2vwm{moA~->#^1dyI9tH9#vWgrwpy;(*+piG){+^?mrgDNBm$Aar%G^gcKTYuo{52J^M^<)*yj-JFGzyD z(=$62gVNH{&8LLe2mo!XYuGQArHPv2@**z*dMuqFhDoTBY<&eGk0J zQcSd8mV>gNyarMV&d#wGZfNjd2su>}y-;aK8^r0^;SM1SL3^D}aI#^w!^BuPw6mnd zkl_F#521IE>uK4JPvhuGYDdz^N2ViGkkKV2?notMGF7X;NAS8TJeiXL-OA?}f&*-N zMhdHZr=rivif(3^psa)r|1gl%6xjf3O6oMQ1uBW@)sBoUaNa5Q>$2GMQLHf2+S(FF zd`+_4whZ>X%u?c=Z<~m-6IY{7Yi@$gO0GmOy?5nFY^4mrIaBIKVDVmRn`pDb!aHgM zyVxc>|6E8bR&(ul$>U?Gf9!;o6yn&pbEK9UJBg1WaYA6wMUxO-j2Kex#oAuR*gaxt zB;cvyn%upNm3hh#F24Cp6CyzfYqV>@)gqFqSeZod{abGZfm*t8ReMt%bg-3qGFm4f zkTDMX4;(+(+B{j>IyND^4Y?iC`fTrY%w#E0Zc`p0a)gp9ie4wSr!$f`KHdEiiX zeUi?$)rC6cmebN9Pzwu@?8GP@ zeA1R$OSLz^&dy9BoVXmd<^`FrUA(E2ZYuKNi)w5%?(GO$QBDW4E3(>u8;s@HZzzw_ z97y{7*i|K$pY$ZlGb>Xter7epWinJ`mXOoZG0RR>EihQB;>CJ8aC;}BXh*(aiN<7_D4?%kXji24g+fmm4>5z0SCi>)$oH zxPF=H`iN?Uaqe(!d(A`G0627j61ph4~E9|K4S;hziHgZ^=}!wxgIkf;QF_X zhkUUG#v@!mU_8q8E@O!6ek02DO~w;kUtv7O^(&1Lu3u$5%k|C1ey(pZUf_Dbh;jX9 z;{ey2jF-9oLE}}fHyekzev9!s*Sn23xW3hRo9ova?{Gb67+mi)4s-oh<6W+YjH6ul z8SimDY#ifyzj2)F11978b!IWwmzkwpzs;P;_3O=Yu7Aj!%Jo5WCf7Hbv$%evIfv^V zW(C(f&3RnkW-j3R<>n%;Utuoc`UlMAT))~}!S!mhn(H-Y9oJWyja;uao4H#DNb#4xFeVutH*VmgG*Eg6u zxPFuQpf6Tz{vFp}H@}5pG=J=iEi`}Xi(O*A>5E+&AA|0UpNTP#qkhk`p)a!-uz~2t z*uwD5COKtnhDE^0gSBs=gYOV{fnh|l@N=*Hk=V$Fir6T9K$YlEqtEE*I43JT#5@_L zPkNeCd*ZOY6^szK$e2$^dMVeJ8f)ox1ATR(?Z&-mI~&?r6vJ$=YfF&nj~cokpQ!-9 z7@vLk+(0k>pZt^48RUVn$qh*zE;PwRKk(Oz&(sF8c?e~b)MJvG(0cE~`^kdnNOsD~ z>1E$UyPH85-p6kX8y!6x8EV;kqchnQRzs+qHY(8d}FOO-6o2jbfQ?q2uB$!6PgWiEL2S> z6ueC)pD0j;-lx(~4;$MndWp_@ ziOzb7&U%T?-k*cczU|Q2UpjR5SE+Q?7n@9U_A#Qfj}x8!Jki-G(b?TZXCp*s_Yj@^ z8)MKHn@DtanCR?2qO*TTboL3Nv!5e6`?o}ArxKkVB075?(b-)@XFp~<;fqZ-p7O;e z7$d&e>Bh6Z*c9X2G}+$5JTv};E0bDeQe(_{^mUytHp~35FLtJRlP`9*xycusZQksQ zon!X+Vsp%pFIHg=_+l5BxBFsq&AWZE3(b%CV&|G4^~KIJKjw?gH~+>LJInmIFLs9c zurGGL`59kqp7}Xn>>~4VU+iKthQ<&@D>i?F*>4_0N5m)NHhzvTc3S)zu0I_|m%JVi za{cRU=qVP%q!$Fv4b757A}>j62Qpwx@0LG=8YzF6NA#!BXLNLOaE+_o9&&8tm#Ds|Zxb|yvH?J}u;7iPh_)_x`zRY}-UuF*R z{25-h6{M znQ!wCn(y#yOoKO@hk1+nE?;dPb0uZz#&*T*aP4Uk?w6kotU9ACsY#+UFL=<~{LV9*l>1Z+wv78o!f=;u`OZ@8IG1 zeY`)un-9bv;J3ve;)C%=__p|?{Py?|zat*ycgCOKcg3ILcgIKgJ@IF`rp5PjO^?68 zwU5MOT-zQ$z_lImm$|kx{wmi#8b8Ffd*iQj?Y{UMT-z0Yn`1%do(eRYkLz5xc0fkBCh>yVhPuV63e;vSYid&9#2$r?emE` zu0<1#TpLa_b8TOum1}>O@Nw;l#CooMA#nrOzL>a?YfmP+xb{?H3)lWW(ap6lC3?B` z|k8EBXglpeQJj=CMVn5fuop^z3-$}%{_G01y*A66J=Gu1? zuX62R;tD<)9j<*pVQ}r$#9^-eAn`8OewaAQwI3zkFVp%C*;xiCp`s zQO>oW8B@9Tb7Lmg-Y{ly?M-72*M4DCaP2Lu1KKaK4rsrEK1q8A`Xue2pik0%135=C zunuS@{*@MjqgV*E z-$BmNeh+<;_HU4LwD%zAX#Wm5M|&S~j`j!0IodJEIof|14{_~}SO~N~LC(>RL(b9u z6LO9=O8O*5`XmqOlf0x)Dkgo>7}6(|l0Iny>60dsK4}u^lO~fssf_eV<)lwKjr2*U zlRjyRVQ_sa>650BK507XlV*@UX(s8D&LDl#nWRrT3k!ihixz^j&52x}P5PvBNS`!^ z^hxKMv$%d9>66YUeNqMKljf2>=>j&?%wpi8Et!UPbf#!xtUH8lc#dC3Z{LWaoW$A;y+58p^_tVF*X&-sW@Yv6V6h3Jde^q`UG>K! z_`lw_^R4iNh!mi9#KP0)VQ=%!_rq644vLa@#QGQE;r2lvLqc4{><+`TX8W04OF0DX zBIgF{=fl@;p#G|0(dk^jJ361fYPr53x{kg!`eKu!eqU@{wA&XO&xSUz*hI$K_>R}Y zrP~i2m976pn;bc~4;D*?!S#vK>PvV$LmDv)_psheeLQT9bFiWzlJbTZdpGG;(W7{xr&qRZ&n3VK#g z&pwD}Y-kOOO{V%djgK_=s$Sdm02RHb!Rr5p=Hw&U{ZAi1?<6k>r#|{CX0Hx z{%gY61<_0BYdP0HLBsG*Y-k#brD*uk`;0zp$j@Ts3`#XJ2{56ohv+TtfktAQ(=R3@ zv!CNqq@_IctP~$gL-%$bdQN%hPpp7LKVY%bY`k1|U(0CZ7m@x_(uT@@Z%tlC4nR%*q-9*JyX9+<$j$I4291eZR_1gc>Wy_zrAZ1$r@$R zXNS8ziAB*1qLIJnkA@)U|8Qp7wXFQufrpBVipP`hc#k%X+dL zZ$uiIu*LYpo00Z+{_qQNyQdukCtuXRv8(=_)cZrb>JQ6AEk~a|AA|d;SJA0}-+D8< znWaIMvvN<%(d#6v+U5uvHwd~lRXiwru5A(CIxw74_tYOc5optGXq73@j-2k)a?+o# zgn7A3IPK(0NRC)qzeQRMjD@KFmO1QIbZchE(y*c{D_X6KMYu5Vd8btpp{m$*8#Z+9 zD&O)5A(1uGFaDP1z>%ZCe7Pk4jzz&MJQ-aKY&JO8pNn44^{*LFw4NRvfPOFv{oumr z1DF=k$1xeAum3o321E zA0BSN+hOQa_YYr7&#t4lx6qYNUylIP@Dr5e?-kz^lunFsbQq8Cf)MK!9n4`n>F4~^Ns?>4T#9N&S)?Ay)t-|YK1*9|sg z6vd{vPt6z5T{JbHrEaSg%thIbzr&3f4C-<*H9sj{C#U94+~=5@KgMJA)I>Rv)*EOb zF;VkQOMBG4^~SFHcOCS;#6i9SCSmGSetTEFk$V67NkgSJWKQkIl&O*|rLd{(Ky#*c zQwp?{sXfMN`w0?{>=LSTmoV<+O8Co9?KcPs-KO>!kK5FqLR0%eIki8Bxf1<6=1TNw zn$_RM#ESkCJxkEDf2P~_X(}?Ve`*+{;*sIAXg-1$d1m-3BpC)n^2On1O7bDP0_0B* zf0W)v=@)QCNxJ$reZ4`T4pS0<-#`3s^z1mz~OcRqbxfr9pdvwCLV8YI~V zbHx|;eVCGLLB;p=(u;n2q0w|l8PDu{m~OvD)BOOHeCS?miZS6|&7)f3-Ur z&+0EeSlf7}cE_R_bhT)@c1M$!u9}LpJF3UhRdtDWN96>%svNJ~F|UlSz^cATINnN} zX$gy#aQ!oE=tdTUNdv6t?@&b1u9C>g;$02J+*&;?wp8RR#UnY^e*6HyZ1I4( z5le^l-l|Ql<{cXcmjj zVC>pmQ!66)UtJo}MhH1U&$_RBkqy1&iOr-=TGzh)z&kV))Q`TZQSjrD5lZ=f!}Q{? z*L(NG$jBS$0d4<}sUO1UY5RX9E=nU0yp9X+@3sAhtOuHSaJ1^5B{b^Y$Q1yx!k(B0?^S{%&r>?@1K*TGbx2~ex`;zxytGI0yw+F$?qysuqp7|uTvX4>yZfVx; z3b3*rJ6$S!&K_Syxy|mOP*F|r+o^R=9mt!jQPpDEn3PK2QW~k3l|JVEuXT}nS>vOe z>X(}RNEx1dH>EGOADHtYFcLtIyIC|$_<;5G!6;_?N1|WidS!G!p~pA5{{84Lz(ovu za61e%dUbdTU7dj!!{;K~@YPtuhgTunFb9C)jgZu+~faNT9 zmY@i^-RQ9!o;_b2Ho34^ntvlC15hE^cmIqB&HY&%^#&~rVp zIY`X6A2=o?o>$Ah#mAR@t3gf>pVdT*3fc1yvvcR$gyV8{hC4x_XZ5eTnN_{EzZB0U z%59m3X`cq*U~MZSGM>_3G;df08AYDoehhbF9wt zRTae&`ed||>pw8=;QIFH4zBNrf)lQazCtg4L@yHb;wWAK6}+)?ske!!-Y5F|c-eEc zjVorA?YsyLcxhEJNHTkAEbxV`yV;Z0pvbC;k)x4y4N0b1>))a!@IgGZg{H>pF1mV{4A1mTm3FG)m8M)uU)>Yt|E*y;kUXgZv54M zL=MhhRf+HYcA-lB({*?!SB=rne?i!Puk zvzY5YjIQSTXGwK=X|xO6X>=>D3@j+yqYw&rM!}9=96iEyGx|s5AV&8`!w@p(6Qf%- z4EgfO;f0uZ!{9ofB=+`)!&v4X9sVpWMo*A%`xRW7n4sH-4*}jj%Hu$F8{qB3x>mKXo1TRLZ{Pc3kvN~l&Pyk+f-_3#ULxHNHJ-p?=Ua#^AA8^f#fHu= z!ndHi2Z#>rX|AY9qr8}-6_HmhBy<=bBE0Va|E&eHY{x%hAe}|CY{wzoW)OhL>ta%Y zRT>0I^So47F;UPM@IXXmh^=VNpfn%?v);UZ$!F#73ZfSk8T4Y_uG;08fS^BO4YKQb zC^Z=^IKL%FOV=8K_Ewi3neF->^th?mTIS1kh!w|X5XyG^AYWVi-P@WzhYDzGg=p)d znX0xPX>x4{8al5$e?u!LQkAVTwjWp_R-TUweg5L;`M%h+=mJ`Fz`K7sx{2#cXevBO z{M!!=2x{9&7~C2CKAst`(e0bG5J6gdg!DX1h9RhZYIr7Hfopqscr9Il=XrAYZU8Vp zMN{E(05A-B>`4<0;Pzq2l{02bZv+8$7YOVi6<3K;mv`ChnHz z#@#7|yLb4sd%*178hOcP_BILrVJ0oPDO6qHk10nvm1|C7G~v(KBZ56Z*Sn@^@=_WQ zsXLlQ$vw%E!MT8&m?lehXd#t+q$eN9d1VB0-sF5~Dko4HaH2N#vsjq^aW>zSP6_8o zZuBZfwv)xt3L5iEXv{-2jCY~$qYq;kqmR+mZ%L2$9*qo!_8~d6AYowtj(K7iIKu;@ zPQFDtv~MN79zogAJIBdUkZEDSIvkVH#vX4%JG!1azZj66dcG`IsO?BEO4+s3F-^6PNEKHQLKY|$rQ4fP-j|n z8Q~8UK3|I7NrdBGLY-#`oxVj^?-Dxwfsm-0sKPaL1*-7L;q7!aLWuPZT(O}N7K2|k z1QslQ^MG8W4-m_W^CK#fTg14O#V$(AQ(OAJ_A_V@=JZ27V}Z$;{|q7bUfp8e4;+ZJ#-+rhhc3L_j5h5OD%2p6R;QUsozf)1JO@X4kHT^ z$yQQLjd$DkQ?;OYcmdz(X?N8hqvZdB7fy0*KUr2;c-fxzCy+X#QEoX7El-h2>4`u5 zB2s@$KC$0N+K-Wr=7u9nP9oQO$F}kBqK3yZT4Y%{;!d(zXkK>1?PSstay=slx( z9x@doC+3 z*S}24z^_QZ{|mT&kujg1Ey6Q4bR~-+sPT571#G*0Ct2DADh)j{&p->l90fBxVw_9Q zE~L^H(6dTBBQ*!L3c3XRfl)gi4 z0MWOwx1*2U2Uhd*Q&K!s`WDK!^hRS-aH37`mcSgG4uO4fb9hzN!bR-9QpDWH+^EKO z##y)=pCkLi;7vE<^&Gq|O?&NZC+f43oRyWNonqo!Z>MFwm4H#XKb2yST}N+Vq1BL*Pla!7@wVQS6C9LGaqFx{ zW!9sxf2(}*@Z*e)+32fZ?<=ldQ&&G`V_l7}rdWJ!T-ny<#b<0q#mcq~Eo~)r_4Hec zTi=G(dU1~nx?f$>BJXiQ_iNXzYpb`?kC*oiP0jX`lDbvc;M!We(bwGO#iwLr)9NPQ z#!~vGl1nz$ucv$ZrhE3tSoQ_D9|5oARWY!Uu8LU+_7TWuQ(yqVRZ|I@27S4_ngMU&?mxWp(N4MQ`Mc0g$(A z5X&vKRI#>IH}y3JHX}?0mLVLwloCsp!s_H8L7Be3SXl+Nn;{6-?ulN6kxeZnGkvv) zjEfaqghdFl)vDxOUG{m!c370G!@Fg9gw#Q{<%Ho4E?r~=Uzl6B4Iya|ASD#)*jAD6 z{G_=!bIqqLiB2>Tu}0|FY&+UrzGK)Cn;u~@^LL*(C&fBbR|XNKK}XEft;(DOfj+-H zk5pxWYi;|yw*jV3*Ttq8a) zQ}+k9_4JBX_6}485eDZ7kRQs$KvD=a=Ac9V(^+bj* zHAQ!#2sYRuoIKi<4*WdVTd%6=7CP;kO3618$}?tbxA;46v0|XpWlHR$^h3IQ){xR{ zL2UB0R&DF0xQ7Ci$j@+WL0W>WX4Ao!P8U-nf`#Sr`M37gc64qL4If}Fs&aze{absQ z`s%m#hDF4be0>$jTt%~Ri$Zwyhy81P&B-&Rf~;Lx2^`$r>>u!V*9F9R3syv(e04#L zx3)sSTG5t8I7bAqSE2&~$K@XwOti2c_|UOc1S|^WJ)B2HvTFr2!~~F&0dY6;77kV# zq4N-Zua|5iC?s<8+yv5&7yU*d7%P#Pw49~D4?`dqfH&^oQg zSw2acNmW^#F11;w*oicBtt7i__Z;sB3N2m+>~M}-6gY!L?$fIL!J4iC3JTUG#Ssc_ zAqKGOva~({D-P0&WkDtr>=UO@EMA_LEA?jM#*}#cOA(L%E_Kz!Nz!1N zL~(L-Gs0TR7?1gAZyvCq4RU7R7Rkw_-)Ar>yL1%6BSVox3ZUaw)ZNM^!XJ2BuP`>MYK% zE(p5P>F=gP&Pc7y)LhNe1?5Wb3|?TFqZWbIuc9jRbU#T*Hd^fb#*H|uv?8#m zg1##kVU-UR(i31U>Tc@(vz4imSAn2{okQQDY9UQt%WF#^t z!^7vBB!NtIWsU}rq6G2P#X=97Zy#u8amwP@X7>Ppvmxnj8waH5wCqy7yvbv+X5(Ak$Qs1A7rd zI+?sx4Z5dm4OAhH6(F0PbY5SBQ1Xn=yKsW;hkYNGS=Be^gv0wqiK*600fyEwDwiRYek}1dz0}V_|M+nqW zHM)TeQJoCYnJ|c=x*dkac8@K9H>8^Nhx#Pl4=uLY0EZaKmrSXzz_FNNsY6Hs*LzuG zQ|+47IRE&jLb|9i&=XFcGV89`4&)ZTdI~8+sF#|0y8MHx{?D>Q`9xlt!+}r`#8xY~ zJfC$QbUuvCTijxsS!?eJbV6ik!?A-so0XbyI;=EIOQA3+QiDUVuJjjDZ$f+;4^=M( zLfL5QL(KhfpqC0>3C;r6if!F~rc?t{WvN&z`=765wtI|MEhSi`*AK!}>KtT}Z^Qy_ zGtXaW2mdz|(;DpU?``q-^sBqmg;DD)q%}BsF$6xp_oiS+sEZC;>ySsLhpLw4w7ym@ z4tH#BShKdJ##isFoy)FO7Ge<>TlfqOX0Nj{Nn;=^j*CzkaB<3)IKVtrLQzA-nQ#g& z+=5Chu#{c{u!pKw*R*VF`o+W}9cNDFIm3}82NRP5&RSkc7P{Q~rNZkk6vi$%-dmNf z1b6hvR_{( z1So@1aDnw@Wj_8_!rq$j%3M)n^3c*~sO5sS{;fckhEQ;8n)6$}z9~T8g#!^`OkUGmZ!a+D&on70 zByQV$t6J*on%Y}}nXdIYxO@-}7;n(4dF!TL``S7N{HMP5sT|Gfg3_CXDhxjLlS-_G zWlFm6t=}L}C$}_o0?hl8BpXsyF*oPgvzSney=wW2{6S@Fa5AE3P_O_T04o#-xtHKzl4a(@zPw(> zDqr2Ypg4g$i8ccT=)A>2nHGJ=EFKF2#6tQKqJzf|o*&_5fR>h6_BX-O}H4i`2zX&$&uf0I0Iz zi#=1qHYJ(7Ow*>)d=B-mJodgW00F)adkQT7+}(<%Afxb$5!M0O40qE-Cb>`X$uGor zl+NG1)~oV^pc)BB2qBzOl?Iev2gqZe(F(0UEj=<{0cFVnJh8IKv>>`wJ;eNe2M1 zrDJd{NuRKEfF+f~tWxq*Jz(J;@Slp3U$xw#kTv3X?7kGFP>E2;M9OC3){_nj)Tn}& zN6e{7URB-XslLz{3K5v#gD9zXImt{pm4f|!O2;*sTPluIyr@Epm&ont)h+(MKHvqs zx7W3ZWH4X2XeCr?snoJ8C3U`X+=}T^%;1z_qzX7+A+C*4AuCSiE{qsw(&-cnFDsys zQzOq;mr8X!YdXG29+D7Y{)008M#<74y1Rt*-;f2+tLx}u~?fed z_Jb+v-9}w%F?K4o$m(D?unDJhhm$*#EnBu&p2TKKY&8t_z~19{@j0&?F&@+D46kZO z>zYsiEsnUV3SmJ^rKOE^wQ!!fNYR5%h5@#@kffRHYEAHriWEp2dv?*&~HCiUw95d@QL@&PL@UK`tALu$OL0=CH2})CI(h{74}+ zBld&@Icbv4(XC18aPTB{vfW%r9USS6jPIB^a(}*yao?pU!c|Ik)!Mi&IeTmo?UaL` z&9GRk3+BevUX`J?HRLDXYI1&4;SzW<6$`Q!P9H+doy^ECw z@Dtajs}>2NOJ7T?N=;Aa7U>WWUgfk=`T+|q`?@WDKlTOo z^jMl;H|wDaE!x7t-b`2n%HRx!ic`r&ET7bdlEJt7x$$`Q^~fc-^` z#{uR1@C7$xv2m6k6^g(Dq4E-F@9Fb*TTHiDQuEIcOpqd_#!M2aI)7habC2BRp$-Zn z^UzL#m6%pt7E`H0Cz(sC%b*TOm++%a^=}(c`g+NXay=GCiIfN?gDQa}x4{?8 zpUP~L!)kK50*UP`H2X=9V=KF$gIC2^LF9@;SPhjxzb+IA!=?p|w1rhJt9CwgQ}P9( z5G94nTZBqeg&=DJQKWg!l91T$CEkoMQ(59;N~AV>^S{FtQA`yf=qQDeikrH^!XP@J z++qRUfmoJQL;*hQD#5TugcN|zUeH}jU>i`Hw+amucBuk;SNb8n$+9_3C%&}Ta~lV7mG7I9Q<`XyqToE+}lVL-Wy z3wyXyha)4bU>52ptz&YfvK0$#_xHRi9bH2|ZS3jp?&!6HNw`;BIVA{Ka?fL4I-pu7 zp%{mMn-81;N$fS!p*#?Diy2dhg$R}d$>}*87huBJqnI0{pa=I)l~7Iez#v>QC`hCo z3ANfs(oN3oJ|UMqF)H@Dsb>H|__`23sSr$4QV`N!kph&$9Z~b#9C}q=QZ^hZe#(w= zS%8%0sT2=W>1QY^<{}j4#j)=z##U%9NWUQ~oKxqLn~QK^$Cue{mX5`lGs$NwB1OT{ zSd?^OSza+0QhOTuv<3$K-Euz)7{Q+xsR#kW4~K;S%M{uGCR?d)Gk?+ z+{WKRVX9bJx$rb%i{R}bULD;+r$7^=P?^H)7jPu zccgxcc=uM>?R7X`GSY~Im`45GVOEa1eI#v}{(dqcwU#0PT2x`G7%eT}=OS4kemZm0ks_qMJx_ zc(!+edo`d9!8F{K?4kh*{+ZUCl(}T3mh&$)4~$o;Jhl_cJ$aG_J8xO*@9pmB^w;51 zPLF)O(u?4KF6D+RD=$xfC;9|~ve3N(;9_N=(o!d}EKlz_>Fqf@KznvvYE(_BCDjv7 zIdbo;Pz&YBQRg2BbYlMvEb)V4!3^~-Yh1Uw4i>{|_oB!$A-rE@@7es8L#60F0(a)= z3c*h_ZIwKqU1A;@u-1QTKX6HOvh24!c+^I*qpiN^1LIcL8=yP^mZz_wj?S#kO7{*x zYD{WSSJ?rsXoC4Mi4=!!BjCVE-@OqeK}wmYF4%ulH|&)n(yY+Y1X)dl`yT}lK%5he&9cXq6T4W_jhHJ z5O@%{wMw81(4@7sNuo&}ytVxsJ9@ehnpX7V0O?oUU?(L-N;^P7;N6mfG-25i7&VY@ zp#sh%mBsLr4X_K{fg-W6vdGfsx+hH4bj-_0W(_z|7^&X2Ce#3fPAjcBDJdmdAdm-5 z6DI?OQONABluTA~Ya|A;(8EqD*HvGzA6_k~VXB}U%2^)@V$nyY6zT_f^$8)<0tSZP zdMV6qq+>uFzL7kT!d80A(NMr#YVWa^Lg(W2dI*uTqm!H;t-MKz6MH5?%WP$-%q{d6 z$?9e2&XigKgJlk`-B&a(P;yZ08qdbSlw@h4{WFm&C0#>z5DqOeG?$P^a`rjOwG+~E z+eVu4?v6fdBD-OZHNj|3q6CVfY-Mn(4=_BHnG9yFTXw~fkev0&=YmGe@yKwb68mk| zBu#;k(nUcAAueajQPS!Txwg5fa@g))donrXlw#!Mv0X%{x_R4`n$T&Nvl`oPY5%fY zeY4C$nLB{exgEgSXjQjwSN)WV~uJiS+@It{0sx2Zu9~nrdEmNQy#)(pF<;h zvCSVMo~qB0JzJ&h+311(4n^e2LKIfoUF;K75V9;KOY$B2SRt@1JD(7@1(h1CN`oh= z(q*Np^M^Zdl$GRp?MkLXa0?Afil~%I$|HHwxitT#xoPlh?IuD`Xvqg$X`R`b{#yGC|mLD;c{%wh=!*x8vWgr?eI zb(UwoF1TMEieMM*VrC6)c7hX0kfpE-$CxlGP!r{kK8?1m{jn zd$B5>n+b;k8m1?iEh%=DU7+AQ8WH-_A0oY{GiE_KW-mjBVJCI4zsnjumtM(}!=-;N zb}u8Vjb}P(ty-i|=rf#m8O@^OB&EVUEZemQnrN$C)grBwQ3Q$)+}@dNb#=~0!fI)| z2wOwOu5jQrB{wr=r|Ye>r=brFP=_d+v7sgw!}*%!ICB)IiaMV%T+=h0mN-h+rTEZ$ zx=&3GoNMeGBa?I@OSl^Q6MW-{ekUV+V8-w#IF=v41=NRot7h@P2IBX_y-*7wWgaV07oz4-8E^tI951J^^kN{mRo=r+)eqC~lx_ek@jU~oV zcDDC`7)qS->x&f|Q|Ywbne;V>zUFa#J)OJjH*m`DCSwWLHyg{jzQtI<^?-pRw{JG; zxPFV#$n|ccnd@7PR<8FLKCZ7b)^okxxPj~3xX~9o&DhHIa^sT(bT54k)7RhA*NeW` zIO7c}JWfge1BuPaxHV^S-Dh6vi%m9HbG^*u^sI~PUFM)KR&MU*daL;mJ$sz%r?H_i z#WCOmp~Tqah}tN58q0;qr-`n!zlfgT^j1bRim?ePv(8RIzm$sph+28rq%^7{-^g!D zM<1eaMM#%Qjxy5`rb>!fM(I-MRA$NvR?1>FEtxV;O$iYQ@)$NPC&9~vKh8>D!p_P} zpQjY+bn3OSY*HHi%!Cf=F8ZyMO-`ponF$F|@fjV*N^N45nPeg!P|L=%S@}p}W;SFI z*(R{Fb7V`>%FKLd9;3x+B0Js5=Mc@z#B??_;m0I4DQyACOh=0st#*^yf97!7GPrv-xD}N5kpJU<&Uul2HPJbGGMn}uY zVzDYjUduV#EK=^h*F!5)u~9~}WeP}$aVe;WQRRzGFfQ}O%8Yip-9Wb=qT9RZR;SyY zbURG9U!YqMr!wP5bo&#!#j)#U#wc!0FK*4TxHSo~Nsvu~Z2IXoK(|}zb`RY?NVgz+ zW#)5q`#jx#lWyOn+h5Y{uW=j48S7ingT@}#tNP;eSJ48F`b(-)h>hCIbF%r2X(+aRbA`ddIV4}XjC`?^@| zKNixC+6T)JHV@I|3bX#jSjC`mBy=T z?-H*%6};*t#*JM2R|8Du6k`k5zHM}K?SRqC^$UzJ*Ebl0T))n^lk3+T8rN4DJGg#> zaUa(|WPlm{u<-!bHyRId?TGOR*KaZ&<@)8u5ZA9TqFleyc!KLQjipR(*XJ4UaJ|+r zxL#)*=6byWYTIBO<@$W%J+3zz$GF~P9OwE6O~$o^Sn-L2u1_-;aecbEgzIOT%lUkB1=r6qtN8-6j_b3` zMy{W2HgkQp*~;~EOdr?hnCrQIu6YC3E6f|Yemb$}7n)nRev#SD^^473t}igdT))H| z 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�K#X9CLLMlTjVTDk2d@AXRK#hf17##<_1 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∨_&TaE=r}3s{T^=EvEv}%HEY(Pu@%gK`{e9k zHINys=l+es_1yo>U^n+y1^YoT!9ni-mhl)R`4acPAAFMgkFlW=rk_Kd_?y7bK|VJh z4^$WSzmnhoN^buvIRn*&=%AqmdH;vFH-WGFsP4t*+DS0PU=vIT5JDC^JHe8?k^ow5 zE3s_Fx|S0lm}}|Uy0N6I+(nUrKrT)oK@O!rS<04zlWx$elznyC+tTIHzCK=;&!cVn zFOamQEf4xiTfgr)XJ&ru{ALt~_h(5Y{pOsRIdkUBnX}EQXAqwq*nj-1k1hT?c#Vg; zIJb7qrw*KT{NFEs{Y7i;`FE`5@gF>S%{|v(m5%@5tk%ZkKX_W}!tehr|9a(`dw$Ff zp1tOtU&UX(|1JDu@$0l?#0KC0I{n(a?#I37u3hsBr#6-DIn_RIapx+b)ejH<Qty zukp7vpX@#VzO&XJ|NYa8Z-g5ILy#`-J@vY)7T-y3`{du(d~);#h^m$Mty{l1+I#%p zPq$Vq{`s0up0)nkQsdm#Gym(Gt%>iG<6ooy{y*X8drz;q=kMun|3eSx_)kx_)_~eF zy!+q(UFzm7+}S&sgTB6a!Rfb8VKOw|j}RymYCYP0|NZzC2-=$#KR^%zkCq0KL%jYw zcfDj$@ccgDKo-H&`X7&8p7;0m+9W(yH$jT)zq96nD`^a7z>8nM|J^)ftQ`;ODfCs= zu6@qoZ|pp41I<-9_xt|<9!-3_BKGl*>7$T=rusYhmDf`*?>dbC%G1BV%c^TE6jbHwd8o%P97mj!O9~v6(3_LV8-Wh!8 z!}#;x;{X3Te)+>V-v=M=8t;7k;XU+^myLIZ9`1~9d-z@BogEK<6qp|VsHbBI|ZWHG2H!{3rp=A>DSR$Kq7J{ zfLPgI9(`cwkx}}vK>u91aLeK!Eq>#guQR$bw>#bb;pz5cr`z8<-F{?japmdJ3m@(G zVfv$$f5=VBTIgnRSV>Dq94Jsq)8H#2bsxbJFa8}#-RiE?s|J!)yTPOc@{OX6Ei66+kzV4xZGQu5GRD zOq_N4^y^P|Zi4F?-1)T5P2k46RcB(=>Hj=^nvQ$YBA}ww)PqkiojPCh$YW!4;yrf% z$Pb|}4E+ZkdSn*YhhBmg?jQO$B4uw&_=C{+0d->GHA_0 z7VuG7KCAc9_ham5#SibFRAkjUpH}u0C$AlU;mH?`Ux~%tNXGK>Pgdwyt&MjcJGp?L z$fJ7x$$Q5;|3C-qKc0Noc-JE*=}*5kzVReH(VsZ^mGSPAzl#Nbf|}&ollZR_lc7ikN2ntXVs}^^O2rVyh-ggO84R)#kKAG zcdlN%b2UDSwEmbmT}vXv19eUr&=sJUIiBU*neDV&Y^q$;&o5b!(oPo#=3I6^Diea8Snfv z)~xfd>cJiA)N`4&vBlb})_n4n8}I(t5^e#PTQ$t6V>?!paJm*f;1m5w`+xB(uYCD! zU&hXP|GDbqv&K80J_$F}lzQ-a>J%J9!1*tr{{W3oG|Bs$tVz?>dKzo4P>|uphxkqP zzy9kxHkP6>td-eDW0Bu&@6S{xpF7_9%t^@f8&6(?0GE?Jh;301{*5|?>k81{j=>zD zZTBrKgW0W^?=3RlHTS#)f2Z#c#=n2P7lRmDwb&~vDKylp9)P%x{(}99y|!v`*9G*? z(RKVu4>sP)qKLto4fyE!yx;Hz-3cG_?9BJ6Kf#b5(0f?#-XAW0g_K$${Z2)3WEVYh zE&hQm2(5OUw%;A3)xMUf@+Kn4+dz<$zck+Y(Mg<-|NG>}#ydZLAN?f!6a|5{S`-VK(O&!5Gt zPMaRgzp-b{js=eWBASo?*Xh?^0wmCI?;u#Tc#wO+U+#Vx5Z*U%;iETDFL!H2dGXH} zApovT0%TkC?gBI#-Mw&!YOQ);;6jG9Ebv+oE6oCHJ~{MLVghghE^hC?MLqIdxQ$^o z=)B}4B6xoC$c-dJAdXkj9r{xz4^zm`Yv}9Ulz{ZO1s|{ZI&KjwOG6hNziYL+>ug-`hE^{Qty}yi zI=JTdj{l#F?pnwC&#L3!y9l2iJ?HqBFS>s}Fvt$OerVlY=bB&cA6oYS4NDkSe#8>p zJZBn^;?}AIpK3qZ5PA)xf0_C9=G)I%9JpZgfswyl9J9UH1?3gFe&B-kd5c5ez4z2z zPr@1flkYrwZqKPlpR_piqicr#vUhQ8_1&+&L$%K?VYDl+!c?>h`3t8$Lr0h4>gL)X zzfts%Qt#;EJ4qM($)B(MGT3yr97!6k{p`ho^+0+}e;JfoJ$gHsz_i%E_V_o2aqk~m z3xY+zt)@me=sqrcfX6LpStI*0^xgOODw5PoWLWv)$yd`q?i=rX>EwG!Py3Vc&P^x( zdVIq}mycfqRczBkw~udrs0}UcIR5`0($79Pew})7wK{bf_72;LaDtwV81C{)_25(0 zsrA^|FF@z26#M&A5a**58IM?upfTD#_!I?CdGfE9sSiC(fg)$A68_6kU5i9Kb@zE{ zZ6f|~uF_QZf%iYWI{s&M-?P-+>KT&TaWTT@CMwf7(R;4jsP0>Z60)mHsu!E}nfUQ3 zak%U1hi=E;FT&qfCH`(=kaUCRXBtq7`YgRGp<7&%X(5jp@QZ7@&4rmZDEf_}z54TW zGoSajYST^U5ReZ&*I%zRCUuKTGw=uM4Oo`TQ^=DwjV{hKiryg9c%GMO!g&EzGm3=Ii~wNpBqn1U$?VSnVX_H>kk*u2gyaa z4xwRjYS2oqLIN%RK8X~@S<|uoJNqbWO zK!jt~ng0702B_SrhTuQzh9i~Ml>YNGOlu_hHe04~mK}s;G*Hema3Gff&*w=lo8ZKt z%aG{r*#RI_tllskWciVkBQ@~)7}<7K@jM7>DqIKnX}q$6G*^QpPTl>(aRj{Vn!00C z(^wIWnL+f2{R`!(>P%ICv2^4=nl6}4bbBP6&d`Er0A>fh9`U{{n~Wmhu!R>Nbo=wn zLXA{s+vbn)DI|A-MFTF3vq5|YU__L1xkYzi`DS?LNQ^ygzf=U%^lP!3g!1^Li1&HF z5ZQ$%6WirjGrnEoBr;j68Y;dFlr1_jp+CeY0qWu%Thq~6z4TOmLpNR%;|3WGjsD3A zncAo}m3rrg5Vd-+JakL>tbx6QLuVfx>?`-3#s4_iKQ^`!|9{f@_5EYFj*Oi%I7I(m zgEf&;|KbE-`)dbL%RJ_Q>Z)+3n{?Y2;(?K&9D}4~GaWyG(~mn|Dc~9+$D9d;1KD3g7XKwXY}VHz&V&?X)_j6tER3pLOx0lHFMCP3SgK+)@l^s=-n z-KfEyEMQl=n4wII8GAOfbW8(2MF1~Fb~O*OJJ}!U9{ds+?wqg)fdZBLp9BJ*EKkvpyqXPUr8sfR0 zX_S#7h!Tq^LJ&>k!a^A2FvehZ=zg9jFb=jm7?;7l2fI}13JrIWz`0~#g3O&qj0}9f zKwaZta%@Jf&1oH7%SV&y1wGMqsbbKVp+z_JAlK zU#UC2RscO}6RkvAZF+$~IyN0Ps;xyQGW!J*CXx{aQBH#XQ)0tM>I^gz_@o5!N@E- zZ!qKR5g<2d2uP0km*{>r35ZK5698XqBcZ(6j4WS;!!}^kii~YRce6!+Uf|NMSr-#P zF7OHJ|B9K_R)Kw?Q^l1ut0XkcaUuh^`FbYaOv2u4V7CjbQ@%&S+Po7DSUTu|tua)CnV3?g2@TsXuukb#76AKHSfm;an3=i=E07S1 zjg~6CO!qe^P;QyXO42mm(4_IIl97o+zBxjc-YJhrz(OQz@^anh4qu=24mYoh!0pv= z6la19v-^TTaAZKiNJlCSyml`l;i!h+CGfPl;;fAdvaqfmBI_!dWGbyDo%{&alGPj* zDA(pn!>ZD48t_KboohjmCQ8($6wOE%qBsS%3gll%#>=cMQ0nYFFG$38^5W8x(<%VS zDhlvV;VteMxK)lue8UwjMR(iKliVXv-Ohsw##|J`LyhGGc;~}rv+OnVbyz`I$I%(O zGUHe4u15vZy-7(sN>BGIrr(D)=&GUg`wi?qfpu>hs=`D|t07x@#A5>IULh{Z zogOmEDhMZ*1^hbH9b=^+WhEHLvpN&)W|GbyFk{{?z)l6WjKXFpv4hJ zgwo#B^`?!<4McaGnfg4x8kt66ZoUTuh5HhM?ZD@hA@^D{?^^`Yy;K%*!N3j}*jvrC z^N*_WvVo-pcmoZGy-=LozSxZJ1>05UEu5se)gxZ+>zvGU=D|$Ux!oDB5NJ2S2s*}P zoxd@|zD*!q*9{zc^||((>2QUi)hh+e&0o8!({n6eBU1gI>GPm(t^-`bm3CFVVY>cZ z1N|z2cJHV$dcu?RCXoT=zF84CH;o7`PNR349uEnmo1>);7}$fwCf_A==Z2#v1jS~& zY{C06WsES_YHTc~H`|i5(Egwq#-!l5*14zU5`|M{;;8Q4?8&Nt7A+UOyTNuZL-22! zeh&+zrBj26u3onbmKQrG79Yvrp=n+kdA`UH z>2roiM+KK#6BJzhs>?AA-PUB87FhQcQef%M8b8n9Yx^Wh0_* zR=|6l*4SA0C{Eh8!t-H+p)MGF{3-SUy)QNzSu`iW*W+1IUdyzPLYu4@m<#qA3xk=+ z7tJv45KLQ~M_1qEJXIB)lnEt<^BIGrAxPY;CEQMxigrMxi}ThD#mbzfZzwrf43h7f zp|k`^k5lUKc1esx<9Ll;+O}YDi@q#|rbe+}qnH;I?zI&X#Kl&}%Dg)T*v;7jZ1$F+ zfMv}Efp_Txx@*#Nt=O_^&@q9%!6|G6nu~Xv0VFr~Uz>$^wGWl7W(;9k=Gd+B8bRge zc3o?f-reP)r7?BPpk6DO+}vI;97G_?-oHzbxUEk(6=k>Jb(W?*&hOCRuM_YW;l%~s zyJ`1iSqK9psG=1ck6Gx?o5{Rh5N*f1NsK7t^I)<#@z7cI6!Ns*eYbB!xL}OUAzTGU zHCb&LmKhNVbiveFHYOpI!SoJ`=?#L(b*~&}0~H;;MmFPMkTDz=NEiQG$mpo;{EqGR z9szb67)V<}EnPeksmDBT`n^{`-J4#69*cGSW!v!^L*^S77Ek)iecsAkP48nu=eG<+r zT=iLHrW0B}X=HGae@3RYC^+25w8jDF38HQguVYZ$Z&92O6z*lE0nif?cUqR{M9cbs zK)W??g5HZ)QBB8NEgjzy0!qGb13GL$e@;N%$_*YFfbP3@_YnDj4E%rDIlool-69G? zWk24E1V1J$Lk1I~{q+L;ykK%G(jx4vBiUfM%Fg9&g28^ zVlliUcnApu(GY$iYzWq{XQ*(%QsJFJL&zZbS38Gy2?Ce+uu@BzW$44{evDGV%% zmuc7!3as1vM9y6C<@Fnw2L;A0uMCXo?hCfNU-E%M)bVRb2J|m1=!XQX_b5 zG|=Qw5*mG2pxx^%GKRXh?lXfW8vaSa;+9kx107T(G2`QSqaDXXg2BC3c-Tk+WlZmp z-tn-&UZq^taPb-$z_;5@f7t-K4C3r)ic}6b0EKG5B7klM3-e9ruQ1aJtkP&9-N%4fdajho|#b z7SC@Ap6-052(cjfcsM>$R&Pl#xK*MY4D|+FJ`{H$D1J*&xCL7d3b?s|L&%%?W^VkllsIke`x9QDbu54KaY_~K5mVF)PjCG1eC<6 z*%K@6A@>}|P_e%C7ndfH()VZI>%GibtI30CKTW!ihp14NvIYHnSC;E_&cYm&h z{=8@86t$8V`4$WM1p#&L1SU0!BHMFc;|OAOI%Elu$$wE$IGt=+6abG-j4wq~Pi}$% zetc2?O$cW;!xx`sIzJ`QE{CrJhbm1Q1ivE)$Ptq4KDXmL&Ngsg7C7e(TkdJmurcSy zt}(!05n#73gBC1yBYd3!{Hg%Djg0!>NVvEStKVvu>}!I?xn#7zsCzaf8E|BY>lOdH zfMW2K9k8ALu0Xj&W(pQd_4zB! zTz^kMpRLv<`Qu>Mmd)F?Qo-n@{BPS->AC8@b6{M0>ZxzJ8f|wY*uIfH{(|5S9SF7~ z@Y6Md?Fhr$WJd7EE(8dib0R=I&sKxr+b#r~3lMBkr570lf9gU|K*a4Jq8-7Xxe&OC zxMd@V*lR}coqPmaHoA$pWh01)kbhZ)KX)PMQ7g`JeFG8ic6|fwE^&PW?MBqnb94A^ z%SMo_^V{$`=W*VajUd{9>l}Osii)=&y+m3AaQpz>N6z1>6Irupqc!xf1~e3E7}bT9(MJ;O|4k#`Ud*p zTQ1-B5Qp7@T&P^W?EwW{yQl{g9Cg8gznndSyE=Gr$}t||uXnF-eFcAY);dQE`W#YA zbJy`LV zXUj?*7~Yr06yDKJ8n@-Q6d*@^j#V4;HDnMAgLkFIByUgiM90zL~6o$7uR}&9^-`(0x2c@ zdLBjqp4<+vcNjGJhv-VKiM=6r?0G%own|+O>6>_+Uk60*kd8TIEx=%U_lTnZjg7bm zuh=H1l{T;U+SmH#x>M0KnVatoYH5I8R!*>01|foPh&GNDSEYWnv{Uu_u;5wIYJ-Vf zb7I1&e3KeXVM>iUK0o)<$KXO#THvU&%9H}c(X;J7%=_t#yTL;<+P>}SN(&DqPfS&ZC?U)a9UQw&ElsFb zgyI;-I@TsC)Aj)iE&9^uXcQOTs9`;JLTJycx~~_GIf&xLM1rMJ3&YY8oz50U3JL5( z5j;9R1xC12&G|Z(Tzm<=tWFZ{=I-)7>K~6EXJ6U2v(JBUyhTUn{>R<(zVhxf>iJ9P zxuK5w=1dQ%(oFlUhlYB^R!CnU0-J6m!V!TZ7WaXn?4`5bVWh1?J@dWJT@T{2-Z!_w< zX!kbS*cNxIrCxQtXG#v_?HZ$~3SjJiNAZ9_Cna;4JaAx(fH^sAF`@z$-DZdSVkLD?_iTRO@nT^dz41>z)D z@}oREc$l^6XnsW;rb;gk;a#R0a6*Hkc<5SX8ZIEg!9cMP8{5Y_o^HH z(sBX>la_U(C{pGhWoI1-_+Ir64+-QEdk)tOK7v|ZTQ*98uHFrJ zSaN+&xBg96+R|^)AJo4dJlNIhT3_3^p8najas38TXvs+ZQnf8;bfiot23MoK_&s2_ zRh7LMnwo56;26VMfd~^n^>D`CrfvzwjrgqjTD{#|4*p$gz=NLyy{O`tSR{GWU7Iwm z%Mu2vN;rTIs+ajtS(S=IMkFeFyfWPu^IW6guBA!`LbPyF+F+PN963s-`3T%5a|8_6 ze%ZiiwSXh6bY1BnL}uv*wbeUkC>)LUTnqE8O>&e9VL;EfgmBJqlS!qyhcyDyCRt!%%uLnmukPRoBnX=)1$wk1Z2Q4<624mm;S$=-xTWMQtG zjp(L?U~hS}#u?dc)6ZHzbB06+cXALc%NAsx%r+?`RP2WfpxCqWEYA*15>6YbcMG)z zOm1t4noX0dHxSv)M>i2hoEBV5u@?!@jhWbYRa!^p1>fzNk;ZTAMf?ipI0-T0*RU+s zvAN0wOST1CLukK#gABp8F``*h(1KYYZ7wvmGE5^XKNDd0u*fY97rL|Ao7_FOV6=C5 zO;aO!u^y$B&a-X4Q5X?&+EAZS}-a$t?>Fg#k<$#3CH_O!}!VPs_~GZB&Q-v$Y9a1SPnRYWanc zDAe+VNnS=IN_$vyxQ@$dkTsUlE@_Yu-<>}T-3S`mTxyG?p(^djbGzmZX&JUF%(T;s z11niz?o zyWmjqTiJk=MMa=2nFz6w5Kfp@Mdc##ft?;iFY*Ubn7wSXTz8Q~;lr-&w@G3cgfON% zq&;8kfQmE+zX1yj%;=4c%BY5t8ow|uSfU!6e%ZMS89!PZ1<%8hEs zKhC(h7oO?u#zrHW<7gexMI3&yj4`n3nC?>;!-ML*0b`gocVJfeV{+@*>>SJ36#u7A z4^VI;sY=oHvY#*0t#0$*M%T9F#F$f{FkGsZCe^`!Av94LBurqP0(1x$cj$fhSP&e(iEEy2r6wxC*aox&dg?V>A|vSNF{>X8KxWak@<3O z%mT(JrT+vGQ?V!GQOTrga%4@=`5EdAp!0J##9F_4v7fBgaEs`;`RcyPWUVcyDUlZ^ zgg?}Y=1p$W7)N`x`q>Z-)yAw2hMaxj@1aOAJD)2VIIa&JaHa;k^*7mTNlPitwgas--up z;~q9Ng3(&PwBbx zgFr=!`AXAq#N@|BxySno5H>)oYt`q_^lt-z2L#rkY=UAxLMus%-mNyzfxw#Z-KS)VJUY0qxedd zkc!W^a{b7WX#`(Np<7c3tAWox=;Ff`yEUi55C zb4rWp9T@4a4c6!)6pFU<^O(Q?q5{Nhu?|Ix;4HsiZeT>z+oT;PkjCJc5E}CpE_Bo| zYx^Va21W*}v&Y8ThbE>gcpyw1s)g(Tcl=J(>tRr!MaU8{W_FB+D9VErg5i^s10$4qKlS2^$!Z`jd3m)I-f=uxK+N<>Cb>7aHU4Dk^(=F?R3{Wq=1r<2*1` zt+E3ln+hY-;07S+yBTXbrzI;=aN;jV_+1jR^yTpp7+|R&jDPlD4%+GwMw1~^xF=yr z@tZpYC&f;LjdTM>>JMf)Fd~(pjX4V;Ss89RFtVo(;fSOrEELME<>Ysy7Yb;nDw;jJ za}c7ei7FvO&41?rCaOP<%K;vhtj@Ji9{f>FjBfv&Ox%g=@-Q)S z6CR3S2aTZHmcz2P2_{S`c!+e`Ena&rAb8Zp4+rEd0oqT4j(+vwz=%O>X_;j*i*zc) z5MfaodKM!Lnfl1`rY`r2 zYNJXo2+_fDlno44o2^DYNb_WTu z^7Jp=2s1|6#UN?4?`N|m4ceeB`712q{HLf!8nPu(oG57eutG|CH#eW#CZH-g+(Lvg zU}7gn9xIs^sswAsjoxtG10#{x=*ihR^cCZ~665ph-3Lb4au#~o?3}>*gltA@$;)R= z{)|EH!XR@soK4VhwuNapw?4F}A>1E!Ofn(6tD2CeFYI!z?TNSo+7GCJV8z#GBDshJo1G1p>75BPL|%z0@iZCn`169w|qRb zYwenNK5H;@uCKGH^D*YMQ4bAZqHnb91FkfLR0JHNUF>J6ONtz&es2n`&49N~F|+u* zK>r<-@<+tu=1h+QM4Jl^*v_?>mp_!1>RH3oL{>I-rqRp*eJ%x1`ZAgcHc?sK1VlNU z5k(w>^=yTB)`gR3lttK7=|~XWIH`XOH3OJEYLtS|xeY$6TsgM|Rd8PJ`i3z7S2%86 zTgaCGZ7gIyR@n0QxxPWNR@Bm6YTm=0LKX6ivs<*;*F-ic5eaeoTeNSA^Kw$Lm2vOD z!Gk?fPR`~{>o-v9Lg!N$_iI$k+dq<<#ccgV1`2o11`qpPr2|whpj@4qv+3>N;EO^u zW{LmP)mnW_1r|lbqCdQlYAUx|Rg_AI;^2v~-T+d*B2yhGZFS%T@!i$=>hxfZ132-1 ziEm`QQky_4f0_xSH-59=lvWpmZbf+~WC8khmw3G)=F8%OMD$8AvMPP)k%IMrgbkC- zTZ_HxH|*>{J`B#H$zc<6H`kp`SHq)c^uQ8Ol5XaYl^U#07@`_yB*qgIrHB|}1l^(N z?i|%Gv9*!(t~L2{WkfRacvfRPuU8A64LMlFnK=Wz=6!h)2DxEKjqcQIO8V*NOl%LG zOrQvvHvDcZJAz%cNzmD(Pw$u(cTTbN9uF~17W4qsfzBe1D#1GRgj9Hmp=+6X8M@Gl zi$aCNnY@m5ot4Mi@h#W~?@@0HWfF?|8_Dvn|~V{4oN9b^GvuH#b@oqtB?1c^6r5 zo-wPXB>=LHaBF6wk^3-z1zgA3n{k|dR(&FH>^~b0g`CfgAj@1bZTIpPgDN2$J7kMT zFV*;1>+!JmaL{qBIm$Nx=4~pJ9lxApOY0BTA(JMm)AZ^nNefc{mavq5J0C+Du%hYK zDC81p|7~xPHb(ix-G!;&mxj?a^gfKs>72Lj8m7VTNYjg?B7xKPrzn8lf6U_2;5Xju zE<=g{q4ICto7AcC1ypqKrjpY8>j6P4=n*CQkYOG&ht7kcEGjqy=D{r5@)j`UVItX$ z>w3`Q*3PVA2X}j?YWTM)=h&Ht*Pf`J{pGIAgyqpc?iV70!g~hQO9MyEcRJK)Y8{h( zCdrg!Cb~FK(nwK3-)3rc;S7Zh-Cx}Xvw{h<#mPodYyyN|S4jN}3!SM025^52odK7Z zo_&+Tln_#$XY|@mK@qPCDXKbHC(9%~f=j*OV9(E^=`2AP4>>ako1~KxJ~}f_V?+Ld z`lgp52$J5q<5{OmuAJFcvZW<#_&9aOmlem1yHoEHnp-|O1#VMIKUV)7G#>)XozFr~ zbhE?5;8QiW!An9}*Dx94h4(frbVr$I@v(7Bbc^Ho zNmO31G$8ePM-=23)lxsPa>zF3w(c4p*t_T8K`vvEdv5mN+=Njl|J!Eg;||+ZE~@YUl+iG_Z5C@*v(3BEy2Vpv;V zt=b`TpiM#1_P#098d=sY*XyVVI!E32L&-a;6YbIYU4DZ@(#hdD-oG|yQ+qg`^-C=4(n{RKG&Wz(ceXqlx(36MfGivH&bdNWNvwn6cbGw zJ=Ue0FuQkkm50g$F#m#as_IqVu0#+a*bX7>~j6oc~VTR%M=YU(>(9Vv?{T?AC>_PB{ zW2S)jN>sj^H*o(85l})#(ihFO2m?aZmfu#t5xCTVWfz2F|IPIz*Ts4!w8BSW2J$zs zxw$83<{%egy`Ey62mu~6tk2I2Id_08>SR zzPG5Qcc`CR4pAij^JhF`a?Umfcu$hK zo6vDl<{}u*W@rjrh|h&M3VCj|5Gy$pmk?v5MtLmg#>{ITa?@k1F(oBGIEEgj4zfTH zx|C{Cfbrw%H@!?r7^TR+!5f*l3$@ZSllo4b7Nu*ys=l<0VWYW~J{_Y0?_hw9L$pMVF@PX+8sTv8Kd~@~G+A6i(GrB-$s(D<;|w`z$B^>>#GJJL@->68NO^5uf1mmVFQ+5-C!na?GADgm-dNBJe3>|3Ecsw2TV^3!17qL0`<<0=RU9@Ev2cegk5Y z>}}tEwr+(G10}1Gk@kMZ5$+1~_2)W(XIhOm*H(}EF5>*+6c}U$g@kW$?o~3a<6CEi zEh!fd(`QPy26(Kr7;n*Bi!wb@j%OV6zago?k?^p6JC!H;0EAyYBy8AF8S=lOeAm-L zYWok?^$QO2awPE=RMAck&Wl6p<-V;9c1EPD@qV^z!R}7BXrIE&0yJfGA`wcWDqwhU zf(0-*;LON^$K^6A@pwN1qKtUUM$jG~G9J4^d@lsP0AD-s;eEhbbIQ)Y^B5=>;K&4I z{DQ&H)-0$y0#?=OppPEPw5AxEFv}8$M#!|=cutVGgF$T&4+B;wcaia)xRD-=NIFQz zquf2b?Wjt1s^#Zcrz1P4jz4wZ-VTTO>%o9za>2X&!r zy**#5mR_8BJUQ(fi-1KLinxagp7~2+(wmf|2@cN&D^v84sw&NeR09x>9++y+9u*5C zP0e}pC$tFA^v4^eib&0+<4d@@%1gY@+rR}Q1<3|coNu;?hoDUcM2GwiIWuYp7(UsGec^+|#ea%1`?J4jR2*W)sWO_?ux*1N zjZXqK%OXgB6c+j^Bb3itczXjFJbYQCC&R)f$4`{@vS_hRq6XXwfb>y%z`K}d!g z!*i|W6u>OZ4_#2B&@Y-{uzsRS6CqliygUcvJX?sU2a=$jX`DK_va6da7L7rF z&rv3~ZtUK!N)5-i9^~=@t3P{OhLmgDZ&MYrO?-RsJvAd(#5Z(k+J@lf7hK?G&>vi8 zLJoHHs}9m93+zMn1>0xQ?j~Zt$#qar>)-QuPJ_iIv1bu63j{alj10%1uATrp)Xos`01nSjVD;Pfm*a=xT6=rE zCGkRCP+*Z&STz5wisM&NLju7IY-eM~jI5vu<+7Q!;aE)08mjW(ZmD@!N5We1N#!rQ)Qc6wQch@R~+ls zjhnZ-zo9$})uPD7xdktZt_mr~v$s)0R8?h~%JZ?)h$p#u*Wdt!<#yhH4ZcxbrX)N@coIu=zj{N+2%v(mv+ zs)5S5IIElZO&RYkT*Rho9N*EZM%I7jz@H*xB5 zc-A}b!bHIo@mcT5`oCyYVKfpTEOPhurQ6t@U5^vHjD}7-1=QP&%jYc}`Re z3{zl99NzBD);iFrwNM@xK4HBn9C}*OZoys|e9a?XNJnCYy`*LX{xh9X?{8@Uh)uT-~s*gv@U9G5jXc;^sHW-hS=I6Bic8y$$pJ4+!0%*E9UbSXBGUhyuV)hKNmpw3awM=BI~b!2{IPmndR3 z(0qz&WnAkgfUUNV(Ic?bZ(8G0zlP3tV}bmEcnrd(HEUo+@>&3i^BfS^v;O-V_ed-_ zQkjJj&D`IR)$FfY#IOmKk1kX9fTI@G8+`=DGg9R`QKx8md@4cKrbjFm`8hBiNCSUZ z2{E;QG_9_H{yI~T7Xdqpd$ge#hE3=a7O>pyR=J*rg%!#lMu#WYQEfLg6OWejN0^TP z;wN~`{w`Wl*4NenX+W$u4>ky?DnbABYd_I2S^Ifhen-&ceyq|B-Uv78O!h~WEn`NL zo(~Z@v?)#2G(-*pnEmf^k;n_77acX%2Hn1NogRY}1Gyz+@BoXSPmuQhSX#g&&nATt z`dNBtp*jJ@Pl`;6K-rr1^WY{?n=oZ4JKwB=#4K`k0qP5i$n;P~?!T7g;&B?xw#K89lC@L|h&TGR!b$NVW!N#r^X z#lKLOsihvTmI2&5Y7Kc2i)1fVmiL#*H&8LXA$?B80$SNz0p!pQ{I{wIb!|EziLe$D zfS$m)2hpEW6Sa70VE6U!Sc_RaY|m%?4v3SWZN;o_0=wwB z2O3!sTVTe!LgGPEqh{?VqMN4&(9|C~mFgi7ImXW~T>8&A;Mb4kk>|XR1ub5HDTqew zg@)zNAP3&vc#kfC(0zeho6w~;k%z$8VE)WwJ7S|h!28<<}S;lC`YKNu+%1U6g+c4T7$;P70r&Mja$Bi6k0(Ai)X!dZ72GDklf8(TC6BLUhvO zHJMWFUHmLmsKr0@bs=^R3>-mk^R;?gJ0jg7P{e{1uI{hk zA4B@Y9gTu~oakD|D z6f^2&9CJsPIqE>k3$CZ5&L5Z-$OQck%$P*9lN4CL)1Ks=1%k~EX*>RSG(HzC6shG{2tSx`f`yYI z`eT-i7)k;9Ehcs|1uPNS=}-X9Z{E;I!xtZj!A}0CE|Q38P3?q>cuUax<+>$CIA$S9 zUbtH~rE((@G^obKSiON#D>_#Tag3xr0dupryBUPgJm z**77g(t{AHelG{Ir2Cp-NGWa=B@i0k=gEcHQ&NvNTlui6~PE?x!#YGM)jEh)Sts_ z{oxrvjpfViI=Tq^j}?>*fgfHQGr-}xp!4Io2;}Ah-*l^vXrgZh8<1|O>wIm=H@q>EH^2 z*JM;@unSd}*@JW?nBV^#DP^@%zC1lXO?==iSv?+dS2x%s!Cv^v5h@FQr38}Pqhixm zRXX7MhGQhZEZx@bO_Y)G7hu&~tecUMvB&id5(<^7{V`9m3eHlYmx<;`pDH?pF;aS& z#vk)VrD*+8R{yVJzQRTm7W4Jk*{;<<9xHg`+v@i{o00Qa=~xt2%grXu4}f==v$hy+ zm0g#%u9Qs*J9oTyj*_IKt+#Fc24wCgl!4LOLUkJEpqUB<#ZE&m`PG+Ec&xY-PbL;d zL6E*$HGM>BROc!UybB*o6L3s-8RdO=x9(DX-p;MDtBXi6+Q9D!4DK!KpXlto5)1FI z$4Y{*+H5YnRASMtYt(#-1BUlJkU1}>s~bt?rOu-+`LYE`t(zSjy#-0FSp3(jJ3aK{ z=Me9V-WSSc>B1T2=SQ=1_eellWSm0OyGhn4>Z+Ej=CUB2@zx~ccbKRrDh(^!H@(}U zGp>d@N~r^|Y+yOAIH;i8VD)gNJ)_6gkJtyM%}AG6YC zdLJg>w^#0~)^I)KD`=x?3Ol4o8qphgao+o5O$BR+dJ2rY!!XW~xjY0V=cD)&3pv*| z3?Bk6hyD4csAFBOd-bK4hoqWQO>CqzLzAp%E!>4k(DIAl0*0GrlHyqUIcsv8D)jczPDF);Zik167pOgil zO>(|Vq;Dmaky3+m1wJQ>yAp@Hx8XR*ub#ZMdm9b|eDfE2AWMZ;{PiT4NrEeIrV{WV z=k!Anp?Q+QK7S@u+wC2gr~f`oNR3Wa3-0;5P8x1O&zmtWf55Gz!-nnE&UaUbBq2&{ z^e#9YUkz2QGR;Z%L~ls`UMk%BVvpocBS;irCz3xkXp0CM?%l+*pC4jC5IEbZv0`rt z3D4B9tz*;G_={SY`9L`MIBxVP2b3V(^&y3Jq;~^uXF#+kvQem=KZg_#25{-la(#F8 zaEoeXMAvo1iux`2CJA*JC*MsW)-QuMgjq3N)W^zN)CSt9}7oZPC+ydgP z`_F|q@&lRO=rTTSW>Qr8s^+rScW#Bq zf2+EGIo&2#rm%K=cGKHb5vO&o^M{~BxP&1=YDkb%+nhdE8>+N7&%5PXM$u&l#>PFW zF>T0Wd7|5i7&$L;b2CGBk^$H6>{B!acw@V5VQy1W$cU;n9Bl*aBC3H9jyg0Uo;@&EfGsD~ zD*|^)-{kE$5_y|aO)ul?D%zwRNcIpW@OJf?KA0T(@~@(%yYZl0Xb~ z=x&j9`n!;?KQt-o6BJ#%rIw8pa{&(42XGN@Rml$}HMu-<-l>+}pzaD>U^aM>x)>Sv zk^-l!H9=~#jK{-=^Il(fPf>fBp7=+^5xad%HI_?^p(o%malXHYs{wW)V!NzhB^)9F zY)N5Y;lho(HAg`DRpT=BZmbMRJ!-;r`T811sQ&!DVE*jeIPL|`tz=)s$j%do3$SG^ zx$ds_RPQtsEb7oLF$eSP6Yl=BLKpHVU}ql|E9-3Y;0~D$t{=HZdOS#!vaib3N?ufd zfm+HwBjO$no_<3_%&aIOVlK+<^zo3L;=vJ+@Md*9c=Hln;9t5^U&f0LXa{%Yd*wJ? z;|*Z=6YAb&4MyCudN_^+c|5ei{v)Sbuev^PIM~EmKjc$PTbfXJG#hT^T5T9;s|{2r zrhBd|b()bPL%k|pl2>`#h1yk*x-YLDwF?Q|4xWhms1#F@=cw=Lfr}px$7iEpi$=6} zdQx^rOs6ln+H}5(-j~zEvLD&(ybnEOL!Ml7c!Eld(X_H*j)k2@LN4Qc;6XtHdn#$N z>Ebjlq-3H(X$e+!s`T#8Z6(?YV(nfVHx3GI+Z%d)3QY zhg1n3$V2M3UdeHiE=OJ{-!_dx1@^2fJ?s66G)R&pSSR=ivyvhU zhw;QzpM|+pU0wYRK4a@>(tlz@X0~u-wufkBDc$yi>KBW|4BZsivBg(ly5nk*i3eFw ztBa;t$W}dDQx~an1_xcL9BRKR-KLi2GNI=QhDnZ)xaACO12=jN+_5f~ag9a3NuRnQ zh}c*yXTVc7I9gNKjw)K~hzqmZIt`WP!A;4qWy^yaj+i3OgxO+6=?VoFO$ZX*9(d=IY1P<#Ls4EK~FmclX*}aioofZG_0ma%c zyKF6ZV5jO&4<>pUw0G!^c4eAfhjz&#Y+|+a zqv+#h(M`qb0m^6NUt`ZE_lgIAhd5BLH{k7uJ=S zU#>1mHlt&JYgEM>B81@c-q^nl;2u7r4p$~PE|*w}S9_B)y1&a5(;K#s8#ed^-TLL> zqpId7u1@nk8Lv?i;;0ey^gG_OIohk#eNRz$|5P3SyR#JjbKjXjYpVIcLs5B zWOfoBTQEVHe$n+%qt5p{c8)fPao$lA%9OnYN^lIoX{du-{{`#i$ zgbgG8w0nBqLeb?A+dFghbH!8C_neqT^=D1Uvsn)QZG@sp!E_5Pyt0~xk&XFr^J;@Gmjw*9r1E^|nqAQsMBk-9PpJa*TeES5!x zan9rL%#mqC*ccBvf2NQdJgk{(oUYZ{r~`E@Z#I2Mj;i!eS9K`3Jjd4R%C{0bk{MDD zA;`GPjFet)gqiz5Ue}amfx6f86+UJhL(d<`+?HMYcJ9Huaj6It>3S5Ya5y1b6H`RI z!Qp-(T`p-H_|tUkueD|>bLrO)q`Rz(K&a>t0U*caNpje9liqTVZ~u*B7VbX;?$W}Z2+l(Mr$AhyE-L5=@i`XYzXV`oTEIk;Nv{U& z?&xekEob{>rTwlBbK#+|O-76BSPd+?`oOZ#4$Zb_I5ksn%Wlr$o4`lgae+==Uea@E zrWKF_^~O;|u1z6^U=jf~{99RmGbF!fg^=t}sW&;d0$4J^GM0VS!>GtRJHg3r^XWW= z0u1|55xml@=3uF(ji`lK=#e+NvWXlkes-pwuG&?AnPwP>Hlu*zR#!iCJ09y{4HUoa zmESDIpClCj+={(PijxAL3mH>Dw^9ZhDTP$8*&K8F_j-B(_^Fzydi^N!^I&8_&~?yj zR;e?oZ-R2(YKLmmIGAzafWhtocErTO=C#5KA1)uz@k}P8Kh@ zdTXyuer!Xzp_&MjHF#bS#xotAur7c3f|ns?JZelit#md`z)(9&uczXp~j&>5>Q=+7TF>GA;m3oKB973=Cu&K z)Q{Urg7alSzZNneD+K28_@y2`Oc*tU9g{JM&Ki!P7g1=;0H#HmtR3WSa4fkxw^f^| z0#6zL(yQ6GfS`YpGJ7F7pf*-!B2qp_M&4Dzvsd8FoThfX3q3u0xS5IrTZaYj1~7$g zGXkrA??AQ2RkRn5U?}kqBVwCfsNr$$)R)Qv&78knn1C5RT%MBr!g>MR?F+X%Fdo4u z+!zTChC7kU%~rGb;1~o;i`~?4C2-P$OmYaj9M>5!dE9fEXgXJ@^}XvC=GLcf3i9F` zNm=>Hf~^<2zYrWlV5tkD0UMjaE~l4_cGOz5@D_Z7fWK(vOOr>B0W|&fMsInF!6E%h zVOl}UAV|HVlzNyb(;PIDa09MMqRnfmci^%>{DL{B`qA2>h-2EOlK~mTt0|2cM0$?R z;W)$3_MI_@5QPj)m=?1b>->I|rnbbW@@Ujc!XO2Nt^4IhW#Xs~8LFjUvvc((zEOE}TV|C)Dl_|X~r*p8gVK4{* zyh1=6;ovajp1s4%DKRn*9-78=XyRyunBLjm9vul}03WT_W?N@C)sxE^Rh<87XV!5e zcQTGSs$uUaajK2h=h|~4)!8=hOc$y*c~NEADSxnMMu2oVup9=Hfn-_XXEK~rSWC`u z;5?!n-I(+X%F=}ergvrP-kHS?BfeCdajl#0Z~+6z$eJ-1U8*2&rrTYx*iTn=dJ1pf zf@kM9dpHN?RFmQ%9TCELwZj8ub*MSiW)c-6@8Xxf_Gji2%RlgS8Ty{$%RNI+oGg5v z*8`6hn6gr?#LRq#XCo@Qm|~A-fCh(=(|u5I3#7F2@k_SQ6t0z-@I_Gty+SKpg`ohI zI#E^|J#)>zS(`EzW+-em#v#!dFIwqS#<0nd8m;%4uy+7R6#gq9w|Zt01twAZbVP~n z9eT6GBa;nB{)W;K-5%69Pbn5@Su+3(Hg+}Q#`*MXav93%6!p!{IY;Vo z4s%f$G_sR+c@U))?Ho)x$~N`xX=W}cXHmb?zNcAJn>;eBt8$mRLTLgpSPHZs<_i|(t8A9v;ZWZvGSEi0M z3z!im&0gf?EknbvLB&sH`WGP=&4YD(VmZeF*KOiCtiP1&PWHD^KXVr1?#;<9Qq%YM-mW13W^GDGZ@=#&5}#%OaoQzuIi6dVgQxB2w5iiI$NPh2l!@UbV2 zQ_6kOK;#=0x)`lDVJ@w6prnI8vB6IKekrBNEgw64sxUE%^neW#99e`TiZjc;eV%e_ z6ro`u5gqDt;Dw&+aMK&6aU1*0sIl`({=TX!KsD=J4rd9^q6B03n1 zu}C;9Yw1mJ)I3Dowdu=u0UhifYReva?#eBpgKv_LhYny`=vw-c;qH^+Y(Btz+NN9t zSgxZunGHJ6c=wAyG1k}vRnn3NUQcVu9xU>?ANdTEtjgEmrRz>#a6C9t4|*ggVKrl+HobX2Ep6D|vgMfwgkv3FY482i-_sE~VOroijkOyx+GJgD>w z8w0HPeY1$)OaNRySmg9x6u7firpylxxY#*bXuVC@KOESGW+&;32%xP=rgnhrsv#@D z%zin52;96g1(-4_IeI*RxEiX*PMtZVgG$-C9GK|Og;xK1+S=qm1lCE!BELF_bY!rf zYqcBIet2Y$H0S`LIThRC^YwNUl8ZZeDh}E`V*0{78>t!(1dSVqovE}g4p&G4o3r!@C57!}w$| zz~~NRYu*svEeIUeuL!{e!tJK}D~}C0ebfX4!(R4=U_01zF7!wGA|@X zV(d2Z4noq-7#m3nI5r%NCgmuxV9wFlGvfoYT*RzJ6Uwk!wBR2U48NVOBa<3L76T|E ze@u|W236tn`X0v~q_9uoeSwT<(G6N$<(Noz0G0^9BvW`nI#tn5A-h+j`F}`&9T87Z zEb8rK*kIiT$&44Fr%^ zQ49(GJ3IB?mH@~tDrHB~1JB}-QtT-#A73t(q1nnIybO>W%O?cOdSvJl?^rvUb*->2 zmgKij3LQ46O9iIL9b+f`DFM1nT`WMGwYSA~M2cT>Y@Zg8=OYG{$7UQb7KAsy?cp=h z{9;6Kb8{1c5H;uYJlp=W(wKEyltNo0FuWHU?Pnz08N1&GqGKK zJ_I7+l>CAZqFgz|X^OUwFABsOwR&t9KUi~wyq)&*msTnK=fvgevR++xcKoZboa#3$M9qx z{2}Bu@_q&n$DY9roxo8zkWF#KdLWFU;m8(I2rKlAg_uyI;#*rdT^v`=d`Hy}!v->W zgzxXx>*Zga{84N+zGT+eb60Q(eMMU7*7X}U8;K||td^PO&=ew=9n+N~5nLCZIm4r; zT9)bqgl^6hr5+V z!GXrjM=H&uv~o3E1Rag{;vvfE%CV^B1yM&V^frP%iA z6|xZtcqP({Ok<|wAZ9K%N(ybe%tL#YrT&+&)RRl=d4Faa_Vy$BdUmirL0!*No^l>K z>RKG;(hL_`SF_Q|5d`U(%8&D8t|@97o6u#BmMAPtNTugX9gWClYbNnrQJ2^rN0I0_ zU2RmlCCvXPgvltPHX0`igtIWZo=HX_voQU&!CFGS`kwTX(LybReci!!@_p~X*v>#= z)582fU^1$x?FN_zwto_!O(<(a5_yx+#m$85&_*E%k&ZLd#B0%w+97y3ERG)vj_X$1T+RUK znB+zL%r!7j**Dm!R-*y&tA^gCRDL{><)hD_UBl`UE0nZ1kyO&Y}GKlMo+K#qms181c&$yr?0LMDYM%vkgX}iF&>Ei+#pMDJn z^@6U@TKLEAntG%?<)J6hhNp(5Lka)RFWL;>@mNvM!?O^JEgN#P~MiDfIXnnIhw7+-`#F!Dd>+Omx+6_f$DSwaX zGLJ~Xf(6hPA>CM>+B2THoc&mpivYz{VxeRP?hYYOhEVds zyxEA<5Oc<3h*Bb>Y#P8|c*{x`J2ffZCDy5n75bcW#~zPKskv@rc}-EViR+Jxe0tt8 z>0W0ulbYsv1`PpWu8smR6jF20iyh?fy*iA{cHbBSuoRB-GG`AavIVAs0=5WxjQM5G zP9*iuvs8r>$ZDXP&wA5og*h6iP*1(mGrlt=vO4h=c=%jPskavK_1vKx@sZI8B~mEo z)y=47RKp{+!4eh3?!7`^^j2Eaifta@TAtj>yS|vXZIqYQcPS(@8hfej5q9NR?oF7mbrq>AGYhPz#k7(1=btL8i;qjU{K3mk%v1Hyfj8#vajQJg0Ud0)aRGCaRMN_1lmmktC(Im!sL~g<|21 ztmM+|=wHK=Fsw+sJ6+SkC0=m-j3$zMrKAE*U|SRYKtn|FqFk+tnnJmVH=T!zGR;6E z)fp6nle54^2;6&vh|u?{8`A(Im7`TrT*1~Ti7bT!7v#z2hP%GPda~H2Iw#)B;({&0 zTC&YL59y?FI7}NHRu&EQr83knp@?4!Jq1o_f{`y{%O*`OCuM2r``d`pmv^$~YuV|? zj9fzt)d`sSf~_L90F{|knKXnndx%thtRNbO;pGxim2}X`+bsA76H3sJvGf z3V7Fw?0r59g?xkx%fva*DSOfu09dQyhQVVn)WPOT0X6H}6C0qFWf)F7u;T;97O1kX zZsB$t=V6IT-}W|Ya6X_gtbg*$P^fYZM|ztPgNeX!wxZ+4O7e`rPcf@z)!cK1x@0pX zH!7s26P^}NHiR4g_S=zGpQfPZSMFc$s6q_6s<`Y*S;HE5HA}M`_OWbwvuqJ^N~A_N zB%@1)JKeia+CNaT?n}!oIo+^$t(nq+bMr8$XRYsXD#Rql0=7a{3=ypnMMeJtoV7z{ zPayfqI)mNd^X?Ok5ZZcyd3McY$HJajj*vnT!co!q9D)TUI8rnx#>Jd?)S%r!rHK-y zvy@w`9TIhQo_MpnhA9k21ioF&m&@WH7)Ba(qb~A}z57P`%0uM=+v*iLt?;76$k5>M z{t?^iHfeQP$wuOkaGtL&^p3Sr0kx{t^l9}}ltYXjh*xXyEBvnJEr+T)Z=eo!bfP{x z3-_oV@YD8H=j%r~8H3}J)|ztAM?5>^D^+O0wq=6Al&BFoe>G;Er>2`A}`V+Mw3$tx=nRqFS>t$%C z*u6)EWKrxGIaQ+)>}=GdhaN1Arv>1>CGo#TP1_V^WaA-vV*6l%>R#+zykt$hSx9YXg^5q3$WaE1JcE800~yFqbgNr;NGDNT?oQPB>B zGY|P_MT183{-Yd@MD1O}lftS|pLV4p&8Q_y^^%e}!1LPLSR|_?t ztyW9I2Q7z z>aivQtP*8^aMmN{*fyd22|PV0<_x+MVm#MQUt`s=rA~GoQJ)bQFw;qrMoy7iQKNEV z+6ZJOq_8kkH*`unTEd$?QJprFJKGgiaNI?3{R zD`o%XbY5vO^3vRiHnopolEJ81nRaP-hTw&a33Q3@ta}pJ864)bN)CGE2 zyy<{UX25xcf_pBQz9!5!V-r&_vq<4~JY|rv#`Ht^}|J7i#nmFxPo#RM0v7t)*i-_9%HNw0*K?vh^G1sX> zRS@XExeE^hu<{elQ4bV#$&j_?TC-)jOVe^UMjH+SsvnCMWS*)cx_bqq)Q_bXah@^= zeK2OBUSH4@A-3f@n>U?bidHk(VoebqbK zh+E^el%*+PSFQ(d&_L%wUJSudRF-o}P{`85LFFl_c(uo>fuuG{zk~HQlGwy{WIRxP1>=Q!uMx_K^Ti+b$k588ZZzV<(}-lBRHkJ> z#y05`hWX*^1uP<+m4`=()7pB&6fh-@1ajoi{0bZiR5;1Cs-!2Qo+&P5R6mANq~h|? zzVV<7FvpjLO2C5AMn{LK`&tp_oA+<|^wge$A~~9@cT4s>=lCH*ahhw-Q^95e7ezHx zIC&$j7RKjBrB5de{Us1Tmp9`MUb#s{&Gb*?QNqasg-LJ8c}k{FEJv)RFcz*jbj7m( z=_4lwpquJdn9r<&X)3Blx!#_b(z!siYV%Z8Dt9dOM0Ar%fiQ0^$YVSj8Ls>KBS)oE zT~Tadykxp^sQ_C`!^HCW8=Fmj2PZyB%2m_}$NN^;9;soY?59oZH#D}GC?=xOag7;UCa#CqGpD1O92$~Em$EHrnAW*r zO&#WIW+TTi#K>5uf^E6fK~E+=BlN-=>q0h{NerQ(7S1W&(9X#!4d$yl>>g|8zPPa2 zL=7^_;{BWWH1W$pv$h8hZtjk_ohnzT6CcXROfxx>F<1e%CF@m9i*D0YjQDXUY*ytn zZGl!?7kKQE%H9&mYWmSBGtl00WIKrrsXBd*98QTTYI~6Bz#3crRE#ZO%^X#xC05>4 zqejvr;lwLrb5mU2hdyMkPX=(fNuS54$hi)DPwP0QCGor6@~C-~_S)d8Zj)V1YGQJ7 ztg6{LeMjc>%#s)fbvF>6@eb)GO=Qj0;Hg+$oIw(hlA%tpn@tdyc4ovlHU2^Q~qUZt*OYQd4pzJX z$sY7G=U?#kzzzi4MUBdaB?w^{4z%~WdRz+AaFapfoN8YpOLRu6JvtOyQ7d9soe_BZ ziW`eYXW@92=mGUY$aoutH&X&mrZafWdsVfNnt6zWAO z#!0&9GLuP|{}PxelQ#(ims+y@X=(3r7@cCVL$MZaHo`&n*xU#r2m?;`9x~C+e&I*BozM6zO zyBNya`IAuR2$XAO;}=ysEBzL+3nqR`wlJ%tpSL=1%(S6W*K5SNrY!#P78#W_w&Spq z<8~McUC`SM8e|kSV#jx`jPKbicc%7yOg)eL8)zpt(!+~4pce?@JR(61M%iOkcLuB? zc+5@QUXSeej0`fu0#g*@vpw_OCy{L%g}r1iy0M84H|dv=CSSH2TM!^MGPW~g8;Q|d zh;1!lB0Vz~1$M5O@!LRT=(kE$q~m)aITgBc3;T0@i`XKM(5K9*lCPIO=!WS|FO(g;iQWcGbZU793x*0@ z^~hP|s*qA}iO|UUD9oJ)pI>C9j>5gesc|G|uT{f9;upC021ZD&3&bO#FqYDg3(%_? zE!yog8JAJWMNy$7qGFh`1>~Tr-MMQrFd|2^iGD5G7akn;Iqf1G>6JCSp~t~TxbS4^ z7q8C+HjFHDH>mu-Bl`-JOF2iWQ7w7p>e~h(c6D^BQWSXJZZ~A|~`lYLqc{CI&>| zRP{UMAt2|N(wB}jhW)HfyXRCO`VNfTgqq{lN73lsmNOgMB<)?Jbn@|m-2j2Rr-CX7q7(Y-x~qoOuc&)&UX z_YCM*3lScL8^4gt_YCa9Tkdc~38FkMw@tE~lTg$Vq6SM3F%^5Ab zM&e)gLh54wNie0Gy0%GCfX;ytG>ZwNy!AFMkfc?>I1&5x+5JvwRLQ{^ghYEJ6>5)nycA&My8|Y?{b$Ll6V)zK`K=}Y z#J1LHYEhMF8b^eCv^GI{g>6123>Vc3O*UsYq7LMKqkdmDsuq}RJBs&~pno;+VuMAm zAC*0CrNnncC?Xh6I4PHQi-|7C1wf8c6l^Di&TBVXoz?d_vmCjtXw8Ijjag0-a?8j6t0Hk>^&UA0KtwZn&1Eq`qN z4N7FI&lMDbb>wWdx|X{hxL326(T-kDvdrkQ%lQ&ftLZqnsD=Isw==a;++V;r#&6)&tEMB^3*2@T(+EJ0mO+L zjgA0VgV0eh(HBj!9k?(@6@+su=jz;<3@GvvtA;0CZRpIhZ5%sbv}Vc1q;o9pXnp$F z5ecE1%QrK@lPo5-%uopyflAzu8(7Q+xxoF^SJ!k|0%zZncT)havn`wm9KwwFr9!(} zRvuelov5z3G@niORS&~2G$luY=vbK%?W4E23>V6dL?ORCN!Lpmv8{m$KrcUkTQcNtwmZw6xlkq*P}3#401N)$Smjdl@wp6)G}y z-z5$IV@3Kn)D5kh!`v4#sGu@L$M)RzUH!x5k-kv`?2E0^wwDurq%t?h$v4DE?b_U- zxdn${T&T5rARp37j~|oh7Si4yCFLW9Th0avn2$c`!SOX)Y~lG~a*`Ap!I9ESqmA55AbDC)aU+^{ zteANEv^0q9v|Y=pt&!0Xpl=4zDMGdnc1p2k9C z@zoVmhwYO`Q?BpZ*-w4BCX<#RnkQpK6L+LVTaLIk+EV%j4n&wGWj3)1=3+O;yf%LZtIgMgG%C z6cOHau!>|oblRD^PpR;OkeE;l*7gYEtqte8i?VqK$I;5dG@hr72wMZ-l z>>;F|4_u4tBFVMnW--2S3Y=KJrNjqCG%x}uTC)Y@{qNYjr;MZLKwgK~o0c~g{x5s) z0wz~ctqoUCl9`@sCKqtH2mvP$AV7eK0ReLzl9(HlN+(=|OfsF!khwYClM5h5ohAVy zM&uGM!4?n#1Psz@P}JRfP@^IOay)#BM?CJ`jf%=qR8)@n-?ysv?%lUZ0(ze3`~K6< z(^Y#{)vl_ws%q7$RjXFL*E^7}m1aE!2488G*{js~i^f9AplXrdEj`spqdyGKm{_GX zDs~qiKjzN9|G*t}g^7K81Gxj>WjaT|evV%hL^Wrfxv=fZ-u_6mKjxoe7`3!38tuzn zTjHmuS+N;C?Tb3qQUwRCeM>uj61jM($qPISE<9!*;7n0i@j$wknFF0&?LM>*hXj7x znsEO`;YZ+E7kJBBas;a&I4w?PtX#vc6$|fMA3viLa-zb#W~}mU`uevA_)wrA*^6@} zI1Ir&%A(Z2^~L{kCNQV_y>BJlk1NqwU@J_;Bq#-n1?Pr2DtFsT?qgo+f&@OE9@luf zHfLVZPdfuvf-V~9#aS(UFbn{zu;*rq_rI0866S)sc`^Sone{nqLo;CWCl>3(F+0CD zDLp4n6=N1)m4YMcE&aXSM(FAr4T3?Pw$-br&zm>wX^5WY+bf= zu3vzSUS$-- z&)8h)w!fm5BwbU~<)V@6CbhygQcTgv(Y+}K6P@N1gs5CN&^5c0+Lah*+!cq1om5;y zPT4vMWVC%=4^#?_epCztrO7wn;#nQi>yPC^6|eVUmSajQ$dYx-ZPJvVWU4{jX0B5g zo>UVA&MV8sXw;ogrsfEk7t@tB3Yel!8!R^sIu_~8i85^_a$Y-zctMpeiT3wu+j4ps zVyxC_-d-aSwxG<;NgU|tQIo4^$ermUtA-ovqCM?KhhVv=qRpY1Fn{3RA?Kl##uHl? zFPX}sD3%wMD1<4ixxFnryK&|=?1^YZqb_-AZ&zo#E;kI-4%m}Lr>O;O*tb`bKvCBk zZ3mWgDWZm8>rH5Jm~Lye?Gr`)V?5rrT+}0p&Wi70zk5Gkr;?~K@Ltn``E z00xmP^au*jx3n8~OQ>6xWUqhO(nkM)kukxLOI6&>P=K!Zf?h3L_f8Y*!gZX&vN4jy z=hn47^LpALI0f=+KuK3Ywt_GaE{U={2=0DQuIUSV69X2>C%-K?C{yGENBBc~+BY4CYU zQ(01t#V}IpbOOyIi3TJ=TAY&AJ*X1zGiEJO2vSl@QA4bmZr5Ni?Y)ONhuNo@v*(U@?$2HJ5l&;-;WZ;o@tr*mut-q^W~^(IxK|EwB7?|O%UXZ=*K=pPTi zDvZ@?kd$JisMn*55$rka5lY`v!`Ha1r{A-8)zub@Ef;{v*uT>6vkK9#^V1DBM;Sh* zKp=i5cHi*xdNsJZg>x1yyW$E&u2;b6FIWfvmO34z?9=PVqOs8l=+Rkptf)5{3g%U8 zyDn5oL*eM@Y!>t*MN5f1@3~%I6RePqqA@mN)JVq{4fsN5w77pQhE3&yCGp9^mnVBZE%f&biJK0BXSS}Z%kbpxCp9N?Q8&|(_s2zqkHuW53Q$cm$v(8-Z(OktM z#W{f^AM^tn(l)gHLxtEcnUNyt+A2{w=BP*vr}pK;dLluwW665ZG~o2Pp21tu&@K0WulI65{Iep3vcJ~ z)19ExjS&qh-LzRC*qF5aTa33D)8~izOpvDk)0boW!_Q@s&ZQ{P%uTsVtG1k^6ZutE zZ4;>S$vXZhAVXbM=p8Verv^ChHS)_lMJMI@N+tCU_{9_-=_i|9P~zOyIZKxrgc-Y zml%C-ZX36<^pRnjlq*O9rA!4ZJ7i!z;$<*n4d!OBXuwGDUMJsLvzLxG(%T>oSN(c@ zaYb;Wo!3cqNxAF(3|w3rpF5W#~qJGo7oO$>)rv` z*6-hKzGaSH>a>pV$A_xUf~Zm!EmwC%9(f4>OwuWVwb^@JpIZY?hn@$gQPyl@(Fp4+ zg?LLq|fjausrz>%p~LLB{SKUBLGG7mg+p7Y~|3Xe*vTYn4dDO;B>bKC{Yd(SnQQO-j(nw_o?An{`Sqf-(}%qgGskBXmNESbsH#Ha z0Pw}u=Pa)m=YK`+Rv0Ao34U?Hk~6@tUzDWDfomR%%P8W~{J!k9T|jFq7gq$lkKule z;j04IdmDByuq#pA)xCCSN5`$FJ~`t4B_s{H=lNsVr#?B-(+AS+b4qF~%_asU- zebG3pRZe7{YX;4tE$%7cVp@FUpuyMp{k#7VXPVr;23nwV;L?J+?bQYpn*!=Iu zKE`^i2>DA+30aui*k?zKk>-m9qG;oYHy$2lAOP{C+Ba>4R!RtL6kAmXZCA2Z@&B+)X3XnWK8*y0{? zU91O=jzC9F>t&8xAH%HbeGBuzxEbi!^$;g11O808xMvv_6tn^CeS^wDrXKI4!dD~8 zR(b{tqir#$@3M-rckNnQt->Q@y;%80+ZzIaBBst@MG}t{LhP&+j*wQ!nE*#5PY@OY ziNMOSMF@c)jKkd1JfB`TLZSy%u|L3Vh_D#lbD0bR&L%1Gzk>eDNQ1;|ye z8ZRSquCic>12+&ep$GN$s1E_Z`qgxFbS~<}`NIBaEgoT$p|@+GyQf+`&s*KwQ?En0 zH@K>IW0_iya^53RX!0as0c)z(Kx}c*#g(pfj#gie4OvyD(seLBm0wwJ6jXI|s^J5F zX0)q!LpAHZfprhF>b&)=!F$lhddB6y&e;f=aibTe#t?w=zk$dOlHYnSenH!+Xjh&8 z7G0BzY6Z_8SKlq7ws@Du6zZV558qHD*}DMWppsCrVEWM=oy(vl)xQ*`^0|kC_XWKh za_?o3Tjd-jft=)V!U`MY)(25k+|R?#c=g3%VO1NXj+P2qWnqA(gj6R`4!S`YmTCte z_3R!Ea~x{@VFVLpYEo&ctp{*N> zAqXBcyHM}FDt=>hTQ}&uA~G$V{joR_d+!UdEGbi+8d8HAkz6XQT2q~BoB{7uvfjMj zUfqsrs18%r-mSV)xE;5Qu;K_%eX2|uwN8^n?#cPWsxxIYsB`ANm048~#-sXGw)c`E z!{a;&Zvn0jDU)>_$8DWRzDHQ~I_t{O`B@han|kMP_07e?YB1HQLkr|LuQ$fSo$GWj zt?69n5v3|T;;cfnx^vy)_1N8P+f=4@4eWglB6+#6Mw^mqIkY=rh{0=Rub}FVZqsop zJGz;oQG3uqFyTlcArg*v_LV7av~#3HLCIcWVbm)k)`a5BN5EoqCUzlF_f1{8hj|#U z>F8Qz3K@$>Y{RaOF2xMe73D``ydXuP8B(VFM(0F{k~!GMn8v7=>IjY|D8)@N#u*qRG^f7}M&Om{xxBtn6>U=+I@QrO4_nc;MG(}KsnS^ISc$BYgTfl; z7Z8zt$Mc7?n(xs48hh8#!edJ(H;zEeW8-?)fk|1|wo$(;y|J3!b-eSDUEe%%jt%#& z!}Nx5{Q$0^Or^%LQsYE&qOisrJdp(CRjL?Q<2j1()#rqDm;t2*Um4r`3QHhy>Q~*n zCbkf#={kEx<4JJ}FcKV(_x9A{T@kaHy&LQCtVwP+b{)caQ$v6cih5V!f!it#@D&YS zyoRr1@ru2%#gGT_%+3sSsWNaLMwa0)k(?x~!*z}8S>wfP*2JPPeT}Mu!m6Lw#ycAo zi*mTr6xlM7LmOWoFRXXzugCd}9?`89()=2|*zhRta^5Aalt{i?SWWtq@pVfAykUOo zCTFa)jupv8!a72yZm5O&-Rh3$92_3o7!I0{oo=P(7m9PBkmad(MV0nIWVO-#L8BtIzND#Fr0is`5J zV97^SmDvUkU~42BE+0+rc8&#xoHJF!O%~QMrhvwCgRI+W=fZM(;HUE3S*d!Ct!ngRb+?(qzS9!fGRe72S`|)DWXnm;M(`j%c{;DM z-~SDcJBH3AIv3DcDy?JO4mtF)yD74z+5MOt`l6MLBI+N=B$$2kLa)LQGd`<^ z4NMKXZA;-qUooQ_lUY^(Pd!R|L5EFHVKY11JINvHFtiv(rJ^!apVNWIGq53_sq;{M zmO^#2b3|mzWak7>R_AOYUEzC0Wbg&2ixHb6gReTbLQ8zd(7o^4)C_)U64c>U*L4ZgheasFglF?bRQsmubQxg`8WkZ-8JtN`#t0a< zQ`zslxPpSNiB27zMrobEHT^_#Q4`7hOyrt(qBALy_>nU?vSkYCf>5+1VBu{EM?a zGWaG&U}BrIBQp4R=k7@2>rOI~_=a6(gyrmwB+|}P zk%aA7k;IVmbR==VvoDhPyz@*X@u2fuB=NNKVkGe`=cP#E+s@08#3Rltk;Fd7i6p-3 zyc$V-&v`A9_`dUcB=MN@MkMhA=gmmsS?8@t;)kw?BvS6kNaAKU97#Oq)NMf&hbR_YlJ0X(zs@oh%JmpS`BwlbQM-sQXQzD7m+^Lboo$fT57VdeG z#9i+6NaAjHb|kUWof}Eq<1UCKlJ1g7;&ZNyB<^)rL=yM8mqii}xGN)x?QVM{@vz$w zN$hsJB8f-czDVK=Zak9M<8F*39(S*aB);fwjwGIN2P28Exm#sA3ZgjUt5}$N$ zk?E1{j!5Dr_wGpIQ*KhGN4SrHs=ME$^CG4~_jhP+_s?)LmD2i0rbb%(GbbWl=3EfN z%uIBU44QUgW);X{=EIooGoXzxWNt#vnR~zyWFBCWROGs^isW=*fp;QRtSU1&JPHa; zlb{5aom6#q!_}W)x?&`8y-02o)`>dT5%rKZ;h-$U>}@e4aP*jo@aq0~YkRPHU%$E^ zI^_f116{nNq;7S;V%H$#Z?s2@fpIE&Zrkb5lgvs>WQo&7B3eF zYm%a%JAkJjofOX67zo zon&Bb0RN;_U=_E_DMolJ@v4ECQpc<_{!&~W*MUIghX-Q%yHNvNjMgYu#pG20_%>|8 zNnA~yB$EBY0+Y+;8&PrYnRx})cl1Sbm+;i$P07L9`qwq!S+jW;c5UkGsKYDQ|LUa* zFQlq9@Q0TMolIuqWVIYVMOUG6yx|^|qN!QQ7q9$JABNGCRYSd zsbn)=3HVysdZxiX>h|^khgwq>pChr+n$-;x#(U>g8n&e{WsQ5lJKGhifLLtmLp^Aq zKbqIEm>mZqxl?2(I|jN}d98tpdb?6b$IbZEn_Y@LVW~>sv3q4AQ7|84*ehGz5k<|_ zdeApugrX22J8)(shW_MLPBj>pxt+ZOv93+JQ%2jVgC1>1BY5e0O=-X`Ec*qpYu*fY zfl?EI?aR&>uBJr0Dw~l@+In!EH+!#o^|8nR>;m8_Er`C@Dku~cMNjPlY3^4QE<=ST zlLSmwdvB*265#K>k;Brmg)Jn5AYD}+@j8`)G<^0UVPP?@h*u*hCt=E$TC4d5AJ?>Z zQti15r!^X}l0fg#Ymvo0U7M=gvGJomD^tnSD4sk`k^R$6Xm75T! zI1?p+NS-1rh@;pJmG3GIN=&h;XpRc64bPxB5Z|^6>r8_N%FtM=Vu5^p7sw|!l~h!? zD>l0qn_lfzT`_fzrwpdgpb|>ht81hW4~+;{^2AuQzfsbRa)8$qwQaeo=K~^? zl%hx~ZhD0faXficWP}=vrVtYt%-IBaa*v%SDY3)*oK=)KtN{qll@vSlNb7yhH4M9f zVLK59UPf5c3~bzJ*Q5I#Ll-u}vOJ=y^Y$U+!?lM5rAC9!1~3~qr#W4OsgE#Wt+(9S zD6MJEbqu?OVGklqBySMbIe|7Lkq<&y@wVE~gXKd3F!Y*u*v8s0mAeSS^O)Jy!+6e#CH3Ox8_E$^I8p(mDc z&CcpbVx4o99QwJlg|DBKfGK&Yu->mBqrQ;d?ImPtFF?@?EnR^0p%W9NB0-&{QHjb!X@B9RB+$Sa`ok#)NgADXZe#sdpL zjYgPW^DLS*%%f=6uc`_;7)+E-;nn@=UXu zxhcvct+lJvaX4ktsb*D7_IIksG9>K+F261i$xdN?AkYdrbDmZ8Cjze(YS(mgu&4>Y zeoeRGxH$cX*K{xK;N^Y@4j2|GQ}PGcCLa*VPlT-Lx@6U}72Cn~e~SH!KW^Q4KM38G zEehRFIUiKi1sIjQF3tsrPFF@;ANG`obSethD#Ej(+Ju@8Yi3w2k5@y!OKF-8#%u|} zFAFfTGrS9s&0=XVXpE?6jG!1y#2AJ18-qq zmD#n`L3VLihPp~!eLt%g^B<0u#Cfxs`r*@^7pY-89mDpzi<~p0b%ryK zQ^dvG30M~ye3Y9vFF2o}gUy?X&NnHZev!`WkXA!JHTb+cMOufuvk>cEB8PqN)d19M@%@r0fqqIS2c%;~f$|KOdexaW1^LBkK} zfeKXlnjk+{^KN#`#7-@ZpW(EU7%eAMm(sZcawccBv}OQsL_q8`(fJ6SPa;AjJA^gU zj1)puv~bO%Dx*57U|89S)iCrmI)nO-|9j_GBX=nMpmgnQA+HmMNHA zhh8SnR;Q#r1vrleRL9z(E3yGnfSOo4xph948Hcl+p|p;4y1*`zqL)tYIJDUWL= ze&LUgyb|kr+Nj2>IoWFc0&cjWr;S1shi<>_lN8^LT^M>F^~Z{(tc^K%-$qWy|6XJ<)z*e}rg7 z!~$`O*b09>W;Jf!%@;?A$zqJmN$r%K-k262Rw_Y8RFkx#-4(@?Rfx(Nri==q6avHu z$dt=`qPjsygqD(5o32`;QY$WcALyk8nh>KSw@c(HW~lEmtElhA&X0t+oimR+IrF)b zGmkqt^Ej{1<0j6D#hW-=-sf%NY?Z(M6X$Sj;v{OZi8HvLn>d1- zI3aH0RDiKhybGH+i4oYuNsQzs&M0i+B$}{^lQ;sKIEhMZ;v}lT@uzFBiIb?|CeD%E z#Hr&ZPCYkq8n}sb6gP1ixrsA|n>b^gr)0X8n>gdRiE}hJao){MoMX6&b1XM;CU6ty zIBwz`&rO{7a1*DQn>Z7(i8J`$oDl!PO`I?{aYl0!XFNA?4s%D!bTu^`Qtk=Bkc+MM zc`o*U=ed#*oj{_yfw4EB&h9OkaooEZhWU1~yBD=`f56wDGUC?=6UkM=ny*J&RdE;c zb~}nRm_nig3@AE)YE3+$rz%Y+({VQ=X!HL4qg1yNJGFYid}?sbbB>A({@OW<&3!&< zs?-BMsB18qlLp8a={WU^kcsJGd@{d0au&+lg!rIQ4*=7!z)%oQ3rfZbQQiSp*2T-` zP!$LrRFT_D@(mrRl81m&>uIxiRygr8LVB~tT;N0izjFzBq|3=AU}?R8OU{&YEyHd= zn9^QYXkek{xLT{^12nSyGP49gVaocc;0kp(ZX&4X;lU*0DYo=L9jT~B{%SM`s$T?o zNBn}IYkp~=^I?KHKrlB^qX)cEv-2@&Ep$G`2#OJ$hZymsLc#`NEiy=87tum%-JHWL zvWuZ-ZPf1~xE?sA7S{$GVOG_PoLQ{ag{;;GrM1Ypn6GQ`Dw4Z|wb;~+^1=ei`rGJp zqEhb;=I<4nifb<(sQxe=PbD_F<@()XUxw2}8P186;VkxLI8DxNYG3f9!oqgFnuWyh;}2UsWWmPwFdWVzdTU$Qi#tfL>bgC;UXgCEaW2MB55sguHb7oUPW@B zu+WgIj%1@%9l25FkM%ef8M-YzIa8T&agozY=pe3XayAg+t2r({ zDy@qs=twztF#@uu-OiWPxVTqXt%Z%Gb_(BCx6_p?-EORYPXN*b_BszzkJ`0Ti7(}> zBm|g@cRL-@YIW8#Y>;74mq3ed64p`!5Gl8J-s*sTZkVBt{0?QPFLmayPHN@4P+Ci! zOZke46+``$kOdZ0^*7RDwNA;W#g*l0an<6kcD1C*Q(CS9IW7*ZM%q%XQH|6k!Vla@ z*?j!j_FiszS;Cr0R&<55mU9at<-}D*4+~jfR8>Vu zG{8VAA(cksFe9=A(HO}yBY6jg%FGHz*6p!{p&BDsgYJ2)Cx6B>fGqt>A$Yif=(Q8S zR+Tb|{rOSPAil_5y#^|QJt|H&k;Gs%7=5y`nB3mr>gn9P8W;f)%{RpqJP{{W7ws}=4S;23eE)O&A!KxyQg z&>zG1+MucgF?{E3pf(kA)nlV*A?d|ViWuKZj05)#SS$kLPZQ(&*eF;mu5+G~*2T_$ zFx_iR_a@R&NV?k{i*)XBNavn~bRzjXVO?UHhkTINJlwO?4K(Us(m)z8mQopuV-Iv~ zNo?;7U|H*rmRvcgZ_D@eTvJu4N9OOddQ)Y7Xb>X<0YsFVR=j*om-I3u zyn6HXQc9Hdvcp+Nut29Tbw0u{fZwEsxLS`~7U&E-y6tJ`=*}<|Jxc_ zMZQUcGz_0#qmR)P^H?Qdt!J*VgP_*LF<%3e-!euA2SC@4cLoqpY5@?afoa+xO7hEO zW9udIhsYMEoUO#bHagp-HIkdpA9C(x#2$v(be=_oNLGa`OsNV#R0>dfVj4MzHUpMg z1dRER^dZAs4TwKI?OjfVgp_j=F?2IW{%zP^a_%IBy(co5Ardbq5>w7MnB;p*@)DE$ zf=T{>BqI4kVO?QxO1_h(q?KsgBdf!7yW-wenvtjtWa{r&72q+V1T?;P^X8 zL`(`ZKWH z6%p!uRa<}r(R`{>`B;yAU2MXdX{|_M2N7{76LwcoHU!@X2=W!5|0v+q4L73Fv%*@r zqs^Jgdf}+gZ=CtkY9l8MWgNVUSL#kz9^=F5K^@|{ zV|D(zV|8%dalf$IO+}AXvm3Lm!uRE5ye!UAiI@N@Pmji37 zdwLWHa|WB81=5If_9N^iR^Xt6-()0nkV z9m-Uby9llDiE!v*|Ir5|@NgZTy|yUD*5)1lG)0;8-ic8heI&%1nO%d8DS+6=gPqVg z%VJ{+H2E4jAWLhUn;3gHV?mg+&Q};=!4b)ag@sY6+Kd8d-30!1tOW!*fRnckey|&s znvQ~}T!B#!Y6YxjZr1W-Xft{wAX!aF*3v@c>bi z1k=})?Icf2y=*Tn)!1HZ9*89P@R-io0ZlYiQnHeuz#XK(&8pFqjsP@CRAU^6nkwTh z!yAlDiwh$-WR%(BC}?vlYAIN{8=i_G;^Ei zO6L=dfbfhw93mb>gh*P#!bv5CZT5>GqP;T{Fu4Y0pt7Uj9RmI_KxIG^?*#C5RFGRoJgjn zhXktG`8dP2E28>{upsGHP*L$G&`8mCTwi%;fHl_fM1}{MSuLt6dd`8Q>ONqKCCJMz z2U#!q#%8CDP=HiCPw9Fux5}EG1jC?Q2!jLf4Ox&%D9CbLjM9(NK>6~@T;B`VY528j zXcf%3@Sg5rRL&bit)n0lUmX{t=0Yys?{`mFM!}sz0gVA%Z2^m0IB7T$}OLG$8O&%FAT!@H49_EAFI3<)u`IKgWycOIk0 z<`+3SzD(zEOYQ#k}{c)3; ztDP~jFvM@eAf+#upA>B`%8QWb#YD{KHYbw4$9(z@=Ghe%lqppGbBoQq`s?bK;F-M1 zC4U)LRF7hq4$M#87HGc$wn8DqAL!*kFPiQ&-yNr2kY-vc*iB0X6QSgW0S%c?oIz(yPmLKynC68HkD`-HX0 zKsDBDrl5+9C!8sQsHBueR2j>_{&cUZW}H0UZ8-UiO7CTrBe|QX449&I{k3Siv<VPRnQ_I3y0pRuV#PN%P0zjveXB?8uEFBZ6l0a z{EFmt!n(#lMy5)Uyt05DGIc(XJR~;XmFA&*Hfygz!Km^peI%IvsRFxBK*;@L$ zRELg!91f<*2(>|FRvw4ib=iF%4m4b;ov9qyOzm}F#XJToP$9~9XuUJ_5w;{|T{h<} zY+~34dY+;^uJF;Tp)d6ju-izYlmeb*lAkj6w{-rjy7qcuU2CvPnIEL%X8AJEAM-Y4 zO0;{ZQr}uQ%3KXfK=6ZZt>0w}renoOn6Ee{HAi(VEhsfZ0C3&coVn7v)`{@79j_vp z7S=~i)sFQ?9V(b_Wwl^FIY6U_#=!=IVhscRrz8mmR(w!~9eoP0QrQwp&F+8H7s>9% z;u$T2ouZFA*AY~-+ionA$Rk1gy4k>cj5ikFDY#+JqE<-}!9h=(3PwM#9AKL@`vgh@ zHB8C}FmrC^vZmRaIVo3$aP4D(hQx~4v(K&P7u^(P+2h<0EJ;QWJf#4uep41O!Tu0K zlHr6rn(O_Tb0t9oxA?r%Png%UF~KN&jNmrwo#D?iLJQaK5Z1?s16&WavOPEl`0y|e zvz6wp=IzMFB_!-mvis}A>bo!wB;YjDgx-~d}(IM-4(-7pw za}7~yJ?8Vd|IFq%*eORL)Hq*;@8P`^yL(`~m!G9HqkFxxgV?*9*h><7_Y=#zh~V8s z@E&PhPeF3Zd4@@#eYM-kFv%a8s5*kEq5(5lIc!^Nj4dB%UYS%INL<2DvjT;i7GAF@nExj+ zK`;-bGi<<(v_+F5@j9O8w{Fx^CAK$@7s;E1warvpoj=#rhJj{%LD_+8J#V(3xHj;t zs!OL0|6!{f_$XK9Z9ET=!uDX|Qr3GLWo9YtG$k%oQ{yL1l{qyMZRVJ$uB|UDF6xT^ zybgFsQ=?|p%d#$?3{H*gP*<_LVQTzjaB5_Po*K^!SvMK@sq{nmX~aPTANF(_ihxH% z?*MKe@${fd%qs}u+0+`9r+RFxSQ7WP6Gw8_EO3KOCL;>5NUqJhyEo`>g zx7l3ZW5Ycwtxq{GGciuspTGnBm%24b>>h!{?g?zODQvC_kXR%~gsknRxu{#vs?>aF zBNRkUw^{j#ZEUS>f;sWk^LpyyZEIT=w=SF^=g3)8Di=oEI|sTMxKv)aaL(*`%NB|{ zB=@aEc%vkQ1Hv&}MpxzEre2~A9tbo*!BnR>*I-2|#1!UWYG6>FSF|@Ibio`FdhXk< z$qkGyTm}hC;zN*(q5Iz**>Wm%akuLhc#5$Drd(7sGXWMXFU5Je7qKGfZQcc~VzFYb=DS`HnfEw;%V_&dYOP=wTM#9SKjPVPAVvn2?FJ$ zg2LdFq?}`^t$nkzj|1?>)J^y`ll+;w380rxy0AfkJENW#)@Kar)d9>kFyL5#`SP>) z+s1qm2C>5Qd6R+#?xAAPJ2>(gV_SisW8c1s5}_ApTj4XtwgMv_X9V_3UJ%K*gmp{F zP8d*_DNlmlHY#wiNSVn5^RU4qMMdagKoWn`cg+0*zS8rdqDT$VK)LKvn)2MDZ9CpT z+}uRm+)Sk1MgnuEv~JHdIpZY9z|B?|{fCm@~sUL`#j=k@V+AnxOxwOFGM z?=a|32GCx>b{rbo^>jnfI1T2;{p}^jz0w?bf#d%6661b1Of?a89~0INgF1EIjz-)M z#9x%bnCcU7h7k{zgw}8vJB*BwT>>(~9Y#ioSCRadu&^ti8p3ayp+kEt))fmcj!fUHz9?N?hDQU2{b0TJ9r2< zg)=OOc!&|yR#XSJa5z&zZ_Y*2T(&ZIdZ4BXjhxvTk9k%jy%rL!0f0*Jf9fk%STlcj zIM)WFHjoVQT=S zgJ>i5nPKAee43MY=3445%Gq(kkPUJb$&`1(@Gf)0kP%;G1Xj4%YrHjNL7Jvoidq6t zq3y%%clywdI?Qi$xXKF`YN}=|>B4Oy3#%kruDh+TrbY$K?TrV*#;P!Fzyd0nd2!(x z3p;!Aa;ViI`N_uVz>;~488w5ZM@LDz9)ipIe+po}K>VPThdhWXgnA9YV?s(mV`6+* z%zF?L6I2QgGxcaF*qT~YakmzO|0r@@tMl)4eoE)(bbd)EOXoL{!5=xlm)70Rf1~N$ zT7BmeH^bIwhE>Wm9XR%k^5 z+RZrwpuZxldjp^oS-f!WK$Ig~!&C>Bm-t6JN>Pw}6qq*%pL|RdPz=So1qwC@3(7(K zlNvtx@Ce4eT1)q8PM0tP*F-hQ7xaN$C>7jGrGgaB!Xfra#(s;jSO-Aw4tFrzXHZ1; z0unL8&Uxvd)j`JiXevYrbQ67S9CW`>iynmu(<$!LXRWU!U_EU6enPiiTK8$w7@(N2 zXvB!!h*0C}sp2jmn<5pu40c;39P34G%huoAh*8 zD#iistgkKjfL>nWB=aP_`Te+Bm+TPMg9dzMCVh23?rzht`&!6FyJw;jxBkW3a-C1V zmp-MwD$r&n)gX0V0D(V~69LltWe*yw2W-0)bS{(DgS5*7YZ-inei?clF4Wm&z~BU= zZlLhhgUWX|v&Tos^`#a1=)JCr5D`3>dX=1ly`49&pY5UsYKqINlUVCr+L}<8x)2cN zBp(X&GY~SIN?pIuu(rkZX};bAMTt5hGhr)L2|4LJO1>m+S1FKfNOU3kvw)46A#!`Ww!W{Ja!ckFwX$_!>z*F zZBW3yJ^u@c@4VQwJR4_3c6R$GKHKRI7m4fOv2J|Ha z;e+(ggk{LBx7?WGv*9MJcn;=;iht8nnvLrQ*H1-fyr z3{*&CBFLAP&NYXn$2fU4kpMP9YZ_Q;m)2v%QWHfSO0WSe{im?LprIK7HKj|;up|s~ zdIq}HC6m29{AvdvISxR}DO6+$P!{R*#)s?J8^_PL0b3gc-wR;NIG+@QN|M zhDjA~QjG^(`UeALwRrO663oDyRT`apRQA^n%f0LGRA^&iXqn~57o5owz$Q-?)*cP= z_#J*E_-<8#23|txR2{k8>sSA;?v%K4@c%Q7K5=-EySs5MTXV2fSspS zGy9JY*Rs-<0ODz;-rPSG_R^XRSvk$GJkH+P#H$5h+Cy49jNNzEhRg)PNC2cgj$j38ifYc`a0QomRQ-M z<^UaX7(*IcASmb>XpiC+Cd?PSo6E;UJqDeJquzd|#t+{bTK7fO%*sSRj$HyLeZNgyo>nNb8CGTRbL@(kQzP{*@Di*mnZ%s+c6ii>iTCasTW5 zF9!Y>1OKmJVCQc~@-CMroH{ztOH1)WmnV3k%M-ly@(JGR@&s>nd9nCbm%+a|=Sllz z9xVGS4wh}1id~b$SAw^?$aFn#b-4v|ar(E;N@@QLC(aU|!P?8d2NSaWIo|5>5amaI zz$#_%kF@Fc25)tFh6mpN#Irts=Fz*q@aWx}yw#hgIS>V1&6x;)KWUB1OzUB1m*T^`}B zF8g?^%XfLJ%lDwPm-s#`DkmP}tu8;HQrNS+)#Zn-koJqX)n#a#8;&HNbL%3BFS(78 z#FyRik;L<`f1cP2<-Wv|?u1C}z9G|pa^IBcKf7sJ<}-DZ)L{I^nYiXWcnYO zqh)%3W`eXurdisd%p_@7WF|{{L}rS#M`or7o%dqQT1w2#Z&E$!noNol_)bHB8kGrOcc zF|%6^eK@m6+9zdF(mpw}SK6m!o|5+Dj3w<;GfzwVw9GzfpPqR}+EX&mN&Ae;h?DlwBh2)~uBF((DRp%j{*+UY1=c z?MSv=+RL*Y(q576lJ+IpK51W?jZ6Eo>_%ySD0_{xFVAk4c6)YE+R^M*X|K(0lXgdT zyR<($K%ib;Ru541;-P!x4-J9Jd?GI;nOS?b2N7}J$O4{-4UTLq-J|*o9 zSxXMZvQJBUV|JgkH)WrZ_Ep*Eq`W?_Qu<~=23Wp`~GGd0jcJg#cud@Ei)ll zjP@%f0TaFlepQP;^=X1q@JF?@_V#*;-+K`-w#7&uxkk}Pu}t!(;8%ecZ-=E=6J)^; zx0w4{P1w`$>squ>%rlbB%I`z?n3koRdRBK7M?QnddbJ)X0OvVGROuywxzkj8FJU96+7Xg9$Q{01+%cep@h^eSS;09T!BJL0)h`R23N z;Wq{&bBj6g2I9sBi9rEmeaTfDB2ori}jk@ zPsLkE)l@1~pb}hcHmWo_gZ4n&NW_h9fjkhBhd5LMyr`X%E5P;GpL9k_`z9wW?d{-y z>`#OLv2OWP-j*Cg^Ke zr`b=zW=NWMR@-04CYt>XSP->`oIzBYZlfF;aL3D`^=^|K+UOoFhpuub$e~ZT&2lK=PLe}g z-N|z3M)zE4HM=vVb)-9AT1UC9Fx8B^WAOybcavSntdDmgs5#1oOlz|H@yKS?qwd;-F2TX@4;UhotI*R&unEzLHWO!YG{27FEU zT!dG*aJMigxez4NI*?Sxr0i2U=%IoQg|2PY zVmspMT3SgjW`MxKkF0y)_=w8&_8AE`?aMZ5B2pu*?%h24m$ZQ%vITC>k&wFkjR z1%lNEC@6AIj5;>{*C`~cYS9v~`8WES13z z6cy}^=O=DN@L1CcqhjZ?I0Xhu;k=uIv~#(&-AbpEJ4M*)KaO{pj0Op|a1t5hhc}m; zNd2lK-D*6!$J057gl#eCl6xg`ajye`aqnS-#jv0Ayb+YKCS}GVU1l;OK+djPn)whC zX9h{mZm07YooDFaz|x7?Fk-Xs#*N3>_n|`BMRc%X^+Fape3P?xL47Ox7#*9=OLTq@ z#}Kw!D%Is-V?4kf=Yh@Ge5^Df8mv(_aPKw!(Fm&3j7whl1cg9-Y(S06yx3;MVt(d$ zRpHcyG0ikdWtfZd3Yv`YdQ)~@U!Q^q$WB=)P2B=xbgGIAY}N(hrXdbALmg~T#T9)$ z9Vr@GU<9@W_rdE}Q^~oAse&jN$8o&+EkF<$QbqiO4Cz9Ik2c|u>~;m{^ddynv{2(A z_`za?R;$oJ(j_W)eMz;tEZ$T^BK)u%vBaFQKHA!b(_~(?R^%s8ms+>9t;TH-CWniW zz^8ngFb2xK1hF+Og`@gXgf@^`t}Sf6%MdeK#rQf~UQJf|AIXmXXsn~R%WHBj6YA7Q z=Jd0vT8GK3w@)9Q^0+tZhbk*>CUg*?wJL4iQerED!8CMr_O0s0h3vWWW~71J5II)Sz{sox ztdeK5gpp!9VjEgaOkQPgQJMPtlliNX9f%y)GGid#s}rcz)B3h9Q_s5*-I$9OJD(O# zJqQ9se7M8v19WhRdL1{3t08&A(=L!2(6mGsPVm9`D;yV>0>8$S3HQ0!&A-51DXkB< zI3Iqdiyh^uE^N_$#f7HuiJ)m)PJ+zGYRMcE8T=K5H`ZyHMbfIzbdr930^DBaE>OV? zkI-gdyy2+KuMhzNhjn@u=Pz%_p3c`fbg-p2Kf8&H8L40vCLr#}LU-!1>~B$!A!nnz zf*efJ2X)TIKB&G2_IlS-Dr>U_U4l*tdph3MldurksJfGGMa5K^CVU!kW4Tq- zAMFNffD4eKZT+h|Ozb|yHh>;5NOpEddj~w$;~B)%o0ypc0Ldc&&mm&8;f(lMV3fUx zD6(P<_E&PIq<=#k2)~L8*6$^RgL7=_>4AF5a?thO4d#oNgE4vB?JJ0>_r6gi#eCwZ z2##50&ISCaiFgeWqp>14yR|`1?{!3t4MeGnaD5K)zYyDinFlN+>L)vZM3dzkh#3QZ zTyJfAS3H`?HxXIe!eimFSm&xP@0+&}JUS4}69MK6$~1yei`x8>!iXBzvbaZ|Atk=i zMSJ_bmTE+FJ)(;unh*iVdXx#lqSy(DJ&n&aV01)_+4qr4BX)?c79Vh zWVoD(AQH|wbQaP9!*wF{KTZTqgM$NtHBi6A(=Vho6W1n!RviI;$vu-i8II-5;^pkK z-H%1C+v|dd`kMPC&`1~b=6N@Z#F+~6WE1FMLEDsBjx}nghtBoL1%hGgeVHLTFVp#( zw5DZ`qH{K#h_uehg1(-Ug+g6YO2cYf^7E!55k!-%@WX^Af_n;iYYunTS`+2Sg<`4?OndRfK~LRO8WD)iPha9{55LC=>oB!ihTO zr->*qRh8OwQ<+{$+M1YNg2;QGu)4Ok{`oPTSfcAU+S=hbVT#1-)%SDdI*G z%*(?9&9ga>I;S2YwgyrMg0~}>jah8!+SF?Y5)k5G0>zkB@oL56d5IvFj!UU8xq{c& zUknK$F3beybSW>)T;P14jw7vw&fh^tT&y1#xhK<^PG<#-+PTpD`-FQv%%{1KaBXqF zM(4+{P~?I*ZOwo{?>u@Vr_TzN!z$Itc#eo9>+5mQ9VCh>6(B7qI6mRZM0^oN6;8OPI z4Ni6$oRmmnQtUBk)zd}rCN7g;FA{Ln>IA{jX(5LFPsey8$9%RAkw<&2p;MF`g3lq{ zyL}*rN%#^H653&sykhbSCh@A3H=tidLe8@~p}IC|cJBsHruI5wIer7N%lZshZx(+^ z5@%wm1$6|zR9z5*C*S<5Nfbt+BaukANx4*wNJX^kCJ&;$2{A;wj`83#nNL6@$%T&0 z19}q6UE@V5!DDAvXMB^Mc|0OL1*zC>I)f4xwoO6PkdCBNff`8c*_SrSrkm_Eu;tUu zMLG`N@+p=e1=~!~By~fZ(o#0%tUxT0S~MN#3{9$)YU%@>(n`|2nYAU|s2g8m)6@;~F)06`gCPwH8_n)@Bzr{yyqvrL`dwg7Nsw zF}TMl1IoB511n`$WwyYUbq2@ruFin4eFU54*0tHgVKx>!-PXsm$ZALSYC5+{>(=ZS zVJ8AR$=1!;-$~dO(PZi@LKzDRlc_aqq2x_8@}&rNp6G zlkr}pV2{Xsns@h#No66GnpN^HED02}4+#wSRw!V44pD4Z)tW`J=$8=7&JsA|Y|4BE zF9ziu+-hsFY%iFp$-oPHUz7`Dkwuz>EN7fdOS z3FhhB9GT-cYkk(D5pk@7k37cVekAj06QTo@pP%N~p9v=4JgZLT!%2u^qhJu1vQ=bK zO+hL%)D`EKzhgZOv21!hoKWxFSts-Tbfh3#?H{o*;d2pA=1U6$OyCj(5<5y(o`YhA z$pbabTVk(7IGZ&f3pE*aAcjcc7dfSW6WNDIBAY7hzR1bMZA4rSAbw7Ht}4K!*o+jK zs;k4oM2g1*Y(wThhJJtc6ltx_Vm-POdNgwoS@3=LWPb!PTowlA zUegM_e>>Kn&-Vf_HmQ=!_(Bo`wS`DF%AB6|!l-?SYHW$D#--Yux}Xi;21y4dSarhU zyNILKfhP%96YuxMrIUnMAV!N%!(S-&@81i5rg&rjAK}jzO`-Eo((hZ0|Ge>!Kbi4= z-M>`%72=SDeE5BSNRbXZU5tzrpxVs`w*A|A5~rp4flFWTrnV^g;MczrpyQ zH-7!8CLI2JadhYb<-a@hq6t6wG#wu|{ukiS7srJD)4bQ5&i7+OOW-sAtBrr33ZD>q zM)}8u{sF&L{9*t5r|5K78h;1;`QrG{3(9{_s0K$bTE)Ko7rXX^JhaDcc07rCWgKbpYR>~e(hgxe8>17I8TS)WBh+Ne)IV{ z{95Dx#Q29_pu<-f{|V!d|9}qfGyV(4ubi&K=NSJ%FQ zq4DoA{_Dn{J5R@po%1W4Q9R9lJe~6)oPx=DIG{;SK(PeQAf0V=Ky{vg5(ym0s-ED! z7o;-a}AwAI@{>nLg#Ke_tV);Cq?HeI#1JihR%z0UZ&&Fd5z8+bl!pkMN>F+ zbjH&;nocvF$#kaDIgie4It%DXI+xLDr_)6zPUjjrgLJmhxrNT%bnd6Kn@)<(Q*@rD z^9-FA>AXzGq4OG@H|V?thx;5)K!+2~csfVZX{Ixo&Qv<*(V0zW0Ub%_GCJ*ay6D8| zTtjD&&Ne!?(7Bt={d9KINzr+V&eL?Bq4Of0m+3fkUZe8{96h1@`8{`;&h=CH^Tmmw zoSzZiez zr8@kV@GDS{V=mKvhw<+<{%?$b0*;fPEGk1A;WPbH#{ZM?&%a!UUuFDn8^7WT9lp@` z*BSp=<5#WJ@r#YW#rV$}KisC{FEai%S5Dtkx2Ivxlsz-{jN3DQ&#XPodoCC=a!k#bV?{;iY7rVC zM%0M$Vv9Imj1P%W2v^RHs0dY5R8&_~RfH=(AwDh!MFQ+k#fW!}Xc|#jF|uM*#RO3$ zMu~BuBS#z-`py2I?f>Tfm-qjC{|oz{*#C$9`}T|fTYfulrLH81eV9%o9mxK?b0K`1 zLT4JC>2&7OSwd$8ot1RJw?4t8{u5AegtM8>Ryy11?4XmRvy09iI(z9@boSACj?PPT zUZL|Uo!9BS2?x4}aKdyN=`_)qKxY!2DRidMnNDXeoh5Wu&{;{RgH9ivjdV8C*-B?S zogH+Nbav6%LuW4?i_She&(V2_&MS0YrSm$SH{o#e`-x1LP9vQrIuqzjqBDigG&2%QPqqC9DW;$ExY^Sq>PLj?pI(z8srDM_A2M1F|`u6M!nZ6D4M*8OL zN}2u)CXe(jSW%>J#SD_(k?oV|J1~u;@62wL>ANtOr0>pdmg$|CP}28gx61TqF{`AL z+3hm@IZQ3-d$T)a`aaAr>HD)unf^Q`ne+qMT{8V3W}5Ua$bi!iVY*2_oZTzak6_M8 z@6K8>{U|1$^cS-GWSX;2ItBSl`b(IC(qGQLB-3BPJe1y>eMP39#AKBID&)uMuVF?? zKLt(x^w%*hrN5DVQ>MQuk^)MZe>m}9HH?aEIh?IZs?<`A5$Yvuj2+<2B(zM{m>Yd7 zMCA-+4~Pdhb^WSlM`Q8+-c8`gU`wVe$X6oG?SWk3uwWk^)&L=}8v#cF~|Llx?%J66R;?%RWAf%ZAHK)pFQ7GYZKPWm8`ErJJRCUUzr2y%SdS{c2YSw4*98`|?+#vQuXYy!?u) zi*+!5wJN37pDddcOH}+u>pi|%1M$VM($ZJHkDjKIH|XTtH=`Wq;4=3G3}usJIfP#o zGH1?SveMY{O#$qTg)lXxEoY=mr!haJZKqDAhcHQ{pLWK}^tUimrN8YQEz{qD8h?79 z(=5~9g-|E`eP^;ve-9H^`UlQbnf@teFZ&4RJZT?|Da^i@YW!=c#-F4be+HA8{Wk|{ z{QI4C5(f}LeN(*Rz^jBe^OL+Z-SQlmQ&o@rTr840lq?lo#Ha5$%T^V3GOeX{Zp!irZTmW z!QXJFt|@aSj$US38MYedDmgKvGS@{0f0wyK+CR@c&ev~JH{k`SK4hTF|4J6Js#JC? z&H->~k;}rX4yLkqO4|X^#?yCkULgDLjQBZUe~WUpl>E1< zpMlhQ8x$2{l4`;*now=_dTm(LLno?qqh@tMXQ*tV$tqEuN~Gk8(e|=wy^hHYeQm$8 zsXJ88sCVBc*{f4E8Y9(3)KcGl;nVezf&JV%eIbGwuVGQ=DP=6~>4Hvug}CxN%{zck<%2pytwgIo%V{s%Zdm3NQ=Id-^kcGPc?rc=rf5~oO1SIh( z@Wb|-S?mg=Kr-#Wayv1V1-5dQn@QuTWb*XJd3Vcs*@J&BJOzrwubHI;~E` zPHP5xOJ`4KtmB|3?G~8X?KtpYhmaP(x|V^iF3@%AX!#R-LB&+U02c}|Fn%@sQfq6K z0jTi1sVZRnh1fO1mI7?5#e5}mFNY%h-L1Z9z&EoyV|_Tn#1kSqb(7GR-U}(Y&obcC zz@()M7h&qOX}YaK8b$Ro*kI}G>+^RM>%`$R`ue&yachhLZSpL+cn}=`m$OtJhvnyCPFfvg7LXd_7e*G!_S5iRl{2SQAt2wuZ?u<9 z!9-k|nvDwX{8fnZnVU6FE_2K+!&lU_VuhHY>NPI!Q*9RMATxjfF%K|pwQ2Z|%R>S& zA%1>!0*0TEBgte1XadRP-<%UcHXP8l6h!lbzb9w;cYTr9N2UE1W$`KJQJ%fTq8(SE zfaEw?oWi7lBjtiBHMyWl`&}G8`W4Slrrf1+NWdBdZU=yLAVn>SCKvWHcDuNNr`g3R zp=K8+k(%9KFv;)bP$*M}S5nAS20H{znX}|jMFz)_nlrGOmCAs`Hf7e!p%Iw`pFYX3 zyBPa*IW#i!BZmEyVYpfGk<6Qj$Q~(&MrBV!M1V{#lS7r+UWRQz;w-bu-X@2tD1vUH z2s(w6Nch%~Lt)KN{zgs4MGI<$*w8SY$nc3?PoR-4uN8~v#&ne%Z78Whq0aqziAxX3 zYEFFSd|(CkHTfgmF$UGx(8Fg?zR&0dVxyN?;p|$vKIO9;P<8W1Y96%!1F7bb;fZ3o zyj8|wF}j3RDu|EqBk@)U@eYHMN%YWCnnvW4<5F2e!i(RLrLxW+=x?`xt5pFcvcb9s z^Zu8ity2}vrpF;(3CYmBo;AHQOiwDe772NWORx{`9V)34;Z_{sjpIy;u>v0s+(%m> zq$;Qi1@Y*_p)^Uk&QKvBy2o#<$upnSAp(N&x7pXk&Lg;*2gM76J~)SiK44isRIP71 zUdpg`IaEUda+9|N$8!6AIaI3+DW{p_hy3inO!A%qgr`%%@2GQ?Oy&GZJs%{15L6N z`N^gXEyZS@qr+44S*VbtaDWNtl$z<_6jO6{22P1)K_16uSK|w+&8GBurY($pNDht5 z+Kl)fBf!=+WiyD-!p`5kkL_F`u#1Bq=@^?e*k#{7_I1vN|1&w~$rDQxB!5PpuIl7< z3-x7w#SZX&Tzya4N|^p<$d|?=r|m$Qc|adbUoJ(xRw`(YD3NnMQr>R9-cmp*L zX8kXtUD(;fZwyM#R}LPYxcF;Y^>u|92eI?gZdeEi=*zNa=LitOL+`!co<$pPlqK-dZz;uS(7}%(#Cj*GeH2GK_8qm`7|B$0k;$m9j0%v z{|F=gKla`PK8j*%AFc$1u*#;!J!%kCR1_85*@7Ue&@`y11QH++l9&VpMU65O(75l* zjW;T|a$PVYC~D6%Dvqd#%SA;+?FB`>=tackHQ)18b|pVJ+O&e6EwoXTIrcmez6z+D}%Ar3mC z-JK=8x*Yvdhy6b13(Y*h*+$`=xZq;Yp6+(T1#}_J1Kr^~q{4Tz`Ku6JP5QBCVwK4} z$mJ8cd?J@mug{>6++IdYh*oSRG zYpNb;`*=j4-tWpwlia;cd{&h6W3YGo=ZX(z1o&g;lquKl>i)U3Pm^i;wLT6`nSpt^ zCW>#Mj7v2t^QGbj)o4TGk@#mX`IS%lZ2j3+UiEz2XKlk+5PKHd)J?#zB{Oiqc*$)#%QwlTzMEfkmkYGUQi6yflv_&9{6Oa2<-4; zX3z*O-CDp4*Yek0yzsnc9%8NI;d{LB1%LfpGY?f;@Np*4rpDz?%{|zQJdEaR>0G|4 zR=N`C`kD4Unwd$%fOHxLRNJre*L%FMLo>TO?QrOH#RcaO{yJVW4|hiL*La>|PZ0KH zW)DnF;@8bQ_jmsK2NHloyxG(FnukB&g3BJV+YuMs-kNy??ZMJ%4^~arhIF@x7p~LH zBi$!>xPlkf@D~<=GU4Q4_EN)b>r7Tqo#x$zXJOznh96@oB9cm2#;Lze)Eb|p3e^N^ zNB_gan-pdZ!yDGNMkjz&5m1{rIT)+hzVd70y-##=_I5+{QFT%BQfpa#7^u6s15yPIwbEP~N)*?sSxAR;j&poagd)esWrQ2_7kz?#FJhuaf z&K{bvx3dqfI!8d*IbAc4bFk0^&wGfdsY|X7VMJ~oPaiM|DN>CU9{k$C3Cqu^1zTul zA6nU_)5^BmWhdHYCmI^mJb_C(>Fy$4xShX%Hkaw**-msZ?J42ls$KS^|fKOy4MaCECWjZK#F4uxmcW+o$s4orV7=8YCEg|#+Lyry5>zswG046r6n71 z!aV_hU4|6^=_@Y>)`Y&v&8`$d8x%KO)8(3i_xg!Wfg~krke^`v>O4N(mnzgkbjL(| z_PG7n^}nrNjmWC?U;O^HpG+%MSXzEk^=BkLPFKtFs9m@fdlL`=txKs@{T%T*BZGuO?=#cHXSYgSh*MO#>t=>kX4z3Q$6;9<>Jc5xYWqa~g2HNRLt!0>UZ$zp z?9bWk&k;R1(_W>S^I`{E# z8B#&F(mdDU7@w|P)y}t?d7j%6hxFyD1{h~_fqN7W`|xnEW}feI;mEy!hXtBB$%P(H zcPn@ZXWn$05Dj-%ArIxL9tpq}bZ)lq>g{b1Mom3aK1wS> zk2Ko`Q%2ck+ep>TY1W-QW#PR7yJYKmt;}2-iKIXqQB^E;`Vn5awpZqze&KXtWIUJ; zk|&Sxj@2>(Z7)+#hIvdA?Z)KI%gtAak}O%zY)>KyvXwM#^97$Y{B=Svun4{Zv}}~f zdkxgA%v=gqnF~wD6%k2aRy7#)gOBr&Du&BM_ZU+gm<_4bM!dpkEoI`q_^EgzM)DC& zQiaz`xRqly_QJvb!mFWHtT#1t8fP`DEiSmwl1MI#w^hsHeKj+e zPPx@YE=Z>zRJA>q$`eq-nHY`(+5K6bGm)PCA(WyG%b$0kGL7S$J@xYOP7mO|I~+bc zLm}s!tC=(7rs(egt2lf5bi+d=DY2v54+-mdkExQYHk;+N8R0X zkpp+8W|q2FQiST8!|`f&DdkpC?oEn(%&XsP<|S%@+#21GMwI0pFjrLLl8$3@^8#>s z4K(@Wxbi4fB=?fkTzv zsruq`kM-xjb&-8t zOLvI(m1X8=o0@!&_@k>2cpKH71aMB{^;Wy{I$tK=6$5WMrz&kU@zsc@(@;BYmz|LJ zx_7CfLyfL(yUeQQUGqa^te z>%TX)CilMZ)KH!^t4d4WHm=_4+;@!JQs8k(UW!*u;g5A_GSR@D|)v#lu@2V2E@NRvBA!y(@cTTk-pi@fkAFKmXs;llVLo8w!$G^X1} zaYrAlz*NJF-n@n$^VK%hnY~OiueC9Zt;R4L`~6@}vD)FBB0Lw(>#=Zy!%>=f16IB9 z>uO%WEpK$H`3p*{+Ci`NCRkD8*Dus*a(|11H1lTnWSn!M*VEN#dX{FMNh9;+$`|K) zBtRqc`?Hg{+Qo_1-uL2A?^fHyEQa5(>#t2ufh1L) z@|$b#O?dxRO+wk|U^)&nP_UtLRf|!(KEj=Qi(VuV~D&K;(vh$i0ss3UL znj0mRl^TM!Q%erbNT^wFkD3kc;P+Jis$?&h&y0PxCH{LL)#iO88_K#$wH_<`q;@$~ zigosGUb}fVq!yCD(sie}lWw7m)Avcf1>0NTQU8(~EtJpFC0>xV?(v^4PEmP$iq#qV z!9o{p09?WGs~Ik%^XDc#pTvNdBMg7yCn|c z>}?LV59Kc`;Z$R-8^4BVW|lpHzb@jr*<21U9!A@w$r&z7D$U)YA=vNVHu$me)-5Q`pG;cnV zhx2*0LW>-1-KU2hvmT;cH7~rPMRX!LRa@Wj@HbqAdt)R+ZIR|H&vbhj_Qa&ycePyt zIqbv%<$13bImBKGl|=ZZYWpo-_)Lo&Zhy~nn3=40TH_p0mLl0OwZtKmUAi+0SDndP zq^C2Nzpmhg>v*A3i{xMt9EY#-kZ2cJ36C7%{ElC=O|K>}U%J~}i{!ckc{qlLuwlw1 zX3}9U`>*b`yl@vUJgP-5a7`X^RzNAw@3n}xJ<(?u#eL(N&`^om^GZD)ao1|k8x1Fx z#hclB)Y5HRwBTBA;I7?1p`u65$u9+Dd%>=&=sGbQ&4Qe#f_Y^8E0Ivq?8GFmTs2V% zYP?GoT{mi@nUJ5ChBJ|DNU%$pNRHZQChC+#LY4Ua;$}96L}RQSvC$0FDTRUZ46WMz z`Gz|F{!J8yngZy?X6emX)Jb92vXs#BUOOL?T{@$g#gfqS^~7x;8*(L_GkQt0m5~`h z9fCd_3!PzcZqF*#B7Loe{KYk5>uz3nOpAmq>K2Q-#bU<{?mUqrZ7w_~4a_42=y^gt zq9>cqZFMtYY8g4oUQ7*o6E9%KG2MPriqs`<50dcenBodL2fW2MFVt&!+NKj88@< zUUN~|N?5Itfb<8dhPyKwTkASNZp!)w{wM;YxZ=_|+4-D*ED<6@q+W(((YdWs>~+O{ zP;4Kx30ylvF)CvmRVenPV(%;VyJCmn{ty|VSgB(7D7H$mj}+tnVUWz&L$ZO2a9rk#`mQMX}6Q^5ihZ3KY9TF>ZW<2%jmoM{9W`x`>v`#6JY4HWC=^EuRoiiZ<~M zG}h^#5>Hda?~~%pa$tIQt*1|kOV5aEAl=BVp15+^@>#J$L5TQ0H=KJ;tQ54M@>9U6 zRpNO8V-~+>f-^_p9(%zG#@}Zz0i1!0N8)0)q>J7ghifm0)lAC&OybOo;w8~$SV=T4L1g*qmLxZwr`fhAhQ5D-L>1O#d6Aq71W z5aex@y%e9gVIhC6QI01l))P4+Pfn|yY%9`C0IyBrFImc;`-&&Fpx(sV#7zzo3DvnF zWxQP`C^Z8qF(luxBaH(RXn&&eS8Ue{-C@D3=Na@=!`ISdvYOi{Rb%BG1oMd17DmBx zk*M05#9vcMW|CY?GLPg+l5&#kNp2&#m*fEw47{tYr%0Y7d5Po|l655Sl6*u08_Q}U zmsWF2;A(3J$^U?G?x5Ok2ZH^|v3pz<$?M!g?guD6Y_}9S0JETxG44KG8^}$&Lt4EOZMbP&Jg?XZwD#jO6>q0c-pBt-ZgOd!#JRluNMnge z32awc&uu!!;SuAnN_D-Doi?^G@mTBXrX^}>+N+&7)y|&G)OC1zhU9!>e4MW~`BnR> zQ_}xSmTapPVtG5BvGvFpLv0~^LFJmV)CY^}E%EqNY&^00@7NmdU4Gpys5d2l_BEc+ zntF??om!=eOVz#|p8*9DeiOtt<-Q^qBF_@*#Ya}J`N;0)J{qaFD5b;lA3v;p9D z`vB`Va<%h3Mc~oW!)2cbXcLi(uw6*xLQ$0^jAvEvr$u{BAE(i*niE{I(!81zX{C5W zif3o%;|HrQZ)#F^2%oUZcCsX%wf5JLUe%StSmCAG#ZOh)J;l>TuSEW}DlI96X+cM@ z!2DA?9Qt3=4)~pL255^T?_15E^7E3L(?EV!(8_*=sQOVD&#C;R;VCs`e1|6mm}9GP zxj@h@uG95vn`C;|T2m%?3QM>B7_T=d+n%@J++I^ULIUzCoG?CYQmJ~+oXje5sCohT zaHsK_mnY9+E-53PU~rN%dBIx(l<~%_mD6RdJZCNB_hS+5yein_Ze%096*a|8=%2He z^6E3Z`WmldlbB`v0HGeY31g*KZkluiWX*-)Nh4+VCrc?MjnHYlQ7d>!VvRSs zyu72ofj3ZWQleQ!BwKj=c8MZeXm^>r z*ZWyQy252PEyY+>tH`)5(O*?$DIM7<9SCe=Y@37~chb?EwZcNomJ}slNJ-;un<~U7 z{zxIpLaUVz-!a3#X`(1ssXh=j5;P$St4cFEK^>wfv6!*STEU`R!>cg)TV{RAtJ`H! zn!bysT2IKM>Nu)O6SHoa!(Wzsv6d1O_bFRS7O)d8=j!duB= zU(I62>Qygm4ZfY$TK)o*a;CLjGhSeWthT2`Yv+nA#0W#gmYonR=v?16G3 zp;L;n+N-jzcq=^v_(Y3s^`ELQ=?mpoNUN-QbRzQWtAUCTBKYPjAGUbo{sUFvxnoJq z;N^e?ewHlo)z(cc!`oQk0X4v4h8pNjHL%)K0~FcFIeMh-p*ydqZ)64Egv+hjU z+}orWFR2>oh>wu$8~C4Xshw$fB^pFFgR}XC{HC2 z4dh`v7_SCX5tRdF`MzYKE8fdGh6KKl=@yJHmsuC^5KHg9&;bj5#f7bttcYfACWc75 z^*Keh;hdm)M}RFSY` zc_!WrSw z6K}^%VkN=}ZRF+MWOoVdE`k515~%#PB_}`(?4<$HiMj#n-v;*506BmpgQO=XReR}+ zdRx8qMaNm_GaYaB(?h>-8;wmsr3?LR4b~T(U=7v7&sih%MJHNg^hGDZvdVnKny80w z!)&Yhumyi{58pBZ~X2B0c=5RjP-6u;%HZe_IRm&~~d#53jRAdgw=M zu^!rC0pj>4>n1(46XOu`763Vge#JN>{32XM!!L1K=4DRH+yytN@SW&B z)WZ?<48u|M48taRhT$dX8HSglXBd7MLzVCv>peaEinUP>zlxz>_;FahhS#Fs5`Nv< zqK9MXGKSxYIr?5l;LOXPI`C+KCa<+ z>~uZ6(mp^Bud*}r@bmcAhF9A?_3#F}mma>|?yZOKK%X;w4-Cd4U)iVW;d|}=TI6fY z7=&w8)p({>psFUZ+KD z;HO3$?8O*yVe}jM&fcU&{spMO$Tl0ZVc!GBCGvy)jTZU0y-ka3x3_DNAMKr5WQYBm z7Wv5$T4blwQj7eIZL%W2IPJB_ufSf7{N{AiBL72Ej^D9yW2DB((4qn!y3rJ;rxs0x zSzNRQ_Ros8#0q6}4{V|pZ3Rb*Xls~!MAMwXTC@$^(xPph5n8kz)-R*&9c<&$!I`K< zJ35oJ=$=>87M z??4CTcaU?F7CqRxO^aq=W-fXNX6B-YVrDLSn6pHSW@0O@Xm1|E!xM~s6|hJS8?=2XR{VP$+5I( zUuTOJ?FWnK=*iADEqaQxU5lRT?9`&CIlpPq(_NvNPhuy@=$USs7VQsfzvuwBlNKEa z)0OBTH(iSkb`Q{^S#E|F9Rl2&=vi)0EjkqYz(j{(MK*f2dx91n4%5Ks2y8YP9qIPh zqNCiwT6DBKR5PD;M`+P=VD=In3#_r|ICr8J)!a#1bi6xRi|Sa&jZVNqM06rIk~t4X zG12p}kyvz+J5P&V;4aXj7rJFyG{+5T(OhgK7M%`=vgi!=CM}wWjl`leVK)<<1o z(M#R+TJ$n(Bo@8g-Ka&ca5rhuE8WeSxx%%y=+*8PExOSCMvE?Tw`tME?shF&0h`9? zb<(EM!1~ATzyDYXbd)LkGtck&qXwbQi}g6sbqFUTiVL#y2h)>y+O(2fA5Vbt?T2^+ zv&7IqY{dC?LwG`&^mzT>O*q|gNLD`fZz#ztnUI&`ug8kbG9hor`#?flaK0_J!uDoD zy>zxQooVq6(F5{36Y`uI^ZVWC5Hl5T0?Ip^hO^$z>4~`>?^-8Z<2LnyS%qMB({Y&_ zng=c?J!l55`+KLW+{qH|DO2@Peewae!=)azlf~X*1-kCssQVjmZq(fs&%zD5FOdAq zl-IxqVvo;#kqY@lk3a44ogN>DK^Ct+?eX^Qn9kuTmrLFwWeqs5XJTA;wm2rGQ1VeC zBDqLO?Vzq_gQr4njmP&xo8a+GkM~bW5xr&eX>fpalDzz#der^ad+U zi{6ADQ1oW>fTFjc2Nb;>J)r0v=mACVL=Px>x0R_y??E@nTxs>vqLna!j6MYPwCE#N zKP~zgyFrg*l{vZ$c0JL5a5u3htPxstxdoKnRpxCZ=yQZOU(IgN z^Xvw_fKgd=wFOwn^;m7!$-@Ci;%GOp9*71ey6FtS_S~E~37gtjo3Wn_7PYW3#TINwVG-UO16*@|YrAH~teu+q zhV>gf)6oqwFJw0;$4;Z`czZa9+nwNvX?N4ix9xPzoM#_E&&dq9AK97kGO|&Ai|t-? z66&p)_t+@`G;^ulUo)Su2W#e2HaXWOy*P32noxE3-x ze4~As1s|Mr&qRuH?nHgW>!3u*=)^OLZ_g-K<3)#f^r70w5h`*+zAb^sNG_FI6#}iH z5U^JLnY`~gb`}q?863>|M9f}ED68{%5Xc|Zdo%MVW5P3W z9PvD|OACu@I6Q;DV9tIdY?aKHt;?Y7xELI< zZsz3f{k-}lXCPmOJ|z5yVPOx6Q#k7z!C-Y!PpD;M1rU?WSLxp#p?~`d?22JV>|$zA ztY1YiO2j$fBlof&C4pPfD|Sp@bPNIDBN)};>NXz!u9>enT_ED@hYJ{#87rKA`l4f< z;XDU4khQd3=!KCKu43hN1>N0`b(ko9m0ow?t=^0K3PlJ(x59x9e=nja^>X1$6~p)h zn(-7okvkMmF=;*z*cuOSo0}ZpVm`SBSRVGhxdTA+`P+`q$|1F2-6~LTSC6f5A z^ydk`esp=2JE0_22pU&p$z>Y%)#^y@zWw)oaed_&b`LY=2txT6V9=d zR*88j7>8%#oP9izEihG=X;0V8+qpeXFB>Kk5zG`p_&&|(XfMNCWUuB0ykmFTpYoSO zk?jz1QgKyj19b52a=2*XoP?{+U|t=|t1w0HA_`&F^Om(XtDMHP}%(dwMR*8|JxPaUyADsbx2@m++Q1MP(yXPJ{Ns`c|1JdIy$qttVmIAg0{>MdQ2D7`28m+)Z#ra zlg)B!*7U|F^;D}rGWjvtwbW56s~3)57Ra=~zJc0Q9`Rx7hORhBN=4g`kz^y0!f1vwOYs;-dOSL{cj zv!sw3G!|zUlwj_Z?hyD}og9g$#D1c4BISUSm=TcmvMu(nD=TN8F`Zdc<<&HjEMqu8 zqz%j)oJUk(9E!J6lHJAj#JL0UP`TJ)CM#z;eU^oIJ6Q-rU?C3jgoYIqs9o8jZ<+DfCW22-nLHM22W1Z^y|P9=}&hh6x?wB_!7_DYUGXI~Iiw73~HV7Q-n= zdN@#OCF+)(SMgUyau}sbVFfAe(|KlOJyMyX|!3XE+)2f)*KRZ{d;_EN4bJ-SCUXFrxsw9NOZ*G-#7ERS$pS%+kZ3I*ax2 zXYk4oZ^rI%`1LG*ZP3G?J0I!cFPt5E_)FlLge`Y}J$$Kqm=+oB4#0g~0Ii(E^{O+m zUKJS&(+uMbSY<@UxhwRr?Y^Xk9d{GtJO$WNy~~U17I|4u(J78Ou9k0PD_u}a-C*XV z<_NI|CSnU^+Z$R>Qb)>JKTjK#c?krnr0ghENqo~ad7Ext%VV|TuZ*D=Zd01yXE0A> z)N`AoMQ3GsTg$6?Wvy1+1@Vs&ojp4QdWos!GN!v4=`#4aYl(BmicX%aL}T~cDXbZi z6fdLq7QS&eXRLVUT0Aa;pWt@5*d7Ozb@)LK!tNZZ&_cK)gD-R;ejKla5?Q+!sWA9L zOudgtV@m!@eLm7)2#TB_I#5JvL|=|kZ-UAS%AKf^@}F)Jl40;g_*pqgz%x60dfdxZ z*3>)*G5CT^yRS-%hTJ?X!(|3vgdd=On5;)y@i+BcTxalQ81H0M63qAGv+RD}u!2^@ z;LGwOb&6=i?L6Wi9rTCmXpvJzy?)U1P!hKV6gZPTO|;G`kl4q}2x_u+RhOME8ue{H zLj-)Ahaxi!eukOmnL_%2`OnlFml=X0{e^UHjyIEZlnwAVkh5u4oTf6+|X+S*k6yU@!Yj&AiXPKo31{ zUrn|5upU~??T3%Cf6zlO+E8NO5}TxhgO#vjoowpEux1S5{_8y)F8csFIWpN5xTV`( z4}a$#%5|e7;jHT(17}@myig`J-ZXA!w5J=!joe4+QUA0S$;0B2G0@$hMJ{)DXvQE< z6q61ZAQlE2Otqc=4U^o}HawIVLIN+J`KK`G7KOC2h5S`=t z10HZwvDB4AIea{7;4~UnNsKcRn>WbrTd*&MUSiTiF!CsDHX#M$pP}?xIb?F9<5K=t zWLB-TC^bg3RcZOi1^3h=bdH}hc>Y~N(=q1ax>|xbRW??%8BE@RKaVx$y$HN**x4zC4ma2 zS{^PK1ohQ?xz!>*Tfe^;GWpqBTUul@=CJVr8v{%$7b0 zJ9qG-T=~7=cEW?LNmN+a#jOV%ts^sGR%M(3i>k;hB7GcheXK?Dt)HNhEZ`o^hRKtW z1#72Bq1~UFY^W9~rssdAJxhz0*|+i+R?{-=HG248_80v1wI1H)z%MJ)>7<9hcaG*S zsMa4GT@N|Vg?jkk4i@Hm5Ps=6=V~o-8JExQc7P1EzC#?P_~L_4}E1)5e~t;(nLg`&0R;m`PMQP2#5bi_p>Z9+C|X0oTq3LV-CA3ubE zNZDl3X>>vI5;{{eT8kQnplG(}M$x&{6*&qngq;G4--J{dg5p!8_%w`!IKCW`7r+L& z3!)4`@u^}jq+Ul{X8bPXgdr%OBQs8Uy3Bu!hP&6N3 z4pmkB&Lr|Z14%LjWoL`_o@_kH{z#4?C{_?pk_|tR^VLVg*75iVwC)D4o_Rt7T75R@%uEB9n2_Dv%1{gEs~P`rfI2ys!l zSXiy}&|T>5nePBa-rT?yuuM4pgnxn4Pxx1wBV`gWNr(NZQOvC^=UBgJ##{m+-)WE6 zjD9vulor5)#3-|2k8`rKuV&l=yQ-g6bTojB__!<}`E_!x% zpkrqAgDt;tnWvIEp*RDrHrTTIp{vFae2~(_b5jYh*4V@0OO=LaW(bPU5&QbZRPPHi z6vfIAj59ZJi`s9q(Q2Y{cfPagIUXjJf6+H zkZ1>q!uL8&kmnJ4Alt`iE6At+YPnp9;RziB#T7=m((sBV?q;3@fW z(Vn;~Lr`?F=;(_kluHKEV+aaflMqyO%_r^&A%>vfwX&9_x{^HzK**NP7nSn`aov_! zS(+m zB==~>NC#7M-@q0z{2R>>N73qggnNu;T!Q|RG0(k<>IdHS`7Zi+^F1BZO|^*}=DRoY z%n;0X1+}LtH2G=uOD^%WLlJ`^80$KIF9NZGs;vxajUgz1y(-4poUmpqmfwlqXjTkC z!5fqy?2_k|%CQCWy#;q?2nycF*LD!VXiDX%Ol|5%;mr_a2nyfCet{>9d6^(aA6~o} zwO+oPWgcYGoOMwni=N0RLonG}__Fu~#(QtWeHns6x3cv@pHBzWRN*D+W!MQBhM?SS zGBM6T%~b;d*bmk)1ch$*g$N?ZJ1&L*Lr~}rPbfZig9}a#Lr~~WU&x=!>xoie2nyXL z_8eWnGEjqAZ#3H*QVc=KyQzLCsU9!`ml=X0_fY*%#Cz4#P?`)unR}&7LC)YprCZW* zogpamH(v&2#Ue29_zXdrN>*W3K~8)$PQl(7Suq3!@1rWqqE8nk`r#@=P~z{R6(r{6 z$x-l1=n;mXKov_8S|l4Y8uB%(f&fEM=zc0hCFDJRe-sZxQ0f6@TS5;8-r9Z;Vh9R8 z$cjk_dP&a1O&NlcOGM>D;LU2rr54PnFSllEMx}L!9{wNeWzDz_J~BoHcgdM-4}xyt z&WhL5j`9ZXr+B0Nk!C!=?Hyip(9;=%&2Wsd4!Te0IJfKJ-#T|8IWNJ$~{DV z@5y=F5>s>{#2A9253~3w8lP?)gV&ECDD?>YB9Kzw=7j7Pq{a}GeUz;bvi|F{8P^$t zGLP}Dvh(3fO-4O31f?GL9%E>3ev$NdP;={dAu)!a=rXTlI4R>#ci#jVhM?R(0&@O* zRT&xvLs0GsHX}TQoXAkr@EI?MY*}>9^q+eoikl%QTkR#9G<($(2{Hr) zpXFB_kHYQ!>RT{+3JEg=QlANy8QYT0;1SOy6S2Rfy>#WRaIs_Sl!Y>4Jz+^qG^9X8; zAt<_~w{NvC~Qmkg(5LziP%s_Tidw zzYTr!AoqElXu~%ATzj2noM-MxbPh%f_MNssm?0R!V8d1s6(sDY z54KIgA_y@A1z+VS!{2s^Lj7@HhM>@E%;E6d`3dWjq4$#5HU|3g;>0fwN^I$wwbPd?g4ydw-j zp*N`?Jm+d(kH96nh9M~T7GLFn6Lx%}3=Y~g3_;&5)E-i1!i-$lG2rhJR5(LW?7g^HyaV?X zL>Pi%@5jZkZJc@?1l+@^h%oIl%gWY_ne;%JMgX$u*4Ow2M_}W8`xMQ%k3FJE_RX4c zfxSXA9tOV@+JU3VYPXwSp5mUOm#1O}RQw9* zvXhiqY<*nCf^?}Q9PQ0^nXvXt``9dp+SLJUE{kNx{5#@3yX zHHM(rCe8{oy@u3h8X_MA)-rKHTS@#UgE1iw$5DJdLLooy+e8C}gJ#KQ8 z&p?xA2+Ds+^*vT12z$G~Q*u3?j3Fp#u?+B0QoF-YYA0SwhM<(q5nSTaH4oWi2#Pq= zS9HW<`7@ox5MT%jxxSFE)P^GC3_+nSyqQ|>V>*DJs9^|-e8uNW!XBk$5fWkuN`CE0 zUJ_qGq~ttEF$5*ICZ@U3sgqM-av_iMsJ$A7p!_#1Il1`l4@f&ff*~mNZ9>W$lCH#k z8G=&ZvG*j?N(@S=gAUY;MRbY_6Q;GCifEDb0w#s5FEryCdmqhM%q>zD+R!o~`&P}k z+s44{Z`igpG7pwk#smlc9yd8>bB?G8y2XLW6hQbuC1Ggxuqyx~;6l$W<2?0LDx#L| zV|w`>?s^Q|L{%zu(65K_@h2m{48c7Ai<&`}P~y`+8Lf*UD7TF-2JD9COf8**DerhU zVGPn^2+DrX_8~FPy*JH3Lu3dF{lFr{q(h=T&Oo6v1f~A1#t$XDDIWnCsCC}M?P3qD zaL&{jUyIa;}c2 zQEZE1F!Q2hf5oOMwpg)$D7Hbd9g6J_YYIddpxAW9Zc=Q8Vw)68Z7C&>Rt(mMj8~u- zR@Zq{t=MM8+V3Gx_Eqcx#jaNDVZ~yK{h-)h08vCLXDF7fSXi;A6??lArAl@X!ZdB|k#okryC&kcvp=5u>rYm-;c8zRO~m!j%p_(j8d#rvA-$yvSObr)~daf?4{T!#V%ItZ;HL4*e{A5DY0T& zj8|--Vk;Hfs@T3AWtyWEyHv5IioK)QPQ@}|Rf{6f6bmc%v|^hT+q;t#$x>{-Vvi{H znPTmLvWZmsDwd;|s63aDGR9+c8XFDF7vm9ZiC5mr`nz7<+A{U>G#G-Gx3SQ#F)d6@ zn}AaR!7$CVQ%Ty8bRvPaGHp1hn%I{fq!&pa5_F|ZdjQE$5-g~hHs-8N`vQ_2l366h zB=bqiNEVUYKyo_?7VS*?L6XNvFl02jRA*8nn)a(CZ<4%6@-Yc?sA=1NvNs97 z9wwI|P4_60<4H~?=}&_0smc9QO?N!WB$BBlGf6HcnMZOZNjb^&B)5^=OY#88qa;s} zJV%0Fvgy7;vX10kl8;C}C)q;sFOnS~qUz@shPkuHb*ibYQtVsB4(TFW<2c3UDt3=z zFDv%BVy(MM$zv4L6}w8Y#}s>4vDV$BGD8(*T>~_UgDfWqCt@e?UJrx_KSb<`eEYsI=9C`C?DY=&YtE4D(hO^U${fI6wWVrMCquh`9sty0WUtlPmV zX~iZh7EioKxN7mDq3h?E?nSiWMnD)zEs+Z4+4dOLW)b_M~XTq11p2TSh@>La)2D#NFuH8cV2F+lbrIe_Fa67)+=>o}5rBxjNgAsI=ck(^I5g(Q!ph~yHI zD@ej5*OA;xau3P4J03uY$kC@z9adOaB&U)LBpF6BhGYWCg(SHo`6MMImy%pXvY6yXk~>H$NtTd2PO_Y26^TLe z8p&HE?~`mI`I6*olJ7}&lGK1Wtw=hMbR*fHB{_p6i(~}JIFj>7vPou; z6q3v(xtt_KQbBSH$=xJXBoC83LGmogY7&zqM)D5HMv~7+9FlKIwv+q{;--?cA?ZZ2 z56M9!-AQ_p^dUKgWB|!fk}-N{o2%)e?>WhGj(ZhN3d35YxBCEX15wj$@O6ruC#n_- zV~z5MOT%U|62hNDSP2%Nz&5Y55l@#kajoGx~EIO`_`IumFdm>cSM`6)&F$AfFtg!Vu2`!BX~(D$k8< z=+Ks{DHuA<%`Vy1?Ki|;b#6y52X6FI?}g5t>zr<-jVbgjhP`r0W?$gYFR}XeehB2= z6ANP3#F21hqV?xC6;6iutD7~X=j{sd3ROWhFqHvBwGPlOYWtV>G-D_*l|%0UOwf3R z^MKWWPR8aNZ6RXsO-Pwch}f{pe@9g95XP%sRRJ=eRmB!1CtXz)HN3x5THqmfwTe>O z#BbB+J{x}zlyj|}oiL^X1Zbj_G*-^?Q<1nOi%@Os^eRE%_tx1cD#2M z^untaR6@1&EU!X&`5QaossychO}#nHhyT|SKYC&0c?|;pZUp~E=2RW;rn^gEcM1HL zltAT|613(ug4V1BXsz)Yk!xNfa?NX;g!p5K{gDnnVt?dYd{qB5Vt=IlZp8jSh}a)_ z%f3w^_D9xZ*5(f)_D3u%`A7awBlbr-qbL2>AoiQ@HHp|C?d1R}`Dh1F$;UWA{(1ke zK+0g@-O86tZsk$8_;T{Z6IRt~ZUXJ{Qv{jbW>w zjNve6P+s9|$*ZEOzcALSF;?pas!UE>p?U3@3}wRxCo9`S&0NblC)vNKmIIL2>mPtH z8V4c#JAU^7L>0tty1NAa+e*Mg&3v5$kQm1vuXF72I>#Qb{UOxM-D8i;|1GXRh#1t$MDJ0Cqz~J)T1A{-Z>C#^` zFp$n;F>fSLr*`}?fSo)B_&`?v3=jG$xaPfQQz(_#`k_&j%Fd!`l*%qmqEvR>1(eEe z%|@y8@GO-nY#cD`E-ctH_{BiJ_pYE+?gRA1B$Ue4$sjbcu+!^;NXp8o4{&ndy0Ygo zk&9tTP!+%{!>O|Ung*QQ-$Qq76maqYPpDDANgukhQNYQAngKZJBUi>9G3o=H%l$GIyRtcelZQ4JaFY0yO#@D5`dN!V0Kq01{8q&J0(hme_l>u;`tTMHZ$92) zkB0FUMP*y+VRYH|W^a8zt{xmqEK2|f{SzDa0?QJ6&VGokd4Xj)k64zs5z7*IZXXfP z?PKgUzo-YiJR-v_fEe|_{7y`~~p0QTApaSlZtZ z!LkJrEZY&m5*y=w=JeKqTRD=y#`9M(5iBnyf+ZYmzjW>cf~E5U5G-K~5gF|Ou&9Rv zz@l^Lw|^#V_<&&9n+TTuh+wG`!4kMhj=KPIG`u?-zWkAi?wfjO6?O>h5n!=1cwQ0=A)0%-9d3sZrk!LiH85wVFcxd2Q{s9EV{=15x zIAGTi6bHr;6f4&Vt0Ms{_a=ZPHaO3;P9uP2e*#!)1hAY$0LwCH6>P^DnS^aNjkeZ5 zfhNgq*1Fq=5VmqKVJq_qTUi9yN*bnhpk?*3_Qw=~UD*)lD5|f~NZvVL4}a%e46sUP zKENuS%K=siEZAZ*Jot;E-!f+VFGXPMA^?U&bH4O+kxEX+uSxo^#4rwMJ|#@ZkY~rC9(`Qm^jfB$Om28wsxd&w5zPUFj6+ohff296 zBk)oDN5IdrQOy8^9NkPn$T7_agghsJ@YoDM$gu(OW&lEtYZ?$zs{;thlHs3UIs|$g z-!#yp-XzfD1O@ciIM^WoD(QXK1lZxZO@bW)nzAXd!}ICZ);zGoNp#(83hdD9;+=p! zB>`3mE8*jTX9=*%v!IBq(EzK&`k0Yr6#=Z$x{2qYbc(I#sP^9fK(EwKIU>nO!2Y~6@okakvWEXE196KUq&KlrUI%|Pb>8t}zrQ=fWU&N`z z_IKUg&cLYzs&D8{cxad#-IFx)Ll~|C%5oC0>0rGY{?(n!L)gygSbjE&-G2j{&Qpr; zKjL2*!v(So>p(j4`dx6uWbm6D#V@48<1QdTULY0$+YkYp@bLpR~G7(R- z9};8;%3YF3H2F=Khy)pe!t*4O->zXi&aXF)s&l^Z0yWJLyw9b(jPZC`(-@Bn=n~xo z#^dGmz?L$e2X+Shsu^TXy${V5O=3J=DSfn?!gwsBs=^n7NRZy`I-$TBf`xGvYf^nk z5|9>iL+~yho@LYciXNV2)A)+lN`Ms1RNZ31 z_2xDrHT9*Z)m<ca$9kPNnSvh`|0gaVp`|Gm7rC zBODz#m4E{`<~ff-{Wu#mBcIMc^IcrM)XfDpoqId5>9~u_{}3|nI!_UG<-6NlpvMY+ z{+kW-c%7{HW&k~2&o?I6-nOBZ8G?0i1Di1Yp z!d$L~A($-%QVBd^s#Ctq0D176+)OAY8Q)2y5f(*fI|BcyBpfakVem@8?1O+RJ zSdtL*lAH$x#t@XekGTs1CeA=nF$9JFPNnGCLN)<}Sj7jSXrn-g_w$1dDfIzM$WF#{ zFa&S@fOqp|z!yHqH-yPH0bjTzAU7^g0$9RHpu!pmoJwmJbc=Npa4NB#fpLTN32`b@ ziBs7_GuGJX>5R4Ew|EYAEeQW^KS%O0kWQSIh~^wlau!umK2;Lj7{}8OU!%MII2V3i zkGm%Vkytz!cX)qc8+zA7-0Es!ie_`rws(%?AhY4>zN+Fa#5Q)Jv4I@ztG)s91)e;A5U( zlR$}&^V5iI?g~)iGWL#D6Kf7oq6D?{T4ZBDiBGUWD!HJ_PD2?m1Rw87R+%RXb<_w@ z;#2$zB}p~{l=yTY2Tay`zaK%>G6Zw5oZX{%x|guUtb_m$W&gB(Lpc4eY+0(fzE z;ay?~O0J?(YZ9OEdG-yOjZgRjTYf$Ggc8yl`eq_bzKwIOi9n*Xt^yLJg^|^()(3#2 z#Fi??&4fM4v8Mou(!Q1ij?o1+{GSU6Ix?Fc*hS8TfTN@inn`ErS;Y95=EA#tx;vJ? zFkCyI%YgTBR5po_aTjO_O^V|eR;>)~UQy7UxlNgB+ zCesu~V$>J%;lwuL)nEuV0Fxh#S{RAzF+a@^lv~3hNNS`TA;A!odL=I9H_y$GU?W#T}F+t79yf=R9A4XKM0Lm#{g>9yX6*O{1PM#GF(bvc?b;dz0Fn>DApvr4vLMg5qyA1^%}g*xRA~|i4Y}xV@8W;6&@WvZ<70vHm4&ZhH&F?)byJn&Jy)W9y9??O7 zqqGJCgwmP;I7;h!{(2JFZx;O6vn{BLd$F`vo?@Q`5K4PCf8DH?x8SPYBKuRo%@G2r z+&LPD9Gdkd#KU4|88GL#x4=T$D1^ATz}@b7fSbb>bdh;3upcJ4=r7&mqQ5jsdQ!w{ z4sHgv;0H}(3;vVuc~jVe8+{>P84Se-ogr9PA0}f97U42OP~;<)HW#y!5Cc);br1kQ zW{YbQ0dP}7${W?xhXD8qy9Kon0MX7SBXbPF41UU&xEa8EpRo-$6L@bk3$H2Qz0dh1 zO#<(ILEU#&Zll0^UwXiMe3cfXWS0b%JGsnj)!H8ILyIj`Mrn0Ys-OEz!QpFxu>{G?K{tn4Q(iU5c5C{h-((3JCEW z#TF>`m|~wP)9@T?++L*Ilt8 zip^5&X2qUY%u=k40;xMevGWuwSL|uUK2$6vU6$@qicL@qJKR&LEK|(GvHM*e9j`#@ zvK6~Vv1-LWRji#tr#nfp3lzIqv8ZA{E7o11)19N(Ws2RemIScbx?8=_dgVmB%FoMN9S)>47g^-^rSVha>|T(QlHwK`aq%h8IRtJt-Q zJ+GLhSlbLGsn}S>E>Y|r#nvb$DnFK3o-tys#fZ5UBj#F+{;x4wQ^bh579-|bjF@XN zVy?xAxfUblT8x-$F=DR8h`AQCsJ&oo0YdFX%(WPSe_~t-h!JxwM$EMsG1p?mT#K;_ z8YAXfjF@XN0xiXexfZjiy@IyiJ%(fU1d9ZQA^DbM zJISvg#9WKHZAdzi>_c)8Nq3T7Bz;IuAsIk2lw>rCPI3WB4#_N%Vv_kJWh9G8ZXmgx zXTBaT217|+z~7qvgY?l9g9eGfx-z-nDg+b6tx zZL9|*#9aqryx~6=cx5{zuP-{(YN;9PL{)|W6X7l`(rxNohtwVQI#c+>C;} z{9}ZuLIQ6#kic7_YJf1_5<x^P-X=?o-FkgYxffWX9J0E6jfQm*w{eg;|p@<73EIFer{ftX%yz)Ft;El zqo}Y1Te%hbMf70{v*Z{$sXs)C|E0E(62x0n zr!g!P-brp{g>f-uJwS_3t%Ldg!`d5&dMkzTnRlO#+Cc0_EK?d-m@^-JN%efz; z@rCz1b?(Roy#DH0fFkPu08ec-A+I2(@DlZ8vW=t;9u#=aJq8xfDVd>8$ofhhdz?B# zPgbO+jy+x-_2SVUd@}Fy(eV?WL~ovGH*RWne(sR$sn`ZnMeHX}kA#ZJ%^6pkTZAW* z7b^S6LjA((p_i|;PS(p;!DhGoIe27+Hdxb0O7Yn2J${b;2N#`c{R0G_ofPBChS6Ad z;%5B&9buCE_1{N&iZ+_UYsD&X<-XFx?Mot4SVX4ylE@TaT0Qmh1$B`r7WIX%$D%&) z^;pyoeS<|OSVQ$iCtEOAJ`ufyMJK`kqx?ep#2s$I(72~HSr7eY<>=))=Zq&<`Fi<8 zyk_O+TBUmVdDc9={CsPHUVbT@ManO;uvN$f)?&T! zR9Bt@r@Hc7Yl&Vy&3Z&HpKdMF%V$_m>E(G=wO&5cTB(=MvR3Qm`Ie!V&$ia+F`@I(VnP?9#e~>m zLR090Ih8nyIcPDVX=pK_>7r_|FdVNU8+kOpX3;MQYEo83ux1_gyqS7vqNv&~47cH$ z-RM;#KFgxa`~827We7r8tM6O94SX-#fXgArPnYe5NS${}tgicRx181NOgbqg= z2=!na=*f@s5o`lTq78(OVjJkiHgGiCz%gtC$FdFdW*az;ZQyvefj(>lC$J5i2(1x1 z30fo67g{6K4_YI1GPFkM6ljgmsn8ms)1Wm%XF_X)`a^4k20&|s1`?@x5VS^Uu&6pk z7+btXRHsDzrqiYIrn7~P#ICg;oYzFv)57?w;ihw)*G_H$FZ+MkW;%#{>%RQ)dcQ^1 z`&X>@uUYS3vEIL8z0cvhU$V>Z{>6U1hi36>QOtVJXT2Aq-or=p-9Lu)Uc`6*Sl0VY z*86PMdjadcR8$QT#@AlOG@N(8hSD3ap|34W0DWa0riUhos?NgLicxHa4z=6@{q!Lj za@hKfst#Ezl=)U2Q%A^NTNMTu->GwgzyIa`ZY7Lu{9SdZFuun)NU!*zVxwO1#`2W= zc^a+DBNv`;%AR+7J+ypDXT5ys67=v-lbx3z5U*lm_~SYIu6nzj5YG=roRt{0sMuRu zRGhzG_~X(&PCft7IotW8V$go)n|%x<7$4X$$}n*XIyh%x08)x!$Q&^RN4gk?fyp$S z(J&O@@T368`3Tb?my1CRWr72uqvHc2p67sQ5~K=|UNPe5){*yzPbK0QQG_8-338Ii z7iNvcD8%MKC<6ndIk;YmU(5yh6nVY?cgjb6{BdxCS+F0hE+9+x(HEUAs%8n}-z?e* z3)k$trhR6`wu+A`T4q+nTC~r6=c|g-ZrGYKsG|LSr@mdhb8E`biuUEFzJ1BgQ_Tf? zLV+E@B;3 z?In!uu+yli5r$!iva&K}3l)s>KMH|`vl@&;YA^#-(*;3PUC_eVfzf-#Hw)LSLmvBl ze9Z;x7LTl9JLqGKpD_?^D5s`(&NUf{2Ep^)gW_)@NJ? zdVSWq=dmEE8e>J)x>W~ZOz`|VTv4gTW6jx9o{;1#?6@u}%p{45^$ngFr&VNauSlyo zXK=;1G2YL9{*&~1z0Zb<-ZgsqHCfv#28|hWeb)Lh6~nbY8~PZ*^m`+sdVq@c6DwkO z|MYXk&e7qT1tZVuvqMGOJm&gQDSJLST&ozQ{q$X*4KnuiS(_PA{^qUK$?@yYRcVjh zRxy6Nm+v8?=d9>soOkZIIoEWW(E$bE$Md6^Q2s(}-4Yk-1))UodzZh9Umm84SKi6H zxDAc4!oI-tXDrap@M0I>W#LRh0bVf9S#XXa8?P{5szZ6txFSf$??a_T3FvG{mf$}R zF{k6W5NTxKH6|~{%UFbTB)My zaOniS{Hmpw=%M$PK1&keVT|Nmz5ME>oAq!JC;E3R{aIgh=0hnYt@TBN9P}A>aE|}G zhcfigUJo74b0^~5L#OMD20t_ihw?M?6EzI}s>Tarr}`A3$>77KzKu{uasr7{GieAZ z2#sCmECAMFVg2LHlToStER|P9R$4{>-(*X?03WJWVo6&))J9ZAQjA|b?e-0{Tg6A$ zWc?;()vJb=@f*A~h%1kKSFDY{mWeAe)${mz<26qkBM;A|su>Y7_6+?UnMs_M7kPRy zPA~j5?eg}x_FDgKe5|c))o(t2MaxQ1Q87O4nt|JAG663Ona#=4c9rLCUe@JJB~d4~ zD4kgW>%Xraz+IS+)-SijudS(lY8L+5X+evHZ>Lq5-I0^B`Fjjqxc0aH3%8^cw=Qin zw6w$4E`4@v?fMcI3AVO>i50SSkC#|C3(emaZf#kdCV5&(tE=7;NVDBf`plNQn17|N zRE{NO{4qN#IGKjBQ1$3(5>a`MSkfDfbIF-#mrF)z#?MQzzV-7GSi!$)VP$3K5|dXk zaK37Zt(Pyf;JIupUC8Eq3tRG?IJdM?FMrW~R5N~B`ZU_KsH#jge)ZagF%E4a`Kz^) zUzT}za`olw^YM6@%S5GfHrzWWudv2LIJfZf-$dyFGq}U0{}_q7xF&1M)`R7>`{i?r ziZYt!BnfzlUO)1i<((?hYsTeVpS5Ys%oMf|Ryu1uSa=m#fw~SGd){VlJ#}V^jN|9; z`jPA7Nv*FH-Mia)-p68N=;Jx3%^a%gW#(C`X4WG$b$#LGo5Y+0W={5V-+#=QnH`kS z#%nIvbj<~uEBG-Qxhc6Yt%@wGqW{D(GsVn9&-R{Hw%hwgbNlUHDzV9PO`5j$)fXlO&4`eFa>R!J4)rHdqX3lH+$>86UF&vPxM}I-HIr#`>%|)pp*k`TuoqR z1*70&t&hI#4b?@DV%5cl;`1Qr-y^XJSZlGVp4Ou=$bLVG&81U`)*rmy8*DoWndzso zmE2RlD>jTV_SR!qLYImWa6PaQYhxMxVuC7Mb@w139khO$j8yxEx;Ak)k&v`j}yB0|E?(r{J_)#9FDqzvCJ{F>786)UJE2QxTWip#7N zT&DQz-+m!NWXa7JAyZ~e$y8OTR1gN7EETzLW?954Cn>8sm8$FHsjg3@asu^dmbHCJ zGInXRO-63NIW26Y3~NJB=8-EUgDOt_&s%r1 zUgfP@%nPeMFsR>pO#iw!WW>MiRgc8)!Y6nZ^^^7W-cTFZ6?JR%hrM+*ap+YXi~oob zKV`(v8L^EKFM-;Ozsyoz_o_$ZZ+X?e_(z`B*7(N^`-EZrUiDL6g>5}hfH2?^_2_}K zyy`Kksk9y_Wtf{`^StUm4&2JHr40K9!@lEHj~`fvuxNu<{e;hZw?-d_Pig#`()tga zn@xn^^9Cel@C^}wYki&n1P4h|T-zRf93Jz_+ya8{_;SsVXKW&y_!7PuxY34xSJ0D36VRIM-n2DA8oK#JYBP;^RDv<(d?T?;i@D8LHGPOaHn zZb77IMU_mVtuEC%WPot76U)yIcOObn5@p)=Sd=!ubbh3|3xCJvYxA5)X4hu+e;>v) zmB5{3MirMZh9q#mx zPJLzPxq`zo;KHuy&LYLAf>T}EexH@G|cJ^C8ydx^f+>HD)c z6h82GlpGrYUkp1+PV7tcfhTcqYyrqRJo0UieH|HMzoPRB9q6j7vA8$*QqwTh(^P=4 zriVx15i*Yp?!K^lpX%c65|vxT=ryhIZ+fdF#t{SmG+h7K<>iH}qnh zy~S~~evS^6v@-4j2GQnD(G-c}Q3|*b!0|v}0>?iI93QSmUyf;hr*>1$jF+mH5i*zb ze_=`Z3^3DAS+01=IyAY2-KB^GBN6GH{SP*xf$$Cy+rjJd+$?HCVB}GTiqR>El;wzE z5WuF6N|2Ks{Q@zPSxzLZKdC7C@Z8Va6Kw%C_5uDLwl1LKono<6h$D} zG$vc%93-X!nF4DH6`wOwc7{kU>K~!m1I==ey6y=MYI2W4Hv<2Bo3c`!bf-#`qA^uI zj!LRH1f`CJK<(oZu)sMVOCCGwy<$gO;NXHkjNV4v&*N;Ep5218V_Nm|;SGB^lO|Nv zwVp65KNr#1k6pnD^jdqet1cIx08X4ldP_&-`G&TaP0mU<;MxeXuDY{5K$-O(n|A1GH3BvrurBYtX`(L?t_F1 z+ld5&R(nag({*1L<7ypy-c34 zVb{L^1kM{nMLJCL&DFo{4Zf*A;|;FTA?RHN^d1@+KLQjT1uGzQU^IP2*ao6Au?l?U>P7X@8yO2Szcu=GZ+%7dG2o@>6W&leSi1G)(GDKTu|Fs==s%Q`L0cNT3Q1z~ zfx2Q3c!PJvp70pOu@_Ng3_2&dO+&mPSJNb9Yx2_h8nQLriEK^lkgcf>*_wWiW;FeQ zTfj%&Ahaldp#ZTtlq&BDv*EQCPZ}5xHWl_3suD_=IpnaF=Wr(c*%lyRp?}t#B$J&c zL>Nz?DB_xG$w?HYO6 z4bMmG^dETZuT&bBD+X32#H4RSjQ6@Zy8_2ZX%@VrEWwqi7C6IXJs|4b^=llBXqs=L zw$icwIQN(pW7Z!Zj^DdR0!OJk-%&qstjwCxD#Wd0*v?QMB(0%Q`WfK$=uj-{P#T?z z0cMBB1IARvz__73U9N3q4pcD~#8R2W*ji6`MqRJ#k=BMXhbRwcjnN*%Nhz`cp8JfK z!rAS!9k%dnCrUb+g&0aHwWBP|uJ+})>fZ*m_7~L^pv~k=-HpGe@CS8VB@2x{AAj@l zw-$e!@wXR$mmB;r*tHD^ugqamvk3Idw5K9%z8Pty zzh0!rSpebr8JS9DrqSAhoV4<%A~Yk}X)@Wsc4Uci2ejiuw11E`zk%Qr+RW1$(z?>> z{w0Dk>fR*Npow{D$?BPG9z#+iuu`TR*vcCbd_v802TwbFu(YSkv=;(8#&8I+(WH%5 zU=7|%sDqS7H2V!P<~&XvkReaok_`kL1z$m^%<HSq@a`bmV^28kj(Qx0skzHq}6!eG~~~KuDV{|I5r!HnzVHOF33W9wRWHd zP-uTppax?&VNV~U+-zN>)-aYjo0t> zv^jL$(>6hG@w5%mf9q*GOaC8F+c5ozr)^R+*VE>Vp6O{TjKUD=@aSAm+ZoZDJZ;(0 z?|9lKM;knCBchu;ZD&TG^|a-HYHurw9`dw}j2`#2*2`~CU>@+ecQpQjgi>!w4HS$~z%I9D+sj|0xQ7D%H<+oQet z1oOzX3yl!qx$+A6CHWsfP$NKEN?e(+tGoN9*21O&^1Ic35ZXaSFZ9+|0@c-DtzYD= z`vTRT4RGyHP4%_fC)x*}zou8T-Msq|{aTmo$jczY+l2RtQji2kI?pV-WyFv<@P^B7nb8_nHv_YDsC(dopU~g3 z8v|lyXjzi-W7uUW$>KWHX5<--Bsi*)Xl_t%#9p(sL*<=o6q2Eo6ILHaWQQ6z>uFbS zz&8ROMPgRmt}aACphJD$EH#~V9#*kiXE4*ls`G>p#|a@hCxnnl%`7<|A?@musMXf4 zUWE^QRq{t`1H<8=JJ3FMrN3%PSmgmDIRNcy5t7-`O9O;%osPip;{1+sa#1ttV(dkq z89Os0wnJ^i_aQW(Lw(%zVR*^NvSkz;R-Z(oc6AF}{5+2Yjt-R+AbiM7ictEd;Rk(C zIPth1g~Y361kZAdfqXmE0DJ-n2Fzat!aGzSieiff&hA>oYAW6K(sOL>rDx%{Xm^LY z3u#$ehq@ba%-OE~9=@!0^(B1Z=W2XmqUZa|QMw z)A%fJXf*L^PW+2-c!)kl|Ef2Xk3)3bC%O+-tDz*1*iGKx5A~mTgOA0*c&MR>w>JJE zeSf8|AHJvqz5~!ltBHb2sy%QfeN*WBB7L*qi~7BxF$Z9<+6NAEo&UfOJ;tR6p5SMU zpUwRIH9vRp^9_C;^@i??qWH+co4i3i_Em2%3bWTiEe1K*eY{zCMGWbNpFyn+Q1G2;g-fIuB_YL{b`chvwhS67ir{N$PqE{X?Bf>9|sW0@U zQqXIqpkJiu4gDTdRp=inJps^`?JZyb(^dM9RB~T{YcF5n0#bE z^4ggTMkTRN+Pt=NeNI7LW?_*~BOpLjtc3_V(kqhP!DJ!Arx{<7@^bNr(7+2~Mf*o+ zAr@<?xC{xSXm-O3lUj}3=HB&B*adcr9PYTa&4NS z&Xm`Y@)C`i35dT;FAO_wgB04fN5cingnU>eJEx#!6KgY}h0+$piwZqJh z>$#0ebb%KubceUT3R_*>tvcS7xK@XB;a0tpzHfPq%k?$hP?LU-w{AxKA&)Uq|B*NN zcsvRq(|_&_I&|21e@|~`;#VPs&=1o23mv>?@s(&Eozv+|#>pXC>J9CVF5u@~{aL#85cJE5?FSJ4f4TyK`)BX?DOjzNDUW>dRMG7bw15t*W4Rc6K2$loll3 zF(nl@FJ2@Hrx@i*Pg_K3yi_r;Pe9XB+d$(a>QUROGs5{r0GT;Oj(4XOb$#(7%z?V= z<99A8dQ42-qU)iI3P(|n&Jh=s#(RuB9s0xLmBy=Vv~xhp1B3_YCD*nPvFtS4ihTcc zoU_0b%MGi|D6yL=w`7h<$brwbZGLX?PBWIYvsN~zuC->Ux@MuWwz{r$Z7%fqw9+b> zA_1~|S@LW760`8CRFTD^9=RyAjjZ$4qmZ(^2Cjrz#5XmuAbLxkg}%9i6`>u{LR=g| zEu#34)Lh62GzB;IL*ZYrhC~61`3Ab;&aHAiXDL6)>e7lbb7_w;PjOdTjJn@e+!dZ= zQeTr4{Q~eB^n2*sPd+w)tXPNYz^|h@aFoUu?FRM?!6Dttv*=seU%?{nEzi!vIrbu3 zgZtC4|4^XjQ^gfrfr$WbSgpv;n($2kwpge}q(7vs zJ{zFwK1>I~zp(pb@S}XLZFBP~re&>M0@~SRS~s}g57fLrbR`(aslb#^-M>!l8+O+W z@N^fVWSrq|!H*qu4(w3aSlONU^W*Ps{PjbE;U=J}Fx0*y>2kVe?j9rq-Zo{Jv;wR$_YK(=31S)-1NDv;d95IF1Ue z%d`6(?Q>m51YMAYk zq+HT!Oj65&#?7{i!Wdxi=Z{IDpgQfh}9ddH|^ngj9G^;7!PGA&j$;C@&4X zNu`>C5THX-N%%35^pJW>_CL(HzhEprO$tgdY~mk4T>PVQruet1L5V{yG+Mt6I;ZjP z!Uw|{aH4RclRU=6C}KTPoS-Kjs6^NS51m`+ETIG0)o2*7K;l0!aW4|bKx|En;rXVC z2OseUx59jpG5X*>II!RkXCWQPo<=uOndp6_8{PDXkj7kZH%>88%4VVzzcW&^nT%x3 zpA)HRrY~ICM`?SP5KLD;skxt*+d7n|pVi#E#7Ee@w^w}}1L7b@&Hb__4lg1K1Wb-5 zh-fVILW|$m;QpNzm`Ci@;NG(w%;`f_TIvX?^^mc`6QlO>Geb<8EbIn6_1WE>zyp|J zc}%ps4~5V=O5D3@rpu;jrr-vo)G3)ECGO_h z5weOBck5b{KiS}ZMHKX;S>#`Y#;V?@xp$kx+NIWX9rsPsD!bTOVqKypjOl3_#iKG=VD&Twax!-p@+V$^oQ}}~qJCoWChh+qiL0sEi{F`v);e;K2);Lb9ZY`-k zyf#m(-d|Gve615`qojHl2K%){)-$$->Q~qv$ojXGxS5Jm_#{$s3ZJ#6#I>#Zl?yw4 zR{}0=ftr^U-{ju%5jN05Y&!O}-2lhLH0z7_B{2~Lmy8!7IaWMVqaJ8ZfU#%66}B1T zQ4n60JtE=s{_m~E_#gQN`35#9aQJ&fqxQP)UkaTS49pRel@2;<(_EpOJ3+tH8=MFs zMzCCe$kTS7-sCaP(?LZRL*X!ZSJdeZu8w|%zH8~jWw`G~cR=0{O?ZQMH|2v*r8Mrc z8~NrkZTuX|G~&*aYxJbcwKILWCYR!0hhp92rlE{HL zK{D8YXVsd$$9)%SVt-3V2$Pb@wd%cM_K6m_Y3~iYyAsa;J(y?#a`pX*!iak>(8B(- zT(Sxx2d2R41^!znk;9gCL=HCJb%P>@!^K#j0y*qY%uGWLIYbWo?VaK3!xBxn_b2g# z429DF;o_ad3s~W;>wp)Uffv>RFYGh%LOcyGl(@GbiF-4U0+JBTrN+_3(p0ZzejkW2 zNAVR!D)(vXlZZNkZ9v|F(kk~Kc?&vRrcP8M2WgU`V(NBrGmg05uhOn@YNrCD0`;{5 zKry}V1NRRK(sqVoyFN1VP1vGzz4aHiu*;&3R)V>#*W2u%_ zX)bLexgzFf-u(^a{5x6EB%@rJy7MX2x)%_h9tXF*l z=%mzD$5vZ8A+i2~b}^XpR&TiGaYL>OqS7lNyM;EJ*~vZN$gq?B*eKhUzOY5=k%5(z zq;#B4-%L3hY4__oMd_jpGme*bcK=;Rbd_~KZYJ1CoYKOiBmKB#+j0x5Yu5!GRR(vD$PC2i9Kw&0^w>freE$2z6GtG zV3)p_KHM7#>c3^gJ02sf{|&yV-D5 z%6%2|zGRUB0Kf+T|6(I459>T25+NMeDhEe>S3RvK^Hi%i(NuM8u%lNh?8l zB(p<}NShqDLE-~eS(l%@EUQ(?YV7`eFDoLF<-@&=3RAOIfvw;Z^k=XJ&8q$jt`Y2 z)=rD~CMd7|8KWd3wEQ04PdAZFUF*6bk;OKUJb$h%N!09xGGoq+R?So7pvFm}z|1qJ zt55sBpg}!v9~0NxM=i3%v?CNM=XkJ&fGr!BD#mF^E-b{qXiq5Bp3aij*G`By3SK>= z-OtdHI}FE)A(4e_K=>w15bOXRyNi)cdX1CPvj#-o=9P^Tu~x#P@}{NiZLD~0wKurzqb1dNF$`58VeX9 z+#7H~p{K1_X}nl5Cdm4GX7tZ^37Xg@8=6A+{JF~&Q1q6zk}%b9SnCk=upHPx)?6e$ZW-cbkDVIb{<+hTQLe{ zUT`(u@i|L=L|zu?g%E$WQtBWj8zBe@w1}rzK^Ub}l+N@RXDE%QDsUBW{7Z3OyW64M zWuMX7w#9~eW+=wwR9`Jo4SVGqJs0PQjDG$(t)Ds6zL*H@3mQW(&V-QZHQ!Lw;PfZU zzCbaGpqH*~5xZ)6UY>7EZ#j;PF11-48GS)ABNhH_8Uw8pk1+*yh>a=Gp8;|3C66&vTn{=oIi#f=KP|w;0=BaF z5@=wmNReBzfiVQw5qT+qU8ESrsnPbi&EdS%k_3%*_@-jC^ZZ3{ZCH%vniDb(dAsMg zfF}^pn*oll=gOQIf2n@hc)DUt6Lq_^D+@{*MD-4Bc7dGtBE>ilHLJV!X>TuB-XHN4 zIqP2)@afvtra6M@t|HIusOmw zj^{wkLmfI9Vsc)fftyNR;&%G)uueBv$>14coyldgQ7q!w;G{T96GL#P5e01|Y2|vV z)8<7f3c5^D*mN0O+-VBbv|4BJ4$@l1N9U%cD((x{I?$*rSI8+EB}s3fhcBBPCEs*V zEa2LcotD|gQ#CYuBXNj*w#7m!&jBN%+zE|r6l!+`x3Yndv5`;naTPGOKpEKh#XjZ! z*j}C!F3&?+uzl+@(vo{Z^pc)7FI=9lmFL@qvP!soT>sVMkag%Pk}j3tMw;pG#CX`t z#|;Rj0_XMMUwFr(A87Q9*XCE5!}^2yQUnQcBsSY{d7(*kI05I7??DG*u6`AruaVa6 zfiL<3w2Gs+gIq{MsKx~XUV$k=FK#5%X{rua%o!IyK?dqsZwcU^j>K9Q?$sid)ew== zObC53lFB* z2(lTF5mQi&qmPJ|!k>~XU;g$eh^rRj_vR^p0U1lF;4X|9FYA2+Q4;k76g07B!CW!R zNh0eNV)`JWVK!5=B7{+97e-f5lO}l|bPkgLCt^XMCZ!rJlQvms_*oD_8N^Kf_Pv~c5BlAF7>=C(8ww9u}J1|LMnG?9iWMH=~Ix{G*AhaoE0KmcW=v^?8`M8fi1WESJmzFLgU0 zx5?S;@U7Ww^A+F**TxM%70E*m^=!Hglw{#;SHM3UOC+o^nK+M~w!IJWEs70^#KCAB zV!9*D18o5CynHT+!7y{g;t4+%nLuecM9oC5#1d5f6?F$oRGStiq9og(ur8xEg>p^Iy_OGq+cHj4#Kpg`+fnXftLJ zn0Tt%Cy!HXj=FlZ%@xW;fxY#w;u~n)h7V$r?As`|e;X>XQml zP%b;(zYZ#UbHCHzC~|H4G4F9Die(DO0Zr{_$eX7j{>0#g6={WmD!XW45((#r>`#%y zIxrH~fb1_OT$+rOj3)bwRDJTFUbP@HrzociPGObyyA&U(D{4I||_rhA<=YiOIm9CQ;4g-&?^#Aq-*XqBgubca$(_@?hjc_ABTIw;}2XJ@)^ym_A z{lWtfoXP)`_XC z69;6SctTbX{nkrSFRA<-<&*tA42U?U+odaD3$%A z*kLP$^lJNbbqAL&QHpGNSYVdK#E8T&@*0BnM)8{x<=7?MY8wUq*%_{yD81pT$>D_s z+L-%LR=6q~`dlbATs5|%l#G24;v!&axXRgHntP!b5k>hEVtAO)$9wA+;m%V1^%$7Y zZ(!#EEp5C>F)n9&uNB&++bfW^r3wzndmu#uys16vdZf*@4R#3Mc!K4Lr46;w0a}WT zB4*`3CmpKj4~J$V%uv8NkE)0ldV@psOFcUu7YvBma7K@x`kX_>hgo1}rmR7`67BTT(E22g*=CBB@>je;wicnlWQ{6#bd}Uo; zWFtND6kbI|RE%{!Cwxo86;5rW4+VxRvN<=} zPA;?n0t8bA_k$PlLhu}=u~aeKXw7xr@Ef#g)c>{jrpyi>*}?g(o9G`t8?|2H|9j$; z@DY3ti_X5neE5qPfl6^!b zK2BB@!iA+R3cI{_S~fJ!685`3?hR18I1j8Ynj`zzvIl_s{*EoS1PmK7)FiJkowr-b zlKW6;EmWCA)-7V?Cr7i~DZorP*l^L9QYq|7;KU^Bs=$m45}zj%xAeo3oQUSd31IDO zOVdh0k>;6?ySK3T1ha0!pJCFinsRVv_DCy*%BIUf^ut_H{9KP+Ur9_Irn8Ya=TUF4 zR*ynxLv$U}U!?C%I(W|Q0UhSJ?~mg2f%h+PtNRLb$Tt8pHB|n!q55y&%tvneSX`C>n0_LQ!1vh0tph!bg*eU&e*mPxykS8QuGCmm~1mVZ3GYVZKKAlz_@-r{< z(~u-4h(1?KN?uQ zHUk$vL(ih5{_o-0R;4=ur-ee-)U0QI9wQ8jh3*P&M7rg$F}J5``x1DQ((&e`^c65Q zP3#lvXTAv{cI_m#^giIQs%6KJ$gITFO72OF%Lo-2X@*ei5zq}7$JA#n&tf5&KwRZb zCg#OFxiBW7FPc3yQk&7n{~uxXe?12!m;+k+)UWdFY}aq2^BrIn@KJHCcpYr4=^LmC z@grU!a_%iDl8jh2izFhf!PXr*vLIp)pwX{MkXgGw^e~^k`fW`viB2 zWBuOXuJ=KGUeV;j=lfv27)@V7-lh_i{5~kJolOu9RWz-qvw;q@H!7NbN$0=emZGG{ON*6ES_s1u1b$a_z~_!-i%tT*bQNKv*vy{?0!B{y?Hx}WyLf5ue+n07q|tg=RG&AGw?yy zis69|(7acX3aTUy-$bbCVLUQ6IksA~D}hapA1y<$ie*d9v|)QmyINuh?V7Q;fQ z;m1FN8um}}0IP~ej3NTd(=W06l zkZ5`UDlojo^cE~q!g(7`6xNVqQJ|V&w1N(dLN-OeM)jG8yur=UAH#>I28}BY;MURP zC=3KWAH!|Gb+G~>HoUI0K2}W!g6xWzpH7fYGm46VSi3idHxk~8{S{7A9-W&}Thkqg zZK|aMjNH@&v#|Fz?M7`)zlTrU0KP(TO5O53?pcslqatb=6Xux%sB8>D?3H^1Zw`O`26f#6{bFQ15)fPyw%I=F!;+!moE5>G&JmKrr6Y?P{Mpg91`ZqFm(gN}!+ow@=8b>y|lUW@4kzJV3z&B+$gCR2$V1@gWHw}+;J=0T+_xCzhI!UsM2O+s`G znL71D#9ehBYGKjaV$YFxFkUe#+2k!~hcCB5{95sdzCfc_(OOl#MBr$D9vIC#&O!MUp#nWaS*|+m8=DOXfr0Hcuo;mc~<20 z#hsC>it@FPjwsP2vd~@IMmAzRB{M7seNuOgGudi9>nNLw6zag#!zMo%09V6a5;(We zulWWqG1OrHh~r98l|rHTwa_uzK+4cJGAw>g)1tEHqdaZ*D~*>a#(Xh7vo?|@^i69J zzu-vO{Dn}^v8+Fsd<&eivNL9ndN~uZT6k4{lqe3%46{9JBMDy?$KU^Lao|irgoSyz zwgG%Vh+zi z>QBLBdV*uADN9@aZW6NKw}5551dXM1bU$LHbTm+N6i`W`)fBPzBy!Tfa2NOna&7&N ztv2ZO@+PO4%Lex+ftpW-x;C~2{$`_jSJ$@6PgBvN*gVL?lNq$0L~iCcbZw(aN$mS* zk=f`ZEhw5CK$A_$?7NWjnD>llD{i|(P>mv7;2njXPbHT}n) zwjv(%#>aOeNxXwOI*|h=y=WisCB=Z2rdSqy3#Mn*JlNR$IS0R=SBu2(sfQbp(f&z10;nJmJ+7SPUbhWWPOi3ui4nVkJ9%G_<$em1~w1$5i+kHzIsD}zp1~@?!6CC!bA+!wvsb0rZ!E={=hgeF~%dJ zIT@=`o+M{h^7^`U6rq(nN+9tHJ_>(oQOQ8HfC;7*6(0)UQRLLh$A%$_(#oe|XC1C7 zrTB6rvY}gxU-jzj(kCc#JJNYZc{Z*a7y1ZuvLdlKfnsdxTBNA^a8WbsFRN6oS*S4yw=A(d$ z3g!pl`Tbp(IkRG@VIC@;R)pS+(!5h4;FNu>L`y!h$}f~CiI~RZx<1$`O0f2U5;1xH zyr*#kTVjZ5h)?92_lE#0{06L8va!Ed=d4r1gw-Jr(U+Yl#2A#E>?BC#t9 z$#O1iy6${D`d^-%e-%bk7n-P;npIG;t^8P;7A4BP8#SgBQUVQ}K=2MK!1?8GzScFb z?wz$~mCfjl;4%f!W*?6zIeaz-x0crTKyLm{X?$EUZV*it7>#Wx>2U=E7!e?r^R^0tfl1!F zRr)k<-9`F!co<#Bvz2#Y9_qfR2gEz+x?SZ_7Sn&2}zGn#_%h!vvG<#$ur)1Y?Rf^KK%-E>etJk~0e>+haiYVgh`pMpS;7 zdK9u}tr#jX;v*E@<*mO#pXjZ}3$0TW<5nD*(7M-r1ts6(p!r6d8nYMfPKsFXD-ULIG{()G94bt3)7R8xZ~?0vQ;6J>;hLz|N)5C> z6LqDN2oMs#M*}U@`T{!jVm9X~#%-e4)#!6y$#ZNVWYgG-aNS6o(*H;)0zl$Tlu`b% z+5@7V?~8%xASuFnqp(%;i#qwElK$tpKXdvY={omj5)d5J6ETT^Az<7+_b8yr||_^iw_mWfHdmX=^}RjwDa`kXnd zhd8SZn9ydjcg3uNhN6-);m6YHD;J9Cv$|ZtsYnXkDK#?M9YugGSMUM!dHvPdN&qdf z?$VX|Z|E)0u~GLwyVMVoOmso!ON_&sNnVHBedMjwhZTy&limLSX+>0GGl6$F(7P-_ z{q-(1ml`DA0@=DVtljpqucokG`U!PwQAICQ+k}jhn=`x{6 z3LR1y+rrV_r9Z-x{!i$`31*7^8;@~Y6bdiJ(R>nJAit(WXJbE78kZ@?*DcW3)Nw$G zB|W|>pv7_8#_9Ny_(eomn?JUBru(ckgKp+HU7gO&91r$g1jY~c@vE?pi;ZwOnt+`` zD75>&6c)dtjH%X^d`4Vwj*XCN&oG(?9s};xd_$BuYvVO&tM7~k@tZ0nZ(tM6 z?D--)I39PH*~zOUi-Nx*PvO(H6m=s}0T)SXA7x3{M$e>2;a1+C%?S_XqGN9hkXp_SN?hvE{u(>( zbe9MXHsASS@hg_rWVm@?GE4QtI~@}4?!d~!Hj%mOUgj~dac{~pRvzdOu#mtHEM@y7{bZT6ye*josKINk!bN_xEyYSDUXZK5 z5dLrDKjILs`6Xx--%y)xhO&PA%frN^{((6XKh{ zi>J+|L*DDsLp&aW=Qt&f9eX970j<9|j%|A-jbh%SAM~{0!E{JLpsb0r7%fxgmRwoj z8@}9Oz6~fB8*b1p6&AAnTo$w(32Y3RBri0}`$-Z|sh1Ba0!Ipq42KU&5f(|Ko-THP zJJ>i%3ZTX97q&5R2YvFTyq;E$Uy)oP*Q3`5;_o!AUaf)3F_7fYL$@SvgG9JO<$}<4f*z_%4t*iZX#D zE5lTfrUe0N=Oaoc3c$xxL&iU>c>Lo%l(9f*Jg%$(8{W8Fw0tdF{xAzLj})*7T7#xi zc0&HAuUOmJpwb7<(xes)gSB}_)IA6l148}`5r#>ZO!64_(oi^*datz^wN}RiJMKf@ z6jvQ>6;R4P1;3If>I(9tNo!q#IpGS@RzdJ3SMVS*s5^Ll0rt$oTQ|Uir_L?IE`|PK zCepU9t_+oF#GzP>jkti4N2Q`Zwq zYkk_*LDhA&(wS!tsX)WEF?3UeP)^@iF*Rc z%w9v2elV(DFuH`Kc|DpTp6v1EL%;|KIYH8ln-G{qlVK#e9oPQ5LRC#%0WJojGI!>5 z1`e*x*HiL5ms}i0zrh1E)d(@7Dn<{yxIisP#AzvR>lxM#K5n@JNgeJ4S1li5MyIh` z!Q_Lec$9uV`KuRsLoX|hU<(CVs>$RFe6nO!*3(O7&CB%_H_XdT?(3iuPEY#)KX@=c zA8|5|Zu(U08cO38Sw@{0x54-cAHsw~3(w-s0%l2dI5T8syDym-A zVV>s`YpnY6M19~v{O@Z#H(4Xt4&XZUTMU8$?1^C}d*a_=Cj6(8?N+<~HD1D8!Aqzk zFQHEI5F6I_M1EU34@><`}Lu<0;Z>cS(;h&YOf=ukvNzakh4XV{F8 zfXYEwmuHO!P=Z~rG=B_703}$W<5T>~QG#FjQIa2-h?{B+R%~QOekQV z50Nbyl59!5)CAV~ZHj@_At0bm60S&e}wfUyf6YrQ%sxPjF^guJu`p&o>!sk9k zj2LF~NpPw~@y->mi2!Xwl@WHr_VV?=^-5!0F&aeewC2TwJTReF3E^2Dg=dhX-Kmx3 zq}@#KAd@k<&h^C0^5@|_Srw51>GMSkK zS6|YynTiuKM7W8w%=&%9tjEmDa?o`-bV5lD@2t-O96{?L2OIRYN*vncHH7jC9B4xu z^h%FWuixYib;4i>KJTWpjw2L=zLkETH}nt{yB^e^U|5V{PkKYI=>N@ZzY#-stIY_T z6Zb3zPZ;Bk)6k#h&x_Rb;r9~~m+7y8;7$#>BxHG*63w}sWznZdbzI=rYdz*BV^^uU zgu4=ZtIQB>w_ST4VcJHPbk?Oq> z_p{;)6IEkvWyxROCKLixIrhsqgLrSRn6Fa0pDkuHOo6E=17EGQdfyIvki~Bl?}U9{ z1U;|a)K}aFtHZ^w7w^P(b8$P|XN%jaOuQ?%&LneZ0XLID<*;JjZc*YzoHt0DYiW-; z42z5aU@xa<8K-AGLHVb2p7sXc=40^>>bOSm5bq{5z<2{D4|=z1bQ+xty`fhRd$N~obXHJ3*y@LKs~^Ly9t-I;rE$7qXjbNS^)Dc+ z@PvADL`@)m0t*EyMg5&tDWko3u?v(6PN=PCetHrY`aWpb06-0HuZ^l-3zD_83~CpBh3@k3^#MwVGVAt>DM}od3$X? z;7JTA4baXvf$VVA0;uPUU<3S4`l8y_*PB z>i}N!2wpc4yq3_1R{$P@Z9GK4gyMsE_z<5^*PH-*ivT|8Y^r?XYt;RiNYk+Vp zo_(nMWzLM-skY*3mD?Gq=wbS*Lsp{n51gX%umhvWQoscBaNod}Lk?)-PUgMyuxs0o z#MQrde6S=SE3W{Er_GZ&lgARUy_;TJsVqpk;_BwH!72hMno}rpLrP`Nx#$vGw))WV z+EZoaqAp;|lG#132S#HaiQZ{tgf^UU zGXJ!;BVE|WOl@S?FkBs1V9Ljxlbj{dV%%q+!hUKseb>=qs`h5_>#$S`<&?uBo?)_# z#08qa53c|t%^R6%w3?iVf1lbR?8u@9IcSSaSjCjkaLonY(C_stcsu{=#D^={zlcDO z9spG1Gm5d{^LA~)ztXK9`@3ZeM61y$V3)zV)nor-Dh*V0WSY;_Ev&xZ(++RBemgq@ zz3bO`$8|4s6xppV#dt8ITjA&RTUr*uU=`_r85)GxyD#F2S+S z44O9`q2U@v|FQcIH_Uu2k)<6+o#&y>DWcBnBJ*G`AfwKKLAcTXUfP`Els(I1JU~)q z13B)u>c8YCG^#d;o#!F!Jd%0!xixb0sF4d5a!AWy$OxAwQGmJ81$Iyq+Lq+sn4F`b z-q5LhzZ1&P-&2f-#b#$^pe+D5Y2iaYHab^L}mJ_#4^_zD)_kuJPU zhHrHTU8P3o5z%Wgka7!gC{7iNb72Pm2b-(-CW$8|Ky0Z1{$bwGIJ~`AzW|D@pkZ<9AIt(m%_P-X*EUsNYWC&zstwo%9MEf#+jXknr-6IRenZpddtzgT z>h$-`QYZtsk?l@pf`}|nq=yxsNAbSdY|0|IpI>1&eqe&=E+7@Ew?SYAjLr>vEOt(F z!-gd-T!6#>3!Siomqv7h_cI1XnTMxVn2w>Ao9KV{k1=WZkhd|Meuzvn(}F-x3(yvp zLQ~qo8-Df``L3V~O~hqCS8y1L>0ST_usUXY5gOGp4Gwn&sOpdd8mM*H5XI1zi%y8j z@}EHi)@XdE)ihYI7VjB|5}rjx+=xG3o+sM96b}hG)XI+?%SUB=J3HKmP&iaS%N^J; z@G1hyN+1@{0DWXUmzq@vsL2C+%HezNqX&`Ihr$~+z%LqlK0?%Hrh$p^NaY?T*dVgV zVjh>7U{cczJp?Oe-Sar666Rq z@435o2_j`TAI-tjJU_uqSWojD1wRlWoON4j2QI0_qfdT;;Bexr|irLKRkHyTXfegy9kxp~pMNgg#zV%<_%dehI&TKZrj~r-RNOIQoa)(1d8VH}pmn zg!miLEp$M@y%8G$0Z8mL#2!2k>0+hcy7dPktocFgr=ZSa+Yu3K_l71OJczJ^VCnwg zeLI~TG^lA58q{>EpyFVF^@r&ghf+@8*k!k3wuIiMc{UKhPn3hh1yJoISVqC&VzM{% zrqZ}XF&;w^WFuM_S4d3L)gs6|CsXbs3>JK*ahncDk#mb!a8TREvV-ooaC5Z1CO(B| z;YP4WO=O2-O`&+CSzLvFTp+9UyVionlK3h@Z@6+pxKa;SZVp#IDNG*{eNA^J(U<#J zN2x%0pmso5MyjSn)P0z7(M2)=e+E&7jDxL+>gM@c#WbyI3R(?;2?5%~rPxYf?usgM zc!tFFi2H+tJI#Q5gZqP&%%u;eai(Y;>DJxzpCG7cN}&0_>9LH8Bb#Uk-ifa}1MLXx z07?d$3Ffm=ax&P)A(@}OLlxzPQuHxWvxVZ@hr`gA4`0|Z8k`xp>VRZhD*p+w4OiU!Ke!b@IDGa4y_dP~Oy z@X`*xBVJQio&usfmR1v0N zC5*#-O)JbCx$fVC!g|Y=;PUs*WaXGo^Ce{!#vx@h%o-3=-71@2m1p&$yB=8sJ1Wo` z*wBjLi4zsXrvwG9`bjHU;x@IWS@GSZ*0dh4y-udL`vEuKLPj9F%|QLF;4c71unR2% zNNClYlbO#>rqeUhEfMK-E8Tz*E#yiOM1&mCJbb5y!y3_}Xzjl}qLIpnHE)r5cRy17 zaHR5a6V$ZoMy)(Yj2bbh82Q?}IqH62YFytOIIL>zOvV4jR03<@`>b=5P%*NOC-HoCYX7vpRq}jTC7+ip!xmvq#&;0b_GobLY{V$%Ke~&7jw(Ad!M2=_?q}T_h(KiE9>;sT%J|2ZV z-Z@e5YRaRp<9UMUhu&ZyHWufx7%oMwie2KZzcIGZ8#*I~*S2nqHPYEl2Z_s@aB;A! z36h&Y(>1&WeWN!txe1piCpI-8Z_`GkYr>O~tC}EkTi^6DK20R%Fu`wPqPG?#=^VnUyA@{PT=(Atm;+5PKT_EP8k!OFoexP6 zFvX=K3w_^4YBB>XvKZTPwWJ|HT;Tz^<2cxZT(Z^-`9VOaPVm@hJ!dyIYdqrOJ69ZH zk1z(NZqY=6c~RA4{_p1BpP9e2h>>oGL*wel%^tmaDb+p~7I)n<@%hb!dpSMRms z5v-kSr((M`9~dcd?^%bFiW%Q!uZ|-=TdnC-thcW))0|`9Orn8mlqRdp!~-za0lT{H zMqmLsl6%BRcH^{?zD7eCpNYHWtYrDW7I}nQb2@m701LtcYrdmcGH*+UjwwovN>04! zz`5mxnz{er!Y?Q~3pph6I1vdv0c3@UE`b4eOkJI$_;G21G{>C)dtB>^sLSBdymBWC zKXw(r0IsS>c$&j%6+B+GeOzP*DFe~fh{|N)@=FpEPgrGQky$CNI>&D3j@#magW63R zGm*cY0b(;whJsqenJ{z)&V=Ug4l1-V0eVag%+CLW&!vnWB<&vf{r5DMf*@e;if z4FSPK9|VHIBR_3xqpvZn7fx&vP+@Eq(#61zm=IfpB(d9Z#Wn_O4{ya_?O}54FGOPf zL`kRMov|bdI6Qc|CYyi{QW=D`H0TPaFoVd6}V`Bl2pDJACWrF z?IBD(fO&)YtEb+oF^T7{4B{C}X}~`-gy!!?pfb>T(A~pTk2!p{cL^2!z;v9X7@L6i zn*~+m%L_Df>$Za#FdK}gf+?ms%`{A^i70QxSCq59!sN z=vB}jI{Rv3VW-1Cw6_#iv-6>11*6zZEmQGC1^g-U%N2%F#$e%flonws`#VYt`O{uH zof)ujcBK!Yx{Ic$+cmodg=5fc@#4>3-o5*Ad@h~&bZ&%0+xIue*U=ZE z7TLp4{fR%u&!2efZ;$^Bz9@KVMNp))8I9&7#l_21qDfQGha|F9*TI5$-R)>j$6P_% zzdLtm*B2kg_aAO7az19_zY_IXWOAuQ$aw4tlq2{=^f`P5W)e(a-aGi)hY=HyoVZ1R zf`fv!O=_>~#{$CpHTQuobnZQ5D zHQt1Pp4rN#8~BDu=q#!aHbQj;clp0cXQj7(sb1#|y`?|IPaI2^f)0ZNOI&xstRG%< zf<{Vi6zJ+6h`j2TM&}@LbiOyJk`7xM#bp6#_noa6PbAl~)qswnWHfP6^K8*0xNG2R zjqrxfRvJ5O#!pN>)_#kRWpc5|-Z8mY*E@UddAP^6#*sXH;Vs|r?3@Z$-C`uuRyum^ zrMzeC2$vQ1+OvFTces1FEMSA*eFRj057`;yaFOU=$Q1&*qHJ8Ry>O7ELa%H<((%1^ zJUA9ZPddtSF6b9)vJAUyU&$68EBy|2p3^2CqNFbi*G(Kg`oNw+5;AN=5+@=GMMU8V zEfg1oGpa2WpyJxrI=l=_tN|T*+CH#D>B)QTHeWgWTk1pi_v7za6&|yaS#UUhluOc* z+I4F4@~_a!BpWRmx*%_9hhuiy20|;H17FjyHVVG=G{$561gwwX@1hVHyd1^;R2mI? zL%+bzS6_ahiLp*GQ5TtY$%eM2{a+4a;|M%;hVq`@|~^LICupP z0t6G&vmaJ6t;VT+%uOGh2lK(25iphSF)r3G!3L`|PPG}js0L~X;Esl&da#llbG0wq zRX-Fx!t+p*F|D*OMRNlm;ja^aR4-BS_dNb+f!dGXtMLamEG|WH&QVnNi}<3FBN+o2 zTPbQ{#FHW!=bc}ItW?B>k5rN{lHd^<-U3MmCho#*u9fK}C2mK^t@8e#00)$pxE5iR z^3Ipf4aFz(Rrp!(GWtS9pPx}kAwtFvD1@dZk`)UavFUatj_W3Rd@`N0=}e__KAj8c zKxC=MAwkgN(Cg88MO%;KG^WR|grk4m(>4S~586iRPk7o+)wg=u&eA(PZRhBHp0+PX z@zhL7v;amSl*S(@MpRA;HgcisWunP+LS9fMRyYO{m{trxx_WAhGZj9Fs_OP2qFW#c z=iX>CoiQ%59MR>xu?`f0*|h%gktjR_bFU7~ntSzg=m1&XOVeHV>htJ$0mHOr8;vi6 zgL~YebCkwv#Q<^u3KPn|>T4*`>6`3&&dbXOV5U8cO?G=JDru>JbF$W$W`(V`U{{7d z?<9{Aix?Mgv=3pz*$X($Odm1}N@$L)-AkQ0Q0j&Bzgd+8cUS-{GzQ8pO>cu4gI6FU^fJzM#V$ACCq= z&OxU3jCc5uF0>!0&n226n+?P+IpWm+#PzmpPWanO;{luTlqgEPzWBBjt)GOZas8T@ z)9I>z2q+Pla~A3YPg>cDzYp+7PFS`1$L(SP7CeO zGX=HW0R%h|%Z7Isn=ut|pBMA<8hjpLDF<$V6J0?vvX-$MJ;pDijZ6&K{$=!6 z^g#p-3i9`g0U?7>mVly)`5MwO1;-`<&2rquXo1agjuqGnbjMfbaV{E|tfUL~z|sP7 zaKbidB8aZUO#|Y{27{^@lq8R751IV3884X}vo7`1!biZ$CStia@K-(rPuqC~QiKHt3bK&{FNqc50qQ2&zi4eX}Y96Rpg%L}IL zQOsAqne7#HqtCWy&EeU-fD7zMM0-+BySN|sGE+FO(cFwnoZmi$+_;DfI(gX&D z=+s=*W%6*kH0(YasXQ84fD?fL9A4O`pk*75N4PlXfR@!#Sjb#4Qf=*q$LUhn=SsXG z-ak@TpAFvbw-D6pKFW8{iG}1)xC(vbuUP{H=^S4YXmB5v#lDNu9Il62C%X?1p6X zF|{R+X$_o3dNx=dP}r9UsP?s~$07E37o&jLb(qlNzk|@?Un@UJC*0fQCLWwH{3*Dzq zg*Cj9(Qo21R}=>%d-Mrxh6i3^Y$u(+)A@wXKj0j+(HTN#I2};uTMvS8+DZ$_oybdxOD)Rlr6EZ^Gd!_6?l64u+t%co2qszk6^Kj$W}}px)T?Kwae=Avwt4s(VgCz1mC|>t81)$y_0p9m1aOdt|Ci5)8N+ zfVhPj#k5(u!1NKb4)z;?*9^7-OY>n-oA2=8g$@&2zb>RQwQP7Qx!gK>OTU~2?)4G- zP5To+OhH2T@0uaVI~!r=s$~=LJN}L!_d@)^x1eE`Wm8P zL~5D^CiV@&qL{Um?m{9&e+IO$CW(6iDrPn*u%*RhGl0u>3W$b4&0)m{kQ0JjZmI%K zN^4Go*Q~=gQs7i_E0zJ9Dx3aC(X&0qGdg$=Tl5Qn8sa5DA5y6FMn}RIMc%LCG!Sw{ak{uG3Jf(e8YUy?d$b~>GoqP3qMrk= zqr51TsB@zbVfha_@EJQ7#m6p3@v&Qh(_*VoXbeon+*p*p=TUs@6_gVD9~9qY2Lfz5 z18A=aEUV(COAygiO~iILV}HQdr%`#+PS4IGP5Z?=+5*{uk&97~Xa}m{=F%5KBq$Am zSJKu~2e$jvz`25;h?Gb~2LZ^;$^Fn&i0gh7(I-xLapr`t%qVj(^zgYSya<%Z;1ga^ zd-4hY7ssGYVmA|b@KP(jKVu9YMzk0MNk*8MSPt>vr|z#J2Ur?plPNHB6^WR=2uFxH@Q81))-DiS~2Q4wx%{Tk>kjp zd4e5?{69no1|4*_C_v7^=No-0oTiWdL;M~%`u=}1`ZpodXO2DzPVIHjssk|K95Lc2 z9p;+MVOBOhPF5w(c2j{a^jeC&Kt7Gse?|6nJJ1D1VPh%Xm>UIdxh)D!a5ee>&{T9c z&|?&wkgpz8h_J@u3TEs)Qe77#9p7IV8zY4k!`p^x3@FYY`v|@!U^jmg_%2U3-Hg2N zqspyKc$ucYi4iwVat48h_kgND=iD~Sq`d)hry)n(o(Hn4 zWgHw9U}`;dld~rZ6pIAg$9e`-72rkCk_QRjuHd)L5NloCY-O>Qs4K|(h{STZN8FZ91iEH(@$pTpeLckmopPt2dB)?L9>b^CUr&s2BLNDo$`#6$blaOe(J_HXB5X*3>k7R#FBIvy)HaVcPwA$$6(p_2VE z_gZ<}f`*mi8^cU_12ZwoRreiWz!~oZYCckY7bp8r;_eYzHRh2WS0$1gx$BXgfxnU@ zu(op8j!i>x5&?4@(ofOJT}z@9p06LZ5RHTc-)!;;U;}fvR!s_mJ)=E{#8k8emPX(4 zjS|WYGKRVqc#UO1+Ca^p0YAl{xmW;13aubCx=+Ve~NIGQbFd2@n(&G(uD~XiW%pkP{ zM<-8?V`A!zY`4_WQZ^+H0@J{$5~RAFqYV04=Y8fJ@8Bi~apz17ZL)bf1I20m*X~>=+M) zHp$cZ|2+^D;s4(=l0EtVeP3U&kS;nGKl~kc1p!c~^ZJK}!VoP`vQXRE?uw5X*4D#a zF%lzmaG(Uwc31osgF#YD#ap*-GQ-$Rr9^gqv}8ORlTF5t1K)LbXK;xN6PH9zq8YqDG;BQq54V>JS|AB8gxj^?rJ_T6@I{bYxNESf>Ml;__QZsVu$_eArm+$pc z0Ss~hTvBG@;7pX2md#x`4grh793Vv#75QBjIKUy_z3||LDy>D~3J~1bI8JXwN*?YA zYPWD#9z_KVvU)As=ipxudTQ{xL#5aK3_L7-ES5s8K7u4dAc<`ZI#H@CHS7uP8Mz>8(aygc|MQdv+_!9_5d*8BSf?TMCezvbYyC zkg=M^;-shl{-woLs4V(a@t@z2&vT528AnE+WAs|^WoAYOcg6U$?}JyV4{M3_hvX7o z)>xF?Wj^1tydXz3DBS80(Uu^q*$Qgzv{OOPyzC0J=4n8}8qY`Jl8+c`WneoKh~e(o zfMTUSp}wvhv>2*k%05cn>JAchf=!2$3fPAN4h9f;>T$nSl?z*ecLJCI@J>Mh-iT;? z&gz~2R=dqOmFRz(8##2W%l#isM69G|;2(j^)a>JOeLQZ4=ApcFwy`zmgT;s#dTx`km)a1P*#x#57D&dqdghf}`{ zoZkl?<|ojn4M3loZ?1pA8~+d&yx2MGZiW4bk66)1)o|9m52YbS-DOz-M|s4%r9VVe ziabZaG*sSZ_Yk z(G~;o#!^;u63ml~8nx#zGEw@BKfo~3s?=@MSRrIwK}49;B2Y;&MK|TGyiy9VO3H{f zZ^}#B#D=21E6GqR`OLlHFyA7;u$^7jWSQA&))wrtwZ4XMCDWX`7hIc1(9(myM2}Tr zP_!7@sToJREb&s0KJ*lg}_ zNgM9WlJgnNP)gvc9mLG3MgP7X>n$)rx`0R6aM^zAc@ErbdRgPfKHTCzGj}%18=V>n zVBypyt#ELgDdQ%z)k?29F~EMPSIX|lh-?FO>fd0b<*!LxC}dmHWJO0~VEONz1=TDARM3s$tVEFVJ9tv--6HEgbf6C6wk@>XRr@wc9uF6 z+`tMOSv9j`dy_Ro595dSFd|z0iq(JQ0|bJ)iDS2kEM%J^clfFe_KGXWTya4? zp4NYZG4Eq!={0gt8xt48i9qyYbfT2L#bmKxN*{C}&xwR^@+fBPdVmk3?w>|%iyrcX z4EY`u+_(=;YRG-t68Qd_A!HUkg#N;g5QVHEMBH#VtvJ2cleeZuBzdFaMaO&|K}ybN zVC+eA`Aya%3g9o?D)76njaRbPi$?-@3wt+^z0W{JPINc{WdRk849LXV%ycR@*e5k8i zxrhywcN+fjWI>$>Af)jy@D|~0M1S^RN!xgb*f6F!`i!>eqw@elEjy?E;0MzQ)rMT* zfQ9!a%!+fpBblEHM@Ww3s6nkxOY|I}XA0UaJs@FGPG~}4*wnTQMY40!G7mU8wLZt! zE~fAgRvV;vKCsqZ)qZG*srVlldaU{jIZ^)_fjnC8NYq#LVdOu#Am8EV2fnIAZ0%`@z z-Z!39xY4>yXOeELh6;c0?c@zBhsSSP{9^SM@|92^rG4KS;DuHfL0AXpCmTd^pV;E1Ws@wkiq6T2g#Qow3VLr3)< z6p#-Q9_Ty{{zyIqUEje=3DI>t9rOyWW5friz)AB_ulhmdC_D2fM z08WG;P_L)f1|E@|1>Z*dX?zu$-WA5@0-sT6ixaEa@Sm|Dv}Kg|@LCa~Z$YiIAlU~W zHS8olfQZS}V=Cq~Bf|Y+UV$DVX^H~XK-JJ(BbjF}xMWJYC$xQ3#@)lhosl8PT==c^ ziL&0_v>>qqn+B-!cj$xU$(+OjU*!rOE*}w?4NV+CMB*@kg=wFXI|Kk`*p zN1yanwMC))aBTGVa71a`PAnWUOG|rCMG`t$(>!P**BNz}IASm9^Mgh@mE?((g7Z}V zCw2Tow0~PJIub5F!o>$0o4|J-iSNeH>D7jyMpAI>#Rq{F8!?3{j>jpe%VXR?B6W;6 zFnwb_9G=}4th3Mps}TxGkT0QV3hoM+hCCg?H_!l=JiMkQ18-^{mWX4x7zyn&s26H# zV0F^av^p)4iiHQDrPNQAFaKK(N>Y=E)A!pjs8Q8FalSY95=3}0lauJ1##1~<8~ zUZS+dOO)1lDe|PRVhYvSeiu51@id6peTilRULtSmOHqd$T0b^oFLU>E7Kvv;VVJeU zpoDoU0HSS<;WQU+i!0C?u^ouyfZ-%2KwCluLm8C{4Q`(yk{xbX@!Cw>p-o+nR6&j1V%EMgZ8m2x9)JI2%M^cvmnrD#|5K9`q z7)N1<(zB(DEhwb=(m?t}<0w_49u!qvK86QncZ9fN9sXtJ8q%f_hl|5rq(_{4llc!s z!n`J3f%rpIu7)uzwm75MbP32)P-;S9BY~7RjqN7MUr^MiDZQaKBcX?E=;ceaw(%`@SQ#OG<+@o7^mH zku@4Cz`8gWMH0h_;qOEI5hM)Nf0OY4cm&;!KW`KhzXP8q6s?FOG@Pj@Wd7sWl0B0}^0KQQtd=xKxuocJAF4E95Z-=id=)kq&DXFa#g2$ml+^o^as1G4^>G|m^n@-Gb7_wyVN z)<_i3BQ8LFt<+?SAJ0LH>{H_q;#2wRnC}C9V6>@w%fGrsgF)y)%Fxz*kfUE}8^7`! z%1EANeCs@zpm%}F!uYW zA~6V-AXh0_sg7&Vhx2Rfw7_stnTeAb@Rb)2HK+0kK+Ivsd#a7u^8_S-X1I)W9O-Eo za)>wngs6B24U0n9qLJV9;R%fQB#||f1y$QiU73+(Fj#LO^qw7iL${SmxM}C`IMuFG z@(oMjJml6(L0ztjs(FuhKhvx)TM+V>@0i(ASWV*fzm1L;#vzScj_uxASWT+79rSJ7 zsYbw*;{ZH>HI7Bm(x!tXO)+GS9a5x2f_BJ72&s6*U4i~V@4Tui)Y(~ngIx>1q~JO` z6RE;z%1Q|)My%?4AHl3|za4_Z+9-U1%@3V$t)z+y)pEN`o{cJVU3!^3L{)IDF0jjVQE9FKWee&l}*0f$wqppm%UlE12`% zh(fK>qShDa+e%*xeXqa=RfpBZt?&4%hebhmjYlD8u`}9*8(BS+Rqs5IhjjILWS&qz zOrT@Th`njyLQp(kQ`Tkf4VHHq{xXHy=KA}kKVblck6SY8e&UksbQzxMK@H?TfY9B5>Q{8pwa`9&j|KfwPf6P3zf?OmvAK4Md5XK4p0 zG1MD-i%MhO!rKwfYuwR_jk-@Au|4{{+X-3zk|PksR#$w*2$<+qn1x;6F^q*6fZ$@IcJ^*^wC(xezTKjbaworD{ouZx zfBr{k`;qs?7W`bmsyGxUh9b=7(Qh98w6<9o;STaV(vbr7Z7g|i<{>vG%~TOXtId!!?@+tUhhu07FR0=o)~*q?A;3myh|2yLbdI|41i zc0vo{WqE)f%|_k3M(lrN^V3GDpkf^%(q)|fJm0MFhJ2(7Z^?zr*P=UahouZz&n3O!7@CIcU$MZ-uta<52m}ps{S-OTIxFU8O@nqq46hT|P})YgdF_PkV+jVW>{a zpsYRVIpN!uk983)|ArZw%_`!9(9hunV{`9j|3zk%eo-LBL|zn5$Ek9nx2G%6zoSYX z>H@Bc_pCP347lb?A`z{--EL9RAWU5r2^`%{{WLL@D-*}#+mTDWvGWr*&^MQ~vqkhl zxBHv~j+M8;ae=U((f2IF;_xN@&QF}{(>1Xy*NE)}=-GTL#+TyjScN|@V(;h)AtBTJ zOd0F?6z382liAd4|ApXn8?KC@`p>4|@#*%hLF=awA!B=Tj>5bP)YoAKX#^G!kwj~Z z)lnQ#=(2SsDF(x$QFpHq15Hb+r>$P|HMzpz6rYStp)F*vGXmp7TOPn_GnQSNx*H8w z*g$^ZEaVC?x^!_59j!6e93GDry-LnU@KKTC(jOWzaB-h5w75I7VoHTb~eWiyoT{kQ5nz%nT_ za@nF*VtH&G4A_rwd1Ez$6r|ON_iiMXU6qkw>(?dN%t283C<(*Qi~=@h#`7~Hek9gL zT7G__A;HZB@Hhe&!{399M!IDdtsC#NR%=Tw)zU0vGICUf&f)MidPJ-elF{OPrm zPOn{hdOE>`^#*F}ZF(jXtA+>BT77POtnTG%&a8gQGVnv!gpkklc*xRtGR`6Z>94@N zr>%FeD%AL@UZk|kZIC;LZ-Y0smgE`+eTqivzQj9j z?$Y_bMN9mFx%YaP%%6Yh_=R)tzk5EtGyQY@^Os(~^auCfRWPdHhWq^suxyQDI_bZh zwA@P_MpqZc2~X+?!yDU|811VD_b;^e;?DePsA>N}<_zIH`f}f^yzmXg-%$Kvlu~Fj z{MK&-Jo(lK|EGq#9^Tmgh}&0v57dEH--~zQ>iZHSys`IrdcU6-MF-@y_Y+g;+=S*C zb#EK7KO4qPC}2!lX_#IKBe{&f2>w{(|J@JUh(DqheWuQzyG#!(hKavC{5faNzjuND z9*plkp8Ngtm#FdA!Nxb8sq63Z&zOIg0p=IZz@HONb{5a3AMm93b7C-^d^#h1)k_j1 zebu89qkPquB|ww9JTb;seRTp_=I>7w`>MZ}fVGe-5|e$^*CfEp`#_@9SAA8&>#P2L zVy?I8lEiXv)5b&%d`8_YBldw>QN8|m?c~U-U&bcYABxp)Lw`4yV3AXqxT z2gDb!@85!T`s~mlrH0=0d;+zW&>Qm^KveE3&ykw(18SG%nV!#~!xh2*K=6=No?|uR z&tX%Ghq$24NSCl*+e)y3(|Xi;3Sx_35Trd`;Enx7#t^#ss+Zz;gKj>&d|s7!1`&}6 z@PNp#1w-xai2YRtmkLa4rs5U)Gd%2${uuWP8b^5#$~evC7gTn+E2>as(b2%=?oE?& zCy&fpdVd!ZxHn;MJ5P+vTH+j;9dLMn@bn3;K#)Z;HuokyR*tdE0hLwG0s)cGSB;A} zoXq?KQYpr!6=Oa;46Ueq2v>m0j{|*BQLh%%Z6pU2-CN*VDj&{d4~Au#%g(|ZyA#Jf z-l&0a$Zc#d$YulFo9u8V_M!4>2^D%20$yBZS-g(qb2~_y+E!ToDjWS2$*<DFRyfV}!p|y(6!`mh%U6gSna0+!2XcU;8+F$>Vh5y2 z=BHFTizC@t*@0JwgClUKnuBg_?zD&F&1CjC^lrRX;TrgC^h?R?>p?-Zl)45!Gh>Bu zLAg?dt_jHhuSk>_f_9AqNx_fXd5U zfP4xEBIkjaAQ1B-8O}0V5+2oDLcubMkjnu15s9yKmmkRTgm#M)FV@GuNdKK@2QgYZv`7|vq8CSqEwqQN5y zB^yQ-9jEzIwaRAX^l?6k0O-Ob07Jxi*WpryPREJ4|gT;jnx-4lD%|D7-Ai&SA57N_6?EUVj5IwgXl@s^=N-q z49`WtzO}|w5gPgxMnscNlEFvtOo9#jI@2$46%0Qx(J&CxBK+t*pG1LyW-ViK#6*Dig7FYCmK z(ZKmp<BrXM+er_b(-q<0k5I)59atN&RmCx`I_7F)l zha$HFLxzq|@LE(e>@hmO@@6O2(*s2mhZ5W2L|*a6OiFbgO1w>{jn2n#qAp*>l<06@ zMI|+3+z>6r4VxN24?$?I+84dY2R`8M`zr5heS%@n@FLAqG^$Qf@Bk(Cs-0=R%Zp7J zT5c^_8Sb&YK*wEp5fPyWOy(!>&k0miJzgw9 zAI7O!vjS&Yvr*UI5&KvjPU&KanhXJEpFDCunk(~xF+2f&^uSQX{|QZ+DN+SEg!U+g zb!Aj&#zM5rP%bn+v?!@Dun#+DN(=!KHs5w60-Qc6yj_KZXWGeg&YG)U2p*4dVDIn4 zF|iciR$M=cTZqxR%eV(e;e8mVyYcm1jMn`)PWZFZ5=2jya6iWQe)yQ$8e>EP>%+-i z`5(aAE2e_ox1xl62_GjGb94jrJxX5~K9SG+L?gIjN*QLYkK}KQv|@gCz);_`a}Dg<}6OzUv2u;8J`HM6p~&6q9vbG|5FAu(ns|J23Ur zd-gHNRB9TUt2p3XpOrjcw3Rxcs5X0Cju!02b9d1==Ss>C^jx1!X#xx;V2gTR;a!P8 z9XB)^4FI&{ zCFboHB(vohg?!6f8ri~%IgGQ|PTor=7o>$x=2zj6HR{F~G0Zg1QF51QiX`b3L#@-h z0vlVX3aQ8dBn@%e^?{jmZ*!l>1-Pgibw75*KGjpO7k3PrKK%kyAXG(b0NqbCFYaYd za+QWl_L^tt>VWroK~yO6>@S~ck0>lan7yFOp_sA~`wOc`8!GY~f7lti(U~i$_pPKq z|5>)LXc8D=k6|DmxzQQKe+Myg;9tglL(fqEVs|BoVB~4WETOJM`Os%;kh6A@)9j^` z2k;#MB^Dq2Jv`upM|CT6P$1C`u@!)a1Ut-yuvJV~cBBw`S4wgUcNV>|@-!w#smUeK z3*b8OlK<)xF9mMQ_T&`C^?G0dk_!DvPR-t3RHna~Yb>*KMn}W+b74FqZN63X8Qn7V z(wQ)?BtD{H{K#-xbc#G|X>^qksc@#;EL0WRuYRXG^&T$okBQdt{n3 zh<+X+Jgt~EuMbG-negB+f=_8h^U%wlhLh*ZIg zJSvk~D@Rrieb$S*Vfad=cUSa8`DkhJ!GEF+km;haMOq3!9p*yhjdnTjDxSkANS50X ztfXmbCGlx1JqPyISLEP2qnrk4z|&kNIbm@9djO3w_ZH1Qf~AB~(U#=$uty1&k(>vu zJ6E6B8MqN-!$M2LfLcsQjl9Vz*~ra>r*mIR4;S8Lk|iVUIaZ=U$Wks@m=fSbYR+7C zsdY!4T*OsAqlLiFqHp-B`a-fHEB1GRX}Fvpsm&>%ff2cw+lV<9Os^NwAp@YY%X~lC zcM=U>_i^~0xNAFkBdd}X);5ceLevkDevb`pcbYpU^P+EU{L`Q~~!2x1;J$ z7zFUP^G(f;012G@$TymQvfxrv;8N3LRoFKz9I9(^4>wwzV;sq=Ew7U@URLVCX{o_^ ztW);@d${&yo3qbKo`0i#a{;urlVzon8Ro5L75%&P_^pElZ0`H^82E2V&z+-6o-n2> z8`5=B3;aEhKb7I4jQUbJveI&}#^kHksGJ5l;NNhJH5}z|u1)*S7|ser6eR<5&7ZQt z<}3C%VvC;?q}ymXlclP)h9f!g%aip;UZj&{C(ljqw^dG7(xnp^>b0uaKRRs^V6bLI zfy-ENZilCj6K@*2KYfF^Kyk1jX5eaZ=E_rJXnT&nijFf9)4fes5@)+9@=I@QY-Ec! zRunl1Ckm3}*yuF+f^?GJ*tqBkZ)|)$7$e5lPefS#{odF`;HNiYM+{?nO-{iSSm0jL z)1P-p!r1;=VnBLSUv8zDcF75iL6X|PTT)vjG1tHj9P$8t~@ zxqH*lcCL*U-f?b%kI&*J{`bw=#%CSPqJPKj;K=Xxj(DW;I1*?|Bh+8-D6C3gdz0@g zJNSyDaGQK}xmMyU3vzyi+u9i2CDG1Mm&^S)?>50#TnEp>|3%yQ%2S>b+V(t>8Ge?b zmqB(FgNmw1zk!}Vg-sMzF?~^|Dqel?*bQxa&dTq!lQIa~pZN#bfs)^IoUH}%t_R2A z+Gck}7`3N8wt^lchZ+n_*^jL-k=;ga#2pd?saCDcxTrlaBeboF^#}S$Tx}J<#4CIQ z@|ngm02YF(XOIGH1}Wx&Wqrv!u(Fq)9Q!hqKnwAHE2UX%WvSOvRBHb$><|I80R&d4 z&q)XUoR`GUVc-ytqI2lGn?eTL`5Fz`&wh$4_e{{dT5<9FJbD8ze$l(ADCIt1)wt-x zc;%0-;!}S!VyWl)^XOh){QfNW63AwE077%Mg4`D;w(mdNy(y<)I%s{X*K=Qkn`b-1 zyVf+p6>8r^BX(3TRK9$h0Tb6Jq@Xi03VA{bS)D2blfXj$@nwZPbc#Y&NFmEJ3jw3U7Z?YUJSEEjo7gF-7s4f- z6L0=NJJDyG(ZQsx0>#RTiLMECV#v0+;I#?+>1L&2pfDG4wuH>@Fy2_jgAsPEWDQK+ zHsqUS_AgJjeAvKTQno@F$TODoebcf6BOBPPMmce83J~9+xy-xESm@^lYu%{tSNrjF6>-5q@RKSrbOb0R+?XBnsz#}fZW=huvQ zkx5$U17FIV#2!Yx!_Ut_*8p1pWl%u6KF*~K(lx}wHh(7b*o}03L}s%cu1&iBkr4w@ zCu?w@EaseW`C%?*>qav>cR^^|FQl|vg2x{k>{!{gZ*cgxa|G+80%eA16S9C=e@dVMyyL0#(RNmWa)d0r;B6ws1$fM z3OtOzF1xS_DJ%#V=x3KMkbAK}Y!Go9Es?DDHu6i32jFrW{lSQ1xv|D}Mafr#*YGsf zOJf}z9BrEOtfNW^c;RYShjk^;JXcs}MCl+dcvF;S9-U3|OJd3{?uml@dFJE$3B@q< zBN5Q`(-CkxCgO3bPgqJ6z--MPO^q}Q-5Gi9pG*7J7C?TFt8;)i?f``x&JA>?!HHD! zP3m!P{3WCATSgpJVo7lzhDkHd$e4@*wq#8{+*^e*P&dJdqjRzdg`S~USCBhPau1nq zmgURoa}?Uk(E;>f)`rgT>~N@m=;6;J61-(!MoC^ z8*jwXb3ARKXD{oXz097yDm~i?SDPC1L+EX>C4x}8uy;@&?C2W_8kcaRjqz0-;%4CS z=bKIl%fjQq*tkhT$@Dnoi!C_JQHG z>}zY8P)p}o1y+h0WUT6MaTuTm=TPKE7{>rDIyM5d=;rBU^IaLKlrhJu2B(AlH27=j z&p@wrP(CkRdJe7)T9E?`^E0O}z|{|Hd4PZfsFovkq2Cho1TN&l70d!T`lRJJ>l8VL zNRGi)jERiQvjd-ve%2lCh$?g>8{mfc?K zFK{IXcTXm{@s=(2HnGrD`_y@Zqj`fMXRtA@>Hxu$kT@FUpGSqG6DH;JA}!TBqDij zDMn_zMRQRRo4`?q!)%V2Wa%Fn;ca$BCVHFmA#&DS8(9hCCp1L03`#XBR?u?6vdC^a zf8yr>F2}>Zs$G#22#cQPtC-w+mN%XQp8j}FlzTQh+EF9MomP+KZ*# z1MSr{QkpsfE>E57j5sLP0J76(ir5b_Jcr@}aD9@YVT;w#A$W*tEm07(?p`@M7V4uz@h<`t=CO$vNfYNA@c~k{vd87# zG>A_+*RH+|GaTr(nN6Jy&TVC)t>aM!JHRNi6UI_#v`vLL8A9r~9g@Y6tl7=TE%0b6V@axOu=SXS9`Yhg{&aeS z6#s|p0q%SIL@bl2#4~6jBEh7)n$ePOA4)>Hi#m$?I0D19)@<29%!!iHb%yE!BwB4A z(6j+2EEYPLYyvys1FQy%(Y`&FExbY6d-=TDVGU2i+gJyOg=_oBTv$no+jLF5k0<9H=wMIh-3N1)55aBo+Q|3Eqo}v*2R~{qSo+($R00Ej-6=fC%g} zb_TPPDU+=b9%z`CvWwd-NLI?MrV@;5w|c#k)!T`oMIB$SipEzkEs|INIARxUd*A8{ zW@H8GwAvcl%ob$9AUNkr9kl%%poZZSvj}++qk&`8DDohwfl_zpgG=#tY~QU*)mW-w zPqDY?Yj&%h?s23Gdv

3>t<&Kq{BDoOJHj0mpJ}?5d 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

LNcx%wq6@v;u9e*RroJFo6X8{ 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 literal 0 HcmV?d00001 diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dpk b/Packages/RAD Studio 10.4/VirtualTreesR.dpk index 70ae7c19f..2080a6269 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesR.dpk +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dpk @@ -46,7 +46,6 @@ contains VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', - VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.dproj b/Packages/RAD Studio 10.4/VirtualTreesR.dproj index 07d2f6e96..fbadcabc2 100644 --- a/Packages/RAD Studio 10.4/VirtualTreesR.dproj +++ b/Packages/RAD Studio 10.4/VirtualTreesR.dproj @@ -91,7 +91,6 @@ - diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.lib b/Packages/RAD Studio 10.4/VirtualTreesR.lib new file mode 100644 index 0000000000000000000000000000000000000000..05c433e7577e08dd90d0a72f1a4ee29aaf68a96a GIT binary patch literal 1172992 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%0D%b@5D_6_R5YkmY)gsSNSF}CXn@J#N1Vn{u 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 cvGnKcoSeSTZ-5iK|2IDW7oKRI^#A|> literal 0 HcmV?d00001 diff --git a/Packages/RAD Studio 10.4/VirtualTreesR.res b/Packages/RAD Studio 10.4/VirtualTreesR.res index 93e7e944f20ed0f0e50532c1b7ef56bb8d255d33..a44368acc9a7c2376be3772192d9e305bcc762d3 100644 GIT binary patch literal 109668 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%0DbB~wvT$Ao=9XAgSAieyG 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 O%YcEC1!e# - diff --git a/Packages/RAD Studio 10/VirtualTreesR.lib b/Packages/RAD Studio 10/VirtualTreesR.lib new file mode 100644 index 0000000000000000000000000000000000000000..674832a4924e05bfc0294747cc450a7c46197d7a GIT binary patch literal 1153536 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%0A$ar|yy2sVYLnJN@*d2{JqyBnV6fq@+K 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

h+Qr!p@nkzhma=K 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�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

9kvURJ|@1P(VnYpd2!t|w?T)ETT!kM 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%0A=i+)&OD^#jTG#%6uECO$%6X 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

OGh*$(oJHw74=2_i(hfO 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%0AMz{AAVNQy5VlZF17+l~~r@E}lQ(Y^g^<+pC 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 literal 0 HcmV?d00001 diff --git a/Packages/RAD Studio XE3/VirtualTreesR.dpk b/Packages/RAD Studio XE3/VirtualTreesR.dpk index 7cf65eeb3..114b9d6fc 100644 --- a/Packages/RAD Studio XE3/VirtualTreesR.dpk +++ b/Packages/RAD Studio XE3/VirtualTreesR.dpk @@ -46,14 +46,14 @@ contains VirtualTrees.Export in '..\..\Source\VirtualTrees.Export.pas', VirtualTrees.Utils in '..\..\Source\VirtualTrees.Utils.pas', VirtualTrees.Types in '..\..\Source\VirtualTrees.Types.pas', - VirtualTrees.Constants in '..\..\Source\VirtualTrees.Constants.pas', VirtualTrees.Header in '..\..\Source\VirtualTrees.Header.pas', VirtualTrees.Columns in '..\..\Source\VirtualTrees.Columns.pas', VirtualTrees.Options in '..\..\Source\VirtualTrees.Options.pas', VirtualTrees.DataObject in '..\..\Source\VirtualTrees.DataObject.pas', VirtualTrees.DragnDrop in '..\..\Source\VirtualTrees.DragnDrop.pas', VirtualTrees.DragImage in '..\..\Source\VirtualTrees.DragImage.pas', - VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas'; + VirtualTrees.EditLink in '..\..\Source\VirtualTrees.EditLink.pas', + VirtualTrees.Colors in '..\..\Source\VirtualTrees.Colors.pas'; end. diff --git a/Packages/RAD Studio XE3/VirtualTreesR.dproj b/Packages/RAD Studio XE3/VirtualTreesR.dproj index 2c66ffb53..f274f8ef6 100644 --- a/Packages/RAD Studio XE3/VirtualTreesR.dproj +++ b/Packages/RAD Studio XE3/VirtualTreesR.dproj @@ -107,7 +107,6 @@ - diff --git a/Packages/RAD Studio XE3/VirtualTreesR.lib b/Packages/RAD Studio XE3/VirtualTreesR.lib new file mode 100644 index 0000000000000000000000000000000000000000..5558b2e6c0f508f9f7455525bc050b00f0c87e7d GIT binary patch literal 1149440 zcmeFae_)(Noj*Rwrfu2=5}+*w3KU2y&{CVElok+@=0_7~+L&yn1%a|nw$0KsyKHtt zilEPPDbn7yii*f7)rea}MXw?vdZ;T@z!Q~2R8;g(pWOw^-StimIpNIr^`3d2XLq07 zXXh!!&mZ@-kY}GCGw+#s&&>PRyyrbfPG?LzflXsB|Fl!s$vhN|#RJ3sXfQastTj9w zj*c#i1V-8JlNtN$NhQA07mc0N795TYg%)fKR5-=+FM)k(fNFWkg zhM&RkO;!%^rElkGEVyG?f0ut}1PzvXB&>m9w0|TP4cld$B+9^h|4?v8aq6|v08lVs zLCJ^TIvf}swR6-F9F*KO5{j8c)|#)VGNKZ9-OJdt&HnaH{;JjuZSB)Gx3&0Ns>EgU zn!Y|C{$|wIuj#w0yKj11JN;JU(SKD>yLiS8J+E)+me07M=iUt)``XR)v*dF}*E;LX z^tQFV8~S^yHv8B0`S3S=bJzMV|K@7CQpwXdw{N0ny3#Yddj|VFW0gK;-c?~}(Yq=( zeTMb!q&ecQYdV`d&$>H#K4YJl&Q6@Fobs`QjqHMEv+H9t?3(4UYxZ-$c{k7&+!}}v z$K)fcY-0ykv4-aQ@ksrO#`0M$AnhaZ9jrqCcm+GSn4PI84++19giBbxD5G(8*)ov& zvMP4)Y<5~nu$voJHmzEXM5j(;2N#w}w0b$KXe|$CbpzmhiYeeUvWiRC!7Euu$@*9r z8EvlLG7yW_-*{s*(s&Uc7zWcW+j+*1vQxia>9KyD!N6cJ+JR=;WubnzvV*JH3U&R~hX;esyf?9f zE$mWt-oD^)a3B^OEP@ec6_>MvaTZa)=nLlRd{p=uZ)}i6uDL%HBO-_1C9=*ccCv%r ztW#Akk>S2zbSyLw9F=3G!7*0ks9_bgUdCL@=xAu$hy=9Kte8n1oBch(=x7K8c_hXv z`pXXT70WTmG3l1D#U&fi0g68yx;YRFg+*cL<{9kZ1?=3CsY4*dA!qt(ws${kD@-q# z(hxDFX6_dco=~%_5Q^Yi&0V9Np>0EAjs-?z?IVFL!@)s0WlEAA|C+#PFpouoYt97M zU?*1*JKuYp&p}0<>6=^Pv2Y)_bC3!Nv-O3QDo#E)=nu`$IamSB@b|AM*9S`jsi6CDn z6d9`zN5|c{G+xc0e zs?_0NAZnwS8WHxE#~<7gX$=evnTYCEl@lJ0?-=PCZQl`z?Ns3^r0%Wojz~Nf?DenP zuyyNbP)-EaudGCTB(^R%794I1ZQU9Kss}{%%V9Xy!1}y^<)P6KC~xafXm}8SRZV2; zR6sG&VnK|Dzz)GLqnOHtvQ=IJr{r5ax<%rRBPL_GQc*bM{hUs|r&S zL10&VEx9u^I2atU`ln+!uua)VYu`HTmf>MyXQJ!ISlBzDSWkEqQbc%UoxfKg$psss zP~(Np{%9G7Otgc-Q2|aMr-SxFV3)~kS!Mh}OLATWwV*!MxT+vq{=??YjrF1U zK>c!Yy>Jn`Rb98OnDQhxiSlIsE+-OZ**Bm-1V+XJqvfb?2sEP9tiQ!u@m;Wkv_~QD zzQy8?z4T*gyi4g0b-akyw}o2>^{SGULKNT#N#By8Fd|hu8l z4=OuSLOkYy5_)%$GM@tUAuX(KVUuX1zJ(_gCP!W$hTWA;GBB+XoDJxM; z`Ba0a+?*|qN~UC{=Gajc&6X?C54tiC9Bv;B3Au2LDaD;Ia#+dBH7x|`*kNHQG5tuD zhzy5fn1sE-(a_C7k^}@{E-mn=N@RP_^r^8YRXa)O-1Td=U5!1`9$JS(1KU-=lw8nZ z6)J@*Doold%AtYX;H}!ws8yAYDr zv_f+DrbhG8GL^~4&F16g%?){6e)S?2^@0h=7HwOG-?lPU8f%-`NIA8rw~3Ry=)2rw zU4^0+!t~rJXN*vZ#$gTe(p5VmP|#H)EY=WmTDeP&e4&z&O=ZKD8%)d67`sT_3YiZa zwmUEaLxnINjVaA=K%2-&WKJ5K%FVZs(_&eo0mS6DnZp(|-;&SsRXGo|nxM3gM*@~= zx>1P$Tg14@)+JOu62cgHN(q>%vSCajG}5rboGO0VJ;yeaY+cRf*;JS?@wlL_v2a1k zZz;}b$sawngfa-cI9oE_WOdadx!Nl1ds_prg~S>fx^g3O(YRf0htd@vU36^taA|Zj z=sXb$2%6MMHqmj86gi`O3aj2zec>2AxunNBZI+lRVl@b zIU+C3VTk)6x0>hj{4gUOQ*n)!xYj=s8i3N!7mH%jD6Qdb)GNdc#Zn^GAc_^1xR-j< zz^DFDRXY?d9;W_qcsLe{P{C^;TVTZkYrITp3Z}|Zv1azSuVl7&x=*bqn5DOm$D)Ca zp~2XYvI1^1FX$D){|}1k2}k0Q?%+sV*%}wt%Lcu)rhgPRz2Q(~OE?f6>wjlw0^hQ?T6TgQgp?iPQ$zjYCNx3UzIx>)*WbiA0+$|UBpfN~YLEiBXLD=BKI z7kaFykb)%-4`ESM&0fvQknkKF+z=NtkIbBwP*xy6JKRZ1Na1KxvYEkQ3)0RG zre^=$@Uw|WgJe*b#gvrb%jqu=Kh@@LF}>T#Nry@q^O|E_Fd!*{7XvxW9G42H(J$Iac%y>L`AK@P6db!s<5wc9H@E|X zr6U^NQQ!kt4mTc5UcEsW9-~yg&A?Jf|NH@2# zbyM@cJ`nw5=Y1C_F)MvqXc$0Hy-S@u7?YlSo30W=r?ftFG~)Y;yrrzBMUhg<31-xV zKDCNPexb5CYXZ_$H|${$Ko*L|Y?ClGs#uY=KMRYo(rGI! z2}i-D+dy+hMlG+)F{Sp5jKT}l-Xu_#9u^wUOw*ocFVZj=RG&}f5J5E(_Aa5I5@yGg z?gz+Y?u8`dKXD(K&qG-y3YB|cxRB9Rmb^MmFt8@3T+%mX;6d-Z6HgI z9hyaAy3AD4HW(jO`mo8|(te!hP!(-lAvc~kcLzsD;qe1c?v33d8J00zY!fQuRDM~e zt-4$}ZpEkuQeH9A5?rnj*TxJeCTM41R7@G_rLyGM=@%&}fmrHg9tt^jja21hb~LtL zrBTeQnHTe6olmu@-eqnAY#AIB>awtbN`zY`Vlj(pbV>ths9aYtmEGGaPYC0dMWHOj zixs29hFG-aZA|IfVdo8xjKEjVOMPMu5vRiE*m+ybmk zjShIPSU4P3?wYb7zZLt)`HFrZ>$@e8s^r*i?7cOGn;}}iG5MEDXNTv__SQJWqR37{ zsksYFWgJ-_j)k^{u;VqiPuX15wnug=_8o>tuxO6uzCnTE zjos^8a~|&(cDA*`i{?B92JH-EY@3&)PJy-LG6Z-@Y4S*lOjyahq|RgQoGmA+6%YGD zc`VHC_R=WoSh<`*uyJEhJ{-gqNC;d_t<6^o%Hh)|`Gg z{#LfN-QcAXwgN}HBr$d5c72yseiu~4nv@c2Pe5#Am%=?Zj1Ev|lw$Ix?1IU0xlgrV z+Y=3vpEbF`NpM92Gl#*D(s891(K>|c`PR7=L_d{%pGwKGg}1~KRlYI3L3pI0H*?Cb z9R$FoUzG-pw?g0h6~j*&-DpcC;lqmkAd&Cv$lUU=QXn{bqq>Sq2$Om*v6YsQfg$M> zKjxtxh-nj*5nKdMQg(vMCrD0Jn^BdcS+NGP37nx^U!^cFv=hA*L3EV9eefAjdEEn^ zX-J}K!s02JvZ_pqx1+Cw)Txf!5}u93VI~SM(H&Kr8i|?EF5vd8dLH= z$h>cxrTmkM8P*qv@7QRJOvCMnV<4lFyv;inT$W%%%K70BZv(Tj*^g?7U>QQ?EYLqP zD#9LloFSMXl}e+TBvNhIp0{np@|#f63z>)Z46LEpRHjtvEtj;K5icf$sQ39;D;Myn z&3AMgQ~G?#jFKOVm5ID~CgUoNr1ZKkq(9Z#O^KzG)rvuEWuaarOO9plf*D>FaRqTK zyoeeqjecV^gvA~*N{g;^wMUmD$V$0D6r!YXWs6Xms<5eExL(UOI#Werdsq0{6kU)P zxUDFbgstf)wUPQQgEIWvm~yiPj0b`(sfYr+*HxOK*yE0k7Ndghni|`fa)rxl+^}~l zP`4%s^-az-IiXb>biM8{R-(I=%onmu)xaSA*B`cRxH6N<#1Fl>D35YiBIvUfsYwQ> zGl`>W127TO+~jbV4nxZQT-eQ(-yM!9gGHv3+ya^8RF-9d?frpIWu)tf(-t4O16VbZ zl~aO%<@P_8r2{HH3DY=y;{A{eNM&!4p5>viTRa^vi$qvFkdmIgg#oJ=`xJA7O!Scc zsS+ymgvQ~RQ5Y12w40I}f1)lPv?}qs3qeNY!oHW0rlcTbz9I`K1wW$ZxjFW#oTV%{ z(pV}H<)St!SE%wFO{Ke`sF;gVSays(t>{~!c$0xcR=A|mrL+{`B2cbuvNn#07&J{z z8sN7~k)mX|UX=LXM? z*>6(16UeeNsdL)iVLcrUlWSeW??`Q)6xNPOGAbO zrW<5Pnj&zMxx*GpP6Jy~HhIn|T_lFI=L%a0+*p(e4i;vLH7H8KmWfzUE(gnr0s87% zibyOwHkSpQwp<8A_*S^W6Qf2%$LoSEAsUk*B+6kOtynzzf98b!1mr?WEgQI z+HfnUM>(uS5W3H$T*RSRUHDDl1f8{hr*6iCP!P4;$2fd$iq~jXk=yQ#`SGj<7;+r2rVTDpH+#yBB7AJBkpFIIo%t) zF^;hzkS6i4#=SVmT?jJ@S|*Eg?UZt3%Z5?7zadOIh8uqDJZ<4PBB!p2i;%3sd=X|X zW%9ut+w2NiB@{DbWhIA2`A4GdxtwvBo^xf z66zxNgc1=lnX~;ScAI6OB7ZmzH;DWYM=&Oo>WyX!e=tf;>I61Aj`ih&WadY)OhY;; zVz@n622#vSZ@Q)lm_<7h3ri3xN&r#z#)4K&a(f_GEQ+qQ1PyZ&rORa~-hr(xWE%vd3bf5HtyzDUY_XP5>!5;=q>j+oY7=SpicCs$V7x zabIcTrXwfBSZK`8lKaN~-U}>qNd+pTga)Ef08dn9&`i}9j0JE&kd#)eO6Egwi^xeT zzlU~-ql4_KxJ374XGv|#q9pck$x~S@Wvf%-^?9tLIQ^G$sgFVx>MRH5s6vG=E(a-Z z9brMaboTgJC)-)&TT+zVUcG3BHnd?2WPZ8Tc|3$|+fJn95}PTbSf`K%;qlP;g_itP z94)PA@^)DO7O|yeios~M5}FfuZJE@PW%gph0n)yRoga3wImQ~XtqUN!MR^zP1g0f@B zj>&n)*y+v`!YF8Nyc@%rCuV~!0G{z`lLRZL%fen!I+xBq!&wtJ$3_A%GeE}}j^c9E zw4|tAgpl>Yn_xPCBb_`kVhr@y0f)lK6(%`Dl5ESs?H4q=69?P{qvQ!}_t}8#v!@Wa z!GdXz{z19Hq1gp1WpcRS=Y01virRRpoz|>H9*I81{^n>Fozf?5lS8sy`(X4l+tn=6 zO8H(4@v)l+a;+}iNURW9{+Q+vVHex*x<&;=f>oR@(qK(Pe>ZrzNXgh_7fV$zR>#=X zae05WxTkkGgl>xNaps`-PS5%2D9!QsI|+Ym@}7!RC1cc1-#q(3v}M*E9Ttb8L}B#9 zjvv5i6bCd3evd~w4uhPPNfN0OkA6fA6g#@Y(Rl-=)0OQ_V`f_$4GofqsrhI*oQlW! z1N~5P5W+ENp05BpC_dB-$Q^IuVdenE#Gz6b6cv(}?2Hn?@;=YYt8w1z(+BJdlWj$K zpRO6k?jup)8Ku68y?Ekt?SS-^vFdkHE;+3#nrZAdisw=XN}^x(&t?Ymrz(v_xUk7@ zR;19In?z6KF4TF0`1`Q=>g)LJG4GG<2Q{F7RKWC(4L8Bh$b5sb#b$Jfnvc?}D)wDm z+t{m9f5Pt-tdd=TVY3F$W9F}q&9ny9=t`skL^+GlApEOll>`(2s(|^em3V=Fm27&8 zS!^XUb4deuxm*=&dS5sm9SBweKo`Mb^GW>15`WRjb1ra1rV2Ds4nqQTxp;S6fg<3N zThzURRkxT54`pZj4~eey8Fl_tl`+Sk@)`5#vXCz4a($h#lzj>Eu3vAg<9fj8;rbTC&-E*fO#`XTx3C0et*BKup zp!?}^fG%I6%j5pkOyhYfJVQzT2Z__i<1xK}>z(O$`cucJ*K@r#&FS4B*Egld{i(Y2 zUaq&L@1%G4a{UB0Ijt&%@jydjMy^M#A$bBTHIPpbsIx9aPjDzEBO1ln?7UfLrJ!F* zMgNFeRj}g3pn~(&f=9XUf@T$|^QDm$FPvX&?~f zX>4vug6D)k(@Z~|o#sqmrW8UtVQmIGu7G|zp@X^$yj8Q~3n`H^Aq`agP0eK07O`?B zslf|s*(|o897%L$Ll%*3HaopUwj8Z==0o!sF{c`KqMgqsn$E;@elLxWmYJiDonD+HuPf)U%Etg1l;9QlGY_3v z$$albe*E;ypIhb60r~S|`GZL!Qeu=NWl;X?lRpp3pI^wIIt*|m>6AZl`LkdCJRyHx z7eBZZ{6lv7r_tZkR4tjn*XB+yrBBvBJLUe{D~K~y8MQ=PP67!r-U;eqH2G7rjf?!L zTBDyHucF6m>G4*2)amgydOSdnpQlF2W7L-b#=A>G3o42r^omd4eAQnI3;ckFV3?n|RCu5w%&=uPS>nU0UeUNtX?D zA%dC(4yv+)blFaqC|yucRaT?R?R5DdUC@N8>^*e(EM2}pm#@&}5xP8vBC}8UQ?s(K z(A^P#s)kLrGEiiq4b*pX!rP~_TKz`9cHfDV$B}nME*(i4^8nL0)t@?!O;%K;FuN?W zZb6|!=-->>O(oJiD!83||F`SkFz`1F{2#%<{;?{e@Y6_OoldgqG?G=Pk*r!%LRRgE ztg8Jx$*O)^R=uD=R@Gi1S@k5ysuvj7bL~HjL9Tt*7~oUj0o4ejTqP08{=Hx zVBEs>cNrSjdyHLNf48xl>%9hq(LUo2t{pb+Omcm`k>q-laX;5r z7!Pp$LgOIUR~ip-{Vd}VuAgl@%Jq69#r2DfLtI~FJjM0Z#?xG1Y&^sDi;d^Feu?os z*Dp0*;QBj^7rB0!VQ~F&<0Yo*KmDqx{m9or04J@>G@ng71H(6^g^zmmagaeg7mpuKRvya>u02wbN$Tp3a&3q zuj2YS=}WnOV!D~@=ce1ZeqOqh>*uG}aeYa;hwDqzey%s9H*xKW^fi2b`g(o}1oTtW zL;SS#Fkg_4@YB;VenxtnpP9aeFHCFvtn@B^c6v8I2jY8u`VPJ*eJ5X>zMH=zeGfl3 zJ;~2YC;9p5`}wl;1N@!ogM4}VA>NRFgg2%iN?#pxILCFvLWrD=n|EBz9`Ed2_Wr(fkQ>DT$1^qagj!+2Y!innK~c}J#( zuLaNT%*^3knfd&R%mRL8W+7jfsps99bNTwrQobRxocCl_@ONic@!rg(yf1?zr88~3 zAIzC&*71#*9=<8#=T~Jm@vAe}@b_e{=htKg`L&rLeqCmmZ_Y&c^_dtCWXAcH%q@H% zqw&GaE*{M6=36s+`L@g*d?<4#4`uG=H)QVN+cT4VIFsZ%GWYY5%mX}}ImjcKhxm<| zM|d>zC?CzFcr0^>$1_jyvCPx_rpz;ZJo6minR%YyoOyx2H}fLDC1dbgGcWP?WnSU$ z&m7^JmU)$HIz$5P?=o+4Z6eFKwkuo3wcE1QT>C(_hHJNH>$tW%JBMp~vh%t2_t^zp zOJo;vZEv=oYx}b2a_xiJrCj?^b~)GX$gbephqJ4=_K_^+)km|E4e6YHKVCf5G!5Z6AH9p>7nvk|UMW@B9YOm>`W z_hxV5+Gn#G*OJ*?Tsx56&9(cod%5>XUYKYJ(FKA*jtYhTFT!?iDFC%N`OHp#Uw zW$)+Om$MIW?H{uTxppx75ZAtveS~WdW*_C+SF@!?@ zB>NoKzL9;NYv0Viz_owMzR0ylvj*3`m3@h8--Z~YeJ6W_YpLw3Tzf40I@ca&ljl^V zroBlj)&KARomX+P({r+wqPYa4Y<~*`aISsNsN&j_Mm5)-GHST?eWQ+R|7^_R+7FER zTzlGBz_lM53%T|qqn>L&2JzOO0rA%U1%?LgSr{6$pTN+dJx9d*ry$f(0&Ett-S!^t^Jy)_iu=L|0_}N7eT$X--3E;zXSEw3{Y<^4eG6B zK)tn>K)tn>LA|v2HM%4QVsJHf?px)YlfqH9y1ohTl1@+ec8`N8S4b)rv6R5ZL zI;gkyKcL>)pFzE~H$c6$H$lC%{{{8d{sQW)O%e5GM7=ABdRG$l_7U~2BI-R2)LWkp z>aEWp>RnCLdnQruSwy{Ov&l}Dg3d?M>Bi~ABhTU| zv&ql06zm*UerqgSGPL!6D?ZG(J|5L?1;g>)!pH`*fNflp=J|64q-s)&c_?_3oJ`fM z!W7m`BO7YIKPc)wgSdP(arqg<L(p8laDi%PWi3=dOV10+Ke|lX7pWx@#dV^p)y7s;2KhLie=~|01z!&n=&-Zkb3`Y5#LV{NEE#AZ@Kk zIQStlA+U)A+1A7Pq?OO0Ksj zFUKRBe6%tJvlE(vW+Z-@IFfjxu(;Y?6F@`l!;eqAURQhjwc=j87}cMuzwJu=2=xx? zzZ_uGrq9@eB-`=f+xR;HU%T*k5B~lGfA7LKKWZw=*tf2}`oU?5RZ|~-6KVIg)mQh} z-@a0LwW!CgRPAjw$Yp=1efTE{|6SGMZ91xd9{beK4< zikTVjx>lO{#R0M^F@Nf# z<@;cFKFDv9t<36y!x0Cx>8duzFLSHh;d<9EvvP<|RMk%$s*^M4cPx1vO?eFK*~Ut` zTuB#LKxP|#blFIk_t0fCT?Xi~4VUDNG!gdEr=0IE9xt4+rNSe5Dy{GLAiH}38` zfi9pR9rvA2cbMpF?`x$yRITp5ZS)~VcN%?wU8e57PvDMC{vCtC1#^_9XgPXNk)sF8 zsZ%875YwzOv3%;T{81ynSp)I#Jd0XrcVo6Cn(GqH^D(9+ss)WYl_h5p0a<~1C41D-lc<~e$RwuQ;rgKf zsOk;)4{ycS9&tT9J1>=-2I!oHTwjxfq4LBe*yM4^>$u*U9OU}(Y;q1u!4`envvFUd zrz-K7fPn^0LxcESHhDEmVX4HdeB$xMT$Ig3BJh>veBb-3_l;J6gH}`ic;Gs-z7vl; z0Q|A|sphj?=b4G0n6Ipg8M9%kP1bRJdlIAm#3XE$CnmeOzL8DNWZ*y;Y*moHYgmem z<$^Pc${Ce@ADcXmrRIQDUO;uK$a_DFQOYyuHQPU(Au-ZT&SGRsFL-+ryZ3mqu2+Fm zftb=?f~sEPpLQBMr9cv(UA9;*-o7jn7-hRpVeGSWOMYj&Pi7iBNuCP15F6tL0>cY= z>NDt!ADRtPj*B4VuP)*s9i1ly}1Hvl_IKZ zR!Vp?pqxBN=9m1EMcd5=TG1oTEOjPY$Vb$CRcpS&c_s4AUH39JZL`09lfSBULtFc_ z&226ImMU@Cyr!?uhrb#1^=taB>h7D~)=t0Gc=TV@(=MKIL(l75y5%!&=(%^p#=dql z{Ve(1(Y4NcGretX?}q-Ks?Gj&eLnn6-`usn%fGpru2k~$&F!1$nXdHA?w-Lu4;d13 zL*7+kM$ugrn+^p+zB>t%4|iSD+1z>7-O11!J~5r0I8})y&4Y`bd+un`3}ezXLpPxN zW>%s9wvruO0vDqC@kqTRxSumQQr}g<4%WkS%A5%?8W+uWy3GwMn^s+fWREg-a2Yw* z=Lavhlligt5^h%XOjDR@u>{p^f`cK|!f`5}TQ=lcs;tfGpUH%sJR=v;S8#2YVWINxQPiXq0tyT$xtuD?kBO(|)YdB*VmDZuD6j znlN(pYe5Iz%^V^4irOc=izHIa9wlFPiy^_WU+mGeuok0<xyY<=f>g4l`Rw2&&d?vOpy53~5FE~{*yYqJN8B0L z+=$7NmsS5YW|nu9V6@o#&j=+avx7IYQMXQa;M3ggZk6uDiqk4wE_gu*b7lg=^eX2; zqpr=S*i;y;ro2TxT9#U)LwelFtD|A)>l{`}p16tXei_xhfMMfK$q9l(9s1D1YP9s#0lyX%r&&2O%=K6t0`?t!LsJ zn{Rm{lfKc6XgSnMY#IsWe4fRZuC3TOBu)eki?6m9I>5$BA!%-JiEfM2fu{CO1X8tT zLLz5Ng-vqrkJ%+wX}Mh1wuNS!X1VO!U^RVkz%|F@Mm7y7XltdHwUI_shPZmG zbblAETPa(&5HhC_tHnkvp<6Zkr3;vF8nuvdkxagmog+F;CM33r5OUa)FcjK~onojH zbwtSALuV%v#}b1hy_AY|w(djxg@5+LWI&^SwX+QRk#X=Dfso)w&^V=nz)sD7!6U^IJgXq!L0Q@Q% z$QzS|#L*HFh&bX*ijpDAm{)Q~u?7UvkF7YbG)5F>6S|@<#0ip+Qb2qti3o52DPoj4 zPfl|mnI(^^DL0PX$Ozkeif_nszhS4D)u?iXxe~2j&m{@+Y(+vVN1E9Ojt-`m`a>Hl zIofbIU4f|W=Y4*HN0wW5yXSh~T*@c}H7P8}=~uG2w}N7X`@hoq20He$4xH!CxV84^L-Kp&nWTIjP5gSq`N`K^U8U8xCFHZicIH}`t&(BLnd+mt zYC{q4%!6c{=I1idY}=Capa{4AIf`)0Ua23|J024!OXdvXZ)K*r=m8Y*>|-7~6O)Z7 z&;P49vtH%XTr4Jw@024sMMreFb>`8;2lYD5FhHOP3ll)@%%j4YWIj<-ijR$Quj|nt ztBv?N1yh)|$eV9S5pEVCgRmeiH2W|Msgf5pZTv#Zh_k3|Cn8`V9z!%5*jXPd*FB36 z?-=Hx@--7HJm$$0t?}rnl=8tsol<2{n+C^1qvqMl_Aw&ia`h>D!ZO>tq=?ZJ6=_EI z5fKJp2qe2n3#5ONAHZ)_Cmgnngm#$Lm3Sx~+Bp&-J4+}sRv$)?3@4j(7M@dTtube5 z%|MpJn&`ApP$U;Q2BL_)P1RKgS49>&yxR_!By%~IVTubHqBCKnK;;zF)7ffX<^?7aR)?w}nKwKQo?2Ig}vkqf>`336_UOLx>|NSfmJ#tn>HEZgQA$ z5f`7t1f1E#{D_FQRYdbU6x} zxzlUK=#;NQ;^tU?6wXKxcZq<}y^_9N=Cx=UGTRRPEd~v^$R||!;gB&#tD1vyk&5(I z(Sr`-ioL)jlL&nppj)z)uM8oH{_hm+;4-Vu$!fEU7F>5&10Sa zI-}1mL}S$1Lc4e~R-1at%8sLa$Aaq8sB}7U6-j$mPpgV{I>(g&RN(3X>EudfhardQ zV4+xg2xHn@>x*~+*cTZN#Xy8)ba_jG26k4P78H}Ah{fr$V;9n>QWObGBb#c*F{gQ{ zm~aaQhw$U_sQ7XfQ*yvgHJ*r@$^ez+N>ZL#h?OaVWZO&Km7S=v)`)egph@7U!#H}v zL%lr=JqgQ0&DBy}Sh9M%^Fl?@o*l+J`MBA%FkjLd$FJjK+s}h4nxVyJ)8GhT! zWP1-Ok32+gi*j=ZH-y;bUV06#4??#JVQTKoGfR)dHsYnLc0`~AtL9j&A>{N*D@CM) zK1MbR>9Rsli=2ww^HDJA{D|Uh%I1^l04Kc(rtCt6JkVBxk~`#UOnKa@Mb=3;wPk-5 z21D>Jc*;|QHjK$5FN-289^1&bbt~K7huSU77A~l3Y%C-imZFQ6{LxcOD1*R@Ge+ci zlf^KK!}c~bbmd0SB6yFO^V?w-jE^olHhj1=IvR3oHrV7mpTiBW#9+3V?wHe0zEoP< z1rzhc;jmvixqXg3;ngV~aliff%FxK5(`~U7#PJB6!aWduH3N||$P3NyRt_E!H_jXP z4;7i{iz#{zoQLXx)P+0{P65-BfXeatu!nA-=(Jv7u{)KhGhGhI6&$8bL@xz>RMIAg zc1`>3p_~-JAj!@}-NeLL=~GK{R5RQB4*z*`UfbkIeq>4$1NnGJwLcGBkEOCw&&5GEnvm8imhk;8#s$m7NF zp=@z3D}O43vv7qWW+>d0sKHUJI0U`msV5wXN4kR}aTS_EE+kudOrEG*Kf=sIqKgPG zQ}Q_t$WwygdR3akG&IHn+d4M%cDMN3{jH0bb6vN%6Ubj}`0=BQCYI!^w`4Zxd+oc}+YP3y&O|=miEF zJDMtWhvoSY$6PAgTjBfA(UjaT4Z%e;exWidy+_yAdg?Vagcu56N+>+O&s#U4*__MXCkByy|^;zU}i zn->cu4H+DQ9`c}mtXEdp1h&(Ygzv%jDB$IB^3qF|&fTpg7$yPAHzt91yTJNKPmPq0 zT^7Wgxu~oddu&s{P44>6QB>GZq1`-m&N15eN+15`)&TNnQ){-=gT#V_V?a4Hdk~*Y zSRN1oS{_j1SBM9Nr#iYwL#1UL_FivASn|$WB|9YrEDx&MvFiqE@wzv7^zLv@7x!Xa z;OM_O#T5)b^U4@COd`4!vS>QnjDv=uJ2-lZIEa4nV|OPj8uvF0*yk46?= zk!SyJS`;ZI=`*#SLXK+oG2m1)Sl88q0P;%|F6Y6hy`8sMBeXvYlbzD72B*El(JUtp z;_xk7e+BaDV`W_)n(~lw;fh6|F~jOE5$AQ`0MA^Z<%nQeA4v7$yg#%f7|pHDk9pv* z;(#xcqBdSspt8Y|C6E6SSaWWTFTt&?Q`qu+?VP)Tism|WVR-&n#_o8?yLDTLV<-ER zosJSL@ZN#DY2_$XNor7_E&P>%qm&~72OHW$qeGH;da#KjN1hOQqiS>_x$AmK&YjFw zVO2{Wz>RWNW7VfEj8*W#PO^7NDphP!vlVwbn^)x_Nh?VbCie<6?2KMfG??2*Mloqg z;1ITIs|TFk=g#4b{u-oAT_c`d&*4K8VjC1)CC7o-{vu5qQkJmvL3{57HcSQK{!NO? zBA;pnRGjf3RAVWeITxC6l3^h|aJCk9Q@M@34RmK@6i$k!f1^t4C2KY<&kL)oY>N8= zy_4$a`3-wCzs8w!ccfcIyet}&sRyJ6)jR=u z#q`?|*cS9s8s^DKK9#u_KoS0;H?(a?hL142dQd$?)G9+|s6+Z@X_?Q6j{ZNak)D zP%Wh7mleKAEclg*YO}Fhmg8oDP>qMYVx(oXTp_M@jseA}>kN!~qpD7Vo40I+S<&J1 zP|&d}m?|HeqcOKC?NVMXtxUB-X>BmV9y6MElIcn|=EjlB3H?g5vsIputBJnJvYb6A z84rEOF{0%;w+*pqOU;;ancU7B9vQ((kC*zy8dPDbig^sdZHdJK14Ck+$hmRX$z!Zq z;XJ?r?-dJ&i+BFHL4GTy4z~9lO0O3(fLrpVN|zP~s>UI%M0OH>OmRzPWM3bSg|>zU zY&{x-(Ok8)1xI_+Zt*8u$5!rv+Yk-GVInZRv3q@M&TaR?&bCM!OTl6HnMw#`dY zrvO>foDZhpoFzHoC9LE^QV$_vf@WMf@u_(HnxdB}BZb-BUK&N6Di;am6ffDRa z%@WJ8*d1h@G;DYInowIvOf6?L1m^@0SRf&T)GN8}S^}*SSF{sp{H^WH>GIv+1x2=E zMa#_M{M|CBazRI|NhwL1dtRl`O=0ik2q+dm%#dNqeGq2cKJpDx4JJreD(iJGq9Noc z?_1(JDwmAjARK6bLIp=8}H&anXVcPAbovke+ z1Be_#0M#ym$c3Oq@}$Ed%B*aj%J)tVO`9;6o2Rjcu+mi)7D?0iKpmP=Y%tbO!GB%BE#mymt-<^F58m zlukD?qvRLDq8->~D`G{lYn}GS(p($DsmkVG!sNbMF^H`!)T?ANuSj~V1Kt4 zR;XfLB2?1`N|Oa5Ityn?x$I(EY*dCGxkVLLOhHo2upwhggTFj@Ud}gb2dyn- z^#r4%p;2-tb_S7lj)7(oDOnC9IX6^p!CrT%DP?aFbY7Op_E!6v6eEb#4}aJ;Ysrj_ zs!Z`%Q6AM~#Bm+im@Y|LP{=!`#-rLuM)V>#z}!WPkaC{^0zKz9HCLyTxQs?sJm1R4K^V zaRy=7RV}KKl1#Rij*yI!;}UF5x!`HR@nEw7yJ#p1{O8n-^1RA&L8IJ)6`>HSgBMDn^phvQ5NI@F0Zw+2i&j>46sT0-KEy>- z(T7k|VOPr8hFH&(*^VlqcEO1$OeKigJWRz3QC{ijb=`+q*~UNljB>>(AW4cD#$69 zt8b`WePv?1z9F{E9k67A{;nQ!u8W&$4Lo+-D&j5TD%<;69E#uyBu~#PQ#H%61)H0P z&E524<(iU1{hIhz!2-5~*s1Qdu&4HBIkivsK}vILH*lJFR0ctpQ%D|sfiq8{JMzBM z8gLm%um*?&Is_PNnZ)*QBeG+qAWm5Z5CXgvTG~{D$T7>7mW95dy&Dt)kqm^W7cPf; zpa-J#=uP1$f+Nc_#kSB{Yi@OK^?sIF`H6{@BdjS}>y~9Gm*iN8e==80;@v#fnmrp zP?p{<&Gs(vVe#9o1tP|u)iznBhQh?=$I{Rxcd+s|`}1%ErkYPCqaj#~C8cyr4rR)u zsUk6~J8rcG1Q^)@SWg@*%oJ--lp>;}} z6(zA8FkqpP{ws-k$O27;H8(4|RaisZ+H_QBc}Ox-vb#(%As>@v=7}X#IOLfz_I`I{ zwS@<^_XZ=wfdOob9oR0%dO37O8dnq|BsUmi4GpUczX@cax7glgKFP~UHWR!_7A$=e zsQ57)Q&b2mR}LMYRX8cd?mf58pwv)d5w*8noGWFr@iGG+IohCl3}Ejb_6M4W3Po0S zZd~669|;)F-HLj!dD-oY-EjaS0!|@1eXQJA5~5;!WKhgPc-4o;aj~NodTLHA~6GACALWEk6jClxuVCx6)HgjZs>&Cb00-6~^3LGV`PG zM-br)!W0*?pe6Z`VrKe03sv#9?ZWKBv>O;8?_{ecxsBp07DZQDVvT7VUZu)jWFB6z zh&hpJ)fqG}x5u1Lrz-@Wm#lpxfca*w-Z?unFI~rQ7|v}nl$kIhmc$jxwGt9T>I-2j zSk9ZC?LEhLwo6s=UG$uD$EpSf6m5_pk1Fa_+iqa17uHwT1_MG~gTrJ;E!hsFTOZgV zN^#S`onl>HB_;8&oWH25VOFM1`3dbd*#zGDYMw@D79)-o6l{_BDYaXygqz$^>J`OF z$vrm&UXnjHuo78_0wK{^t#M&k>m8Z$X8l_-%aoyFmRw}nWt;>eujrBrf0^_pwQ#pN zfFY4Ds|%BOA>yQ%(u{<(8`HAjglYZLEuqO~3X-$Ei+xTd!0Hb4wUnK5D`8^{rvnwh z&%=n9W3bP0WX~%rXX}C6f_oWe7YRyR{v>PXEGVcdox^Ew4sbiPTk52_p`8Z-$jO(r z%XGX<28B5ck6nxSxe-b9XPyl^j59O~QDNbWI-O83=9+>`8Zw1*yjw8P1nGlgDXf;2 ztz?6dNa$*|A+iiPv%1oaoH;sJI(1mIN63y85=eG&k!!R4-89|hYMa+EIdJV2=`o6= zRxqZlv_NZ@+7p~ep<_9=?iQP;=~uIeFMRW zm8B@=D9Y_B14Q;vj@?Dxd~b{W`5B~168*QHWLeeGdk32ilbvVgW8coI4jsf^J3>` z7iYLLoawOPCK8AZh~*;;_@av7Kuj3l;HfX>BpA?HP6!mhvoOg@JBdA?O*EFD<+(-_ z<=c#b+8d;d{SIY4WC_PlKt^h(Os#DpVbHxFRWnmBIm4d5g&Nh#^;7>nDVP|8;F zCZzY#6&6=1UWIM2!gO@m^;$H3l<5)7+Nx2>z8f!|RULK?QBm3~_f~4kPO{zM#%)z;hdWbt;J8HTeCY`TI@$UcuI}ZZ?zs^y{Y`JnP^I->R&rWVbU`@%1UQ z#Iip#5DbXc$5)6{(mHIQm+5A(aiO48f{$vfziqJYmIeWf7OR^^Y4G*B>@^asBJYZmvIK z?B)75j61mg9^+1~KWf~~^@wo~*Tcpn*KahET;FHh&-D))4{-fM#zC&%VLZh3oyH?v zzu9<{>$eywu7B7##PyFDPjUUD#?xHajAyvM(RhyQA2Xik`d!8gT-S{kxqi1{aQz>Q zm$?1`;}x!NH;!=q6UM7t|D^Fc*LNFla(z#lasBVpRb1bjuIBoVbPd->(sf*qrh&S- z>G@pWkY2#`p7cVlkEZLn9!sCg^|AC)uHTei&h>YvS8#niy^8DaO<&6O_obV;{&(p% zu1};pxxOpCj_bFjd$@je+RybwdK1@s)7NmlFMU1N{pmrj_os)r&eOwO-;|DU{i<|~ z>(`{mxqe;x7Jq6^TJxu7r+4{N^U}Nhsrl)<_$BGj;qRaPscGpSqdU_-r^~<6C5uaD znm<*Ondwg*mpRU#IzDrPKUJHV>rd5XPVuKs$eiv^otQbxpE@bC$e%hn)8tQ8XF4%Z zG92(S+x)4SnfKG<9sbm;%opkIQIwi_7I$p&lNGoMx;ev|BNkDuV{=XSH}aw#T}JbV z#y{pUtK5O{TgfUvB7eRne|{x@{vv-CW8Ndl74j!6fAER^-Brn^=ueOa#x(k!nwlky zfY>xK0RK70K86J2jQyoBQMF|$q$GDB3H=r(nVs)agRlSKNJTlOrmBhEx8gK4di=N} ziLB6Z1&%V5Zm%O9y_=evY5T{}^W7!W%*wgNP@<2NOjMKa4a4@|!f5hW8tC>4;{>j+ zGaBh}HC>wNvX(CE>C#V^Yv?jSmmOSx!uSYXzQ!*yzQy(HjbHJV#_zcvF#dwq>FHeG zl3vX9$J5KWK9IhG-t}^QFdgRlq4Z5$53xq_jLR%!QX29t;HX8(VzeD`z1fP`l0sy zpZHqh>8_EJY1~K}j~n{8zk?;;!}T8}gIxb{5^a0Ni1|}KW1KFSj6MUC(Pv;X`V34) zpRt!Nm?}QwPP#lmmq+*|Y;w6T1@VF=$K3+5ypY6!G0LjV5yihGuC`DA1`KfNLPCE)dIYl)nOa+%(n~ic6J9@G^L%;e7zj6 zXLisQ*9b&vkPNWmt!V2y+>zwfjal$j{9b`>c|rVEvgv|t``88Iw~7VC?=(iDz!hvd z`;+*c0di97m!i5wVip4`V-7Z5#BWU=Y6`!uJk;dhVtX1D;1elpn1ol&OtxRi&KIT5 z%6S&MrZh`7y{280$&onIHayFOb*8{kW0l-Pl5 ziYLI3y)JUqu`1c+;%N>LDB^+<*vrMM<7xY2aCFqPB^B1khI|60tzk3FRFvIpiETZW zITx5Mv2)TmM9zv{%ER_wS3%^f%BZ4A1leU8$u3nSyHt_bQbl6R&q|0b&ym>jFC?}+ zYl|&EC9&lvrr6?7ooa0Ir%oY)cbaj%KXtk>=ue$t4Ea-M8pHn7LL=f&okcVKY(pn< z_z`}o@mc;Z2p$(3PjdYsBEoRSdaOoCsQ`3L^4%;)&UnQx*Q8At=uGHI?qmiZqvC=2#KEqf+1vdM;u z)HIWOJEss4D*4Zii%(~j+mHfXm5@JQkUu|=KYx%v$6+#y-oztgpcw`H7Oyn0Kbwe9 z8ql5+X~f8+&!9vlKAS_85`emuNKi#nfj)y0mH1q3ktRxlVOS!Gm?>=kdcjkt84#eV zj8nM&J>y~^)M%kgCtWtsg=8n=TDokb;gZ1E`2xny4`?v`#GhJV{DFG-IDRRctf)#& zFH}XT4#XJf-?``@`nL?f3uemuzg_=^fxltk{}2ZDf2E2z-E?AdGl;)UC;m2__}gVA z_?uA6&nZ;PCkxc_t1Yd(os{x=DCOD|DdiQmQvPjH%DVHr_okoXx1^uux2B)r?@K?&-=BV-Yg+mRuIcF)x%PKygKHD% zm$+*Y3zH;M#{X3%T}@Og+~=nmLzicV?Dy?PHncT)Qi?f_G$AaqZ)oOSyJ;rkQL1 zkZI%ECo-K}`($Pv*Y3&maBY9a&$UlwHgWCKnQOQ-`$A?n*S?t9%hzV^;GLN}c~|CcensXUu04>M z8P^VFtGM>PY&F-O$kuS}$!s0hp32VQ+V`{b`TFbvuKja%A=iG8t>@a) z*>kz}!|YP7{V2PfYd_Ae;Mz0URb2a*?4?|LHrvd#pJdy(_FT4;Yd_7dF z*3Y%)vzxf~i|jRA`(^feuKg-I$h8-;LtOiHc9?6w$ws*Luh|&aUd)bj?YG%mxc0lO z#>XUoX7A+MOWC`*_HyE}A5PA>SD-D<+tBh){Pcv$`4(7o18O9v0R~w*rGYyy^XBi85 zzX1#6Y+@BPU=;x};2sA9E;m+j{diJ@YKc+QK@r+$baMR!;u9y59(0o7=lUFC6m!XZ zJC8WU$>0?FeBuJ&UZjXTy5?9x~o8B1W;8 zjJNL~#oj6?id*lw>Q(`_S} zZkvcrtRU0vg=D&2Nv7M2V7lE*mfKatB~}xcxR_M1OUQD2DOqmc1#kbC@_(lX!CT zb??`e&2ZaQzoa7!-fWCXZ(+ z=rB#s?zw|%`#77N%Tlv|oW%3{Ca6uK=BVrb=`1;m>%U@?SFzM=!rsK8R};@Co=ZHQ zsGj=p(h4*+27}_`B159jzvLO;&l11f6RWTORU7^;=tK>F^L_Ev_k7}UU_qeZhfm9Y zf4-brP{)#|ay@A@z@DC5#q}3N1))DC>ZU$Yk1|lfW|lgRF`f_=2EvzVH!o-La}$px zB6W!;L;UPZ1Oagg4SC5 z^((5jPP{xbHXGLyW7G1Ag7xcGnm1=H(yal6aE3 zi-y3+yAjah}pP;=ocNPjWigjUrDoB0*?IBlTc2#LT13g^#DDqUFh~nNz9aABGxSjs zcWUf+@hCb}j;|g}j>NfB9}^vG4u<HUI@YYMFEr&!GpKmsh+KXv8({Ay^p*hzQGa;tQ_P>`U<9t=}t6XnM{+K3a zmM>0D@pl}+lF*t1ZCr0XfF+@}1NZX92Y!dY|Kf}9^WnvPjaj<6dh%p|19}v+?Ez`foN;i}yX^N(68NzDWYO zum4p7X0aOFY9>`k+&&2m`BD|_B1LmgBL374+ES2Tc^k zfbRL$tn1m=ZuHQH@lITQ<2{KVC!SpL1QEsFbt2tV{K!=Nm8tkkQ*om&;hXANaCnUh z(vN*V5=qvX-C7L8rnR)p1RPX2vGBO0y9W9=ktVvknkBKwcX{$Mu75t+#+MoEumGL> zB0oR*6RxL{|IN=oa58SvEAg6o{PPE{;L8s52OZEya~HCSr<(V)+9~Q@)4m0=P#3Gy2dbu~-an;ZiG^eg&*Swg zF&;Ih4^&V6@6;4AdXgU?oP^lq2^Fb%<|uikkNDjyd%BN6QfPmZdYp{ZCd(@r4SU*O zCsZ~M%Mn5xP2BBif0eR5WoPT#*M9^l#m7Xul5c3#bW5qz|uT4wkB!p`%b$V2df22-w(9e7xFpDppx_nl(Z6F2(@8QGZT zQO|xpxs<;%c`-j1ox7Y&*YlDAVyq#qznC1y9j$N9OWwxy-zN8Q{ddWa^9CbHufD>U zC$TR4`Q+ogG5LLTxbZx_#yZ3AlZQqBFJq~b8QVH>@p19fCmx~s!(ww1yTwH6k}}P- zt;8G!Mr5a9YTv*nm#`Gf{E!NB0>(U=v_oed#Qc(SgX|FM>spq?YV;RKmiv5iA#Y-n zah5tobo#p;V)yR;sl*Y;(UN{Aev~h4C!XJPT}|Q*TgraW;E%hu}OCs^SSwkb&=0@>2q1~G_HRsc@EdF zAhgX)!V_f{oBSP1L0lal&em3&ZjHbGJP^Do1e-Ck(bw+hll5hX@@T;P7Y^Ln)Q9?C#cds zb1IvY_(D0t*D`X@I>T6s3yqX*NlvryMiOstA*|d^2>A#QlKc$UUrK(7>n|rC zu7KCVU|%U=63o7`QIS}4+)nm~&9 zXM9fL)|$k%)r3WIxG}N(WCC>DoTj_m-_UM-gVpXj72hY`Tf^du5iuUSGeq1FWkeBsMG<(TtpT*%tqTrcO3*b^ zQcnC#KtqP5g&B%dGs%Nz|D%l$QpO*M(uwyC~mIUFXxT9G>290f06`d(YD zy>~gQz3s!ObRs@~2?S^pg_j(;bq-+yMB`k1T=E3e2wz3}{E1s@*sar`;I-Ez+UF#m zLK#c`dE$T0xOI+@eyS&4Is=~$PoH?~j6MB;ASc!(?Q?FOZ9VL1pR*4&GYgWp)PvVs z2}p2@g#nMnXIhA^72vP79^ZY#^hE3Yo_*afB>L=9><$$@a!Kp__=$=37jH}5Is+W~ zPtPBo-IO{!BhmiqlJ*yx6MZ$>%^?<_R)O03&O=j75%OcHZxG{ZdR72QjMlyO0V#!4 zK=iwdWWhiE()Sp~XpLk@X1+KLiK)oCL~H%kOCAHVYI<(KIIt4+*G)Vn2kxHsI^Zn# zR6~it=^m1EK$I#W3z6(Gh`jro8PVt@*;F1(-b@!*;=Y>vBnfH|%dSlRmM=Q6m@hfd z$yXfM!7n^84l(Tm_`8P$w0rq7Hd(_`=V0y#6A>8KG-g!#eQfeLma50Jz5s=@ioEx; z7^Nh;k)R<+jC7N;;DqCNdk(wzcm||Y!H);yg#HqQKlx8vz)mgj3A8syFN*|5*=}&f z&(15IfbBk+Y3w9~M2-xF7Q)SKATYd;H?CN=ob9e=?2VcV){KdKCIVc}Dh**-c*_k) zaT-!o7o_Nqg@#96zlz;f=2wDVn3ozbLILs36myGG$WrIfd+D>UC}JGLz5GdyTL8 zH4&fWF0N(PE-o|IJj!K$>KhuyUKsNOP-K_V-<4l;S+9ZNWmde}g8JgUwJkLNy; z7Jo~XxNKh2*XP6EjQaXDeOGn&O>b+b-)cPiuj*+R&$yxI^)21<88`IYyJ2HryP1BL zeD3I4XT6!;w)X#H?|b0mD6acQe=Nx$TLO$RV6XrQ40eoV$;N;I$^u zq_cgttP`h`EE5d7u}Q3+-H?W)B*Bf6$b_`CQ3y$##!W7S0BPOUB{X#cP2Fbi&MCAd z2@N#yFaCYs%$YVOztw$Lr3P$(ZCAyy~S+0 zmR%>&p=hBf26FyU;!L4k%-sw`-6{c;jQ?N*OkeAj% z^DoW3eT)@Uv*~u`^Mom3DF*u}GJUr(PWCm!remW5{WmxgxXbA;)};jBRITv5uAppD{-M+#Z5W)o@6f5bC zG(Re6X4AJhL&CXa1j6Z!7&ev}F3{f2rf+qIgLQ#G8s9buvjRGHCet5df9R>-OwCP# zA$n_>!_~ROu?wOi%T6QiWr2ROkWK6CAy4%JI?%On+LP%yD*q(O?HvFPzq;0}?d?T? zj{+SWw`b&Ljb-$W4o+#hgTdF32lBB#I501F4Q&MI#2mAfbcihX_C>Y_iE%J*T6;$a3qSGvbph z5UwYNp)x(zCr!JZ!(G10-7ae!sFehe1OFK9Pf{t!ZG0+bFS!4c()|!*B{ODtfx+w`tB8u4<5MQUiE5Fh31V z?cHt7Vksp?vK1UIlmN_nUM}mofXx@{-7?Dx-*&467449ndI9#bXw~Jgl%+-3iAza3 zEYMXl@e8KMT#GSm@XKW6ZzI(&V_B)>J4z+3LM5$}9*b;PqIUjJ*P7Mz+O76i3uhyC zrnCxl_xBAAnvv3jY;Nv!R)cC}gZ8ni-0AFveGJYdXl2hT5&G$AQ5-~-^8|UgQX)Ao zlVEn^LRqz8=G{fkHKK|ZTMpmgrVAr@y?~YPcBbuX8_m&bdWYx?1rc+{ zEPjzJUg1eXOpL`c>EEZB9E*tK)@mr6QlxCR#XC_JRS30dw`Ps3hu}g+Kq;*JIcY@h z=^a^yI7b<+lAhP|fTm(ae&N-$+s4@TGw;vbq=xpZwJcR{R7#%sXynWniWeI>Jh z$(@$VFh6ERD?0jyWk!M35jN9o^yVQpOF9!KirGa!QJPFj7-3~=27)bvqa$0`Y>Z?1 zVqJK%Z=991cSb_Zo$Nw6Qmjyzrk%Of8anC&@vt3&q&#zofzd2qf4so99k=Y%f2WQ) zR@x&#BB;T$%ZHRV3^Qj=w+G~a&l+Q9x}0Y8V;C}ybPm@a-ds%lRvd&>I;&#!;X2t# zKgM!lRyL<|ij?buoH)th10aWU0((dEJ9j`ZTa4S4O<2guhZIMgmqE zF4O!KJI_K0B0~|W8s7{$VdAL!LFsB7&r+@`AR1WPOeqz9CsMS~96l*O7QZe38EmqK z#lQ$qTM8;1;sL=Rkhu!PY!L4YYj1ISgWn=UDKZGr04!}zJ(`ga2D}dn8%;`BG}qjl zJH!H_000wzr32f;YvIM=Jlspg*VlT(jZ|Ge=`SyBiK+41cvBa8w72Ao6p5JbB{DoiSM!xP1a0VXt4qFr(!l) z%`D5OlJm1g5>}JVn$9p_D2+@rTrG7-KkzY;i?P>nUuibr^lv!$rawYQNz1w&2o!0e z9zsr-ThiuBEF0!iD67HTQtSrOALCd7i3wflPa&FOvs=Uy%YU~ChHN=!wX`EDmE%RV zc#$rSGIP|37w8DFU!d`2z98Gn*)0Hr6%pH$v^Qk+IHRQnK9S9&3-O>FJ{@ccl=*xy zp3fv>(@+?G2hE2|@vsyYq;Lu-iaLj#0HVp=!{tap_M<-B$?d~F^ZsS?{-5UkQVc74 z|1tAUOd0(qIzo3;CVeQZK!I6Cq=nQC_{;S5P||4jR3W2AqM|CJ(lsJk3H6XaB!_z$ zE6M~;ok(KzPSl_N3W2OpxE~@r;SiXt>dewid4~n3AQMg@yIJI~B<{o-62vlC=^c&g zq|_+4fJzWY0ZbUO+j*^gS>-$=%CU@3DZNZu$!26$N&G5cGi{KCWOPSOoaGpv;xUX# zTSJlV;Sda#p)N|+uIHqfJF4bK=_$Jat#S$}WDQ`ziFhQo^jXVg3 zhRnz1Y*terJ8-M8OEs-^=8&Q<)G2I}opsQI^+6501zOl^poQHECG0lnVBc;u^E;q| z-3|@x4k%!+HT*c#HTw8MV}w^36ZlNx^HshG8qkZ3pW+NKj&qj6I1UF3!bBBH zPe5;{luSuy!Lp+{v5|gl<%?iV+MIxe>g)tmJxUX>P_2NS#p1+k^y}a7B> zY0}!JM)=7z@e50CP=j=VW!ezT5dOFh!8*}@I31CK9xI(=h>QM~Qwv2OxPlq8d9!gH zG|UVjxztz>btYpI9u3j2hxsBl`MCmHTRekI5b2kHE{l)wcQJw_Xv@dt&GdKi&+->3 z2jSfkdFzq4hviK|Cf6{h=!1v!r{UlMi+PzvBIy}O!72J!N$ARN;OLVE4y9E%SO-dph2j)_GExMbY!vq8yX1}AM^df)a9zdZw8GUXo8%a@V$lGZ}y5`Z|(K{ z1}X_=h2@u}cZ-Tn_zd&S(tmhwXVLDX0C3z&Yd?9zdeZt>tYY5}(ANzUz9TRDB#L@Y z;I;hvc9edYTB*=>6lE@r9=qeQ*DLmsU!JCyeQ(iNiI$4mYP){HBIn-hJ2_hNQcDqg z^MR=Etwp}$6TVkor~k%}|L52%ukSkisi^M+6e=cshhC@u^6~b=KmIlhI9^{>{V7rS zv01~I!0At3-*xm;uWvtkY*qv>M4G)VMY~@v-u+VXDc@UnR6I5YjGgkGIMs2a;<4rU z3jG;pIsKs41)x+mceU4X8P4nu5ReVGIYIr|7xQwrlg9?;GNA)f? z$%@ISL-f=>^i=fc`*%Hm@L=?Z6TZVy-(hIZFNC^#2|g8XUV;bF@%f8(Jzw|BHy6~M zqMyT6i$)K<31t%Vjs5JgQ={cDvnL&BEz|y}mDNyS@x{sV`$CLORhjQV0JC zes7g6o$x(N3RmZX4YkzT4nqZv#=uf&)V%zOx)&C`aPupVR8cwdc{QHDWIac56?+Z? z52-0j2{UG`cwmhxdMTho@eSWG8|XF%w`ZlQ3A6{#6VHl8edWV<{{@c<#^=2IW4aH| z6Qzk%834o+0Uk5XF4iOz;B`(eX48 zZ{ha|8w9`OA#rq527njS0K{wnM*!dzCxADH&opO7 zzSF33wC7p$lo%nlJ|;V`2eu2OPPqqOtv(F?7dlf{OeOf02j@axs)k>Ca1p=Z;7)$U z!AJ0H51xISUwrTgek0A(<$U?m*W$ho_W<=0v;aK`%}e`R44c40dPs+K$$M9hwbuKkAKoth3GheQffh!k!Hj8XDZ`rHpBJ zEG@9VD!|{6Q+LmLRb}0H7~)S zf$*vlW)W9RHDUb^@a0CB9_&J2?54gbD6%c>o*}*E*C*~hIdSiM(b4k#y9}%~Kij|S zt%C@%n{lkE^F zrsaiP=>b(``#X-=sJ4vjqObhr31mex&E%1Dw?ao6)RCR)oFkxZdKtW;gu6akilr9-J7Bfm}}bUW~qEGb69yiT&fq)?%WkoHC2{4a+)D_>M-K z7K;KVe6In1O!NyummD9UUl7HLAd+bPOW%F7n6AHUT3S}|SR+sp^%)b;!S$V(XsSwA zMUK8V@&3x^N={ASPojzej^bqMLU(59{8n@u&q39X0EKodGG34WSW*(@l%Sc@;d0~)69 zm{c508z})<83<(FsX+UMQo{_Q7}W`SC+*UOjL3 z0Tx^L!+A$%?K-}@x4ggcTbJDbTx7=O4W*Rmz&98vw4XP%o^K>yNatZkeK|}v>!8=Z z3M-^Z5{hZn(ccRgw`#ZoH zpCs-3hf$set?!CGGsV=a#OM~;#6`A+_&B@XveqUbY&4!-buV!2{fZqaxt!rVgwF;JcOO59sh?_F7 zQvDad+a@(=5Ikv+l~Lt9j$o@52mRhVLu+txq@chNo}m zD-w{Rod0wHJWm4aHvW*Fege$Fr-`)mu^8lWASy7v z!RB*SH5h_4TenrfgS7*l_2xy?~ToJo=R7JnwIdJ!C#^gtrc$#h@_o@W>o%*|;~aA9790s@})r{%IQkm~t) z>lI7gPGPB+XXmt`d};~kps-3$-1 z%^lL!`E9cROV20+nv1!BJaJu;4ky1w0(4Oji)P91?FBIAPv2gk2AU^Xs-;rB5qhF^ zE0;qDb}w{bmoTV|IaN?)I=^pdbh}i5^hNp`kO60VP@-Qf6f5d(@RW@e+~Ofmo7C?& zTPn26gd#4ak5>4R{@v6zT?KWD^a@soCqMELu26S^FF7P?<|+xTy1{ zy6MJxFSxjoI`ebr%$&->Y0y@%f+jZI%McpfQfPP-P?ksG&F(& zm$2y`*6CW=i~-kRV}016Ywbs5vw&GP5qwr~cm9Sr;o8bq1xrbN9>E1&hCyaV(g3iP zB27$arwahb`rV;XC?c2uX1VC!n|+X7186z>`@)-{IV^OYP2HUU1-do6rh(n;y+v&! zq&F=gnjmJg=||WOk03}1!H4K)6lQ3|yZuCPJg3fW7=uKIG=BOfw)`|I&>rX$W(kA< z%B|D{ldzYpIDbPq4H)<3&v zejmOwi#Nq8guM@x(1b;(y(TqSio2#A!Z=&!n73{j)7IL278xNJvIyIl8#Bm3)`DzW zPAn0--+i-+5GXpGd^poLh^R79eHErhG4D|bdv9E{0&H`LqmuU z(li<#35lqGFz48=tj`z%ERtF3=74Vu{8wm|j#P0kZZAPZH z>}!diA{90&bu`FUsj?3Rw?lGSmji>T_nCD6Z3 zMq+QGuR{M<1+^~INZz!FO66zERHag=OB=;hT*zA z@ogWDh%lD-# z_5#+_%WK*a92@{(RmV9k^s^BrVya_2^lcYbm|;w181h-tEAT*R3E*cT|5TQV?Fj96 zD3T*&EKCs*0tfb)kzl~ISGB0Dz1YJ(mh3n8onJJr1c*(?cEF_l3z^&!eq4-oVLX}H znATuKkk;BClbDg&9^`n$+C;5(M1KbkMgMoPrf;u868gh95MyW4b36YmHxmWg+Zf1IL4Ih4&|_t=WALJ-0`rN;=6|jgAvd_ z$l-$z1&9-%ngBD_DrR!(!tz~JBHOp1XpOp5Z`+V<9sptY0n2+KRLr}lQLcgxS2el1 zincmJ9-g-(w$)p;v7=Veov^ajx2wy-wz6+{#6!J33_S_UL(SD}WJoUccIO3GEX`Qq z>zwjB`L@^O=;7KdZI!>V#wkV5)qjGwXntVuw^pWV$BOAK%9K-!dYe{~<3+c5={4{{ zVxtzs^xP?hA#@f4!Ewk7mB6kOp6XO1EK(a}E7Y|S`?riUDv+uhyyn9SYo0t)b;gup zIG{}wNMue5obr+#)~u`uon34;hph``^FXT!3LF){d68_#dS$sbi?PGrOQ?Dzh%xet z5-?T8JYNbA*E)QMoZU>a_iDB`BPvXYcwA7oY+2?iXKkF(l3#dg31tv?akj+!QP;R# zZO5i-bfo6&@Zr+vaL}#UnBsGbu2VMLTExec5|QQ*OvzKp9p~IRC>@fogE++YhL+7x z+OO|`Q&85XuB;gL9u^Vxf+S~!U=qyJ=TSEP#Ba${M=ati?VqS0_st3{;^gxJ3 z6)mnR9~}#M=mzp);sq9aLfNBGI-p$z3F#o6@4(4_P$>xvI(F-D%#a0xxWz0Nlydis ziBal-JEWS~M-5^7+|DLP@}=|WI7fE^kQ5`Nfg8S|wXJC#+#Iq8yr8HR{<^xs7Ar?r z?(Nr*mokKiscmQ=AiUh=8zR|hjC>Fe`QF)77X0dBML&1J*8f2_@ zr8K-bgkGV=usgC-9?OMs!ZPZm-Zb#3KUCEYhO>vMKNK2_1c#~M)!;2~Vu8|`QWQ*; zrDDzOzrB*#zLFyKKEW*AHy#Q1tq%@FwkRv$HuC~r5&XYUOlN3#bhslhG^%Wk%hJmR zytJlg1TsB1WqTk54^>jlQl3tD)wR1*S&B(rto>(rJbR&)Nz7vbv z4D#W+Ozi<%mTYpTq|zs!p$R$8XF{R6%p>R&0C>m7Sx{ei8_D0;r%$84LUGAA7}$xi*syX={w+49YJzD96G%+8FJ#cE&dw}Hu30OAzl(-dKh%=->PKuD5 zmBPxjuIT;Vim_O$GbKAYc{vZ0N3@afCTMx~Uf1TtJV*-8eoLd$?`#fr1-4_bw1h+3 zGj!nc4xBD!7j(fJXdnzv{MG`44$ZJR3X7lJ{xu!G=C+=W5L2J&TNXX}kIB?)UpI^| z&VKDv>6NXcaa7L0&`BU|KR?BPfTIntQY2R-24^|&sj9ck~pRVQx1kCL=QSkfGV zV#h!$Nj)W{%CpKLgoILim;^8qCie<6Epwn_Ct2%NVG;J9j+9oA3ggMup6#%LZ^ILGjB1w{D^qgm$%5!u`fmesZq%;Yd zGc;mpU5+Vt&&VjWK*b?}Y=*mzo0H<7rWR=k3siTJN+E)3Bs3y~76(;nOsRf=Jkas( z8y$?0@Sj#6na@L6vI9@AEGk5X=}61>A;n-s9WzgH!g*#|_AC1o$Eokb{U+E=kmBHg zh;8v$5ie&}dFlZblXdEn#Pp-!h+axl(gENQfu6c(2Z{p^u%)t_RkD6$TZ@D4vA|i$ z`c*3~N-;fqq>T#I2_iC)%$ZpBOx+pp3nXUMtRhwAWT+@or zN0d5jGPjf;rzup0m(@$RA`Mn>)CT|h4v`GU7%s92m2fJ(EK^pUuN=2xR0Aok7%2(P zSBPt4`W4frwQs~5Id)QNNeRSJFY{2y+3!eI>VT&s>s1QHyoz}-AKHslr|NAaUqncn z#(@E`T^2G>nilTMc)p#2)3HPr%V3o!m~l&@kQd@jicw-i%rbc!Q>u2@c|(W@+V4?# zu5yKuy)aQb8zT{z)rm7PhszUJ95n|=`n^{ygkY2^t0}9!XCfcDPSFn}eYY4=)hTO7 zUvS7|ZnlustGRxu_Uv@3*fa{ZXm}@~)U?7<2}jn3BEd}v>OhuLeaG4+Arun@n(=C{RbbH0Fvd1}N$O-+U4)J< zybB$ihLnq!lR5S>D*GyDqw*097uzBj)>*2bcHgcJ zHV4Iw-0h`i#GlYsP99A|i8530z*E-Z!SOROYg6gCh_0|VaAY&J z{UDL=oC>MqVM;TZddO8Q352o}R4TV}s@e-x zzJVC2C7HnG%JWr-(}l`F6Ir~gP!F8}mDWArnSvy$CM2G6QC2|?D$NMU-@!k3qle zVW$l2?9?b+uoqO$4}WO$=E0QL4%HCBF@#E4pl4`AxR3ETLoh)Kl|nN~q!7C3Cs-0oZaK2;86RyS)n(?xH%lm=N2_oS5NN|KqH`w(G)U8Ia#34D?B=Jxg z=z6sv z3DG!o;{D(ZXv^LxHOqq`H-9=_4vBDhAUQqTgaM}*`xSG8MD*bPsS+ym1QCFHCHXa4 z6Yd+{66}YFg&6CchCg8!4O$g@-8M949_)J=X-W!0;wzGXlJg^Ko||H?N>|N-Be$Ot zQ7+p?P#7%sbK7!N`6Ze zDN2^-MQMsGD{CNL$rwMK!STSLG)=)G_)mo@O3;Z=(Be4BK_^wStt$ZK2G5GwA*C*h zgy&_5hM0=1ik+<&P)gxRH`K+jg;z`a^NuQ&#Hu?a!_wyi60FM{8j>!7U{K~}%XgAm zGmDa21K2A}6)!7So>Y{0Ayl9l;&oGDtCxv&GsG4srXhTXo`Wz6OutHc?R-FKDJ0ki zQh~|v8gQ+2i|^3tmud=f4#A7QaGP$ISR+eZoW-b!Ty1U5y=}BHO?P{3=e-(nbTBCy zoxWx|vxA?_(00?gU4xkFatkBKj42jxa%4gZD~Dw3{N{R-kFm=KL^$RI82|%l?bLFv zMC6M;qx$JJ83cc6R?FO0)Mh3&U*?7k#Y#8G;2?!VCev~@9K_anQQqV!Wpj}jT%0Rx zcpZa1B!t!jnVBNBic(+-5edj+UOC`FH(g5+iDk#8vS0nJentIsDE2mV0f^vA69L`F%YIlJ`@kbC7(;Vk=oiD zGv5iEpi`E(R{-p!oRjj*OpdZV*t64Hb68l!xYVeDQWM7+0vAp@KXc=fXRaVeb6_mk z4|^D#iA#&`@bHS(^=q490pH->5IQ0dx~LL+SZl5cF;Ff#2HLbP{q!>FC7IW+pfAkOH7t5tDw8)A)l@;KI z=hC8W?qK9vYyz^5StTMY5z{c#Su$#GrVi&cM#(oeWnuodPU_cDbL`|9%a+m|+}*%+ zs%U23d79e0$nL({z1^6jpxmR{qYFn#as zY`=-!2I+vv9~y;vL*EciicK*FxY>;It@Py9V1$&lhi!kmDvFGwY zikRt5l`{b|OGKVog4lZW!F%3Vz^X~gwz0k@yuxBM%)N`)$(pqXW15{kYx`1P!<Ngn|==?!*(}oR3WD!SA0P`I zY13vgAgA*Zmk6dmH?6bw6&oa%e<_RMRkmY>NajltL2TYc*ab_A zMA89@3N^4!#DTC2dq5Ue$!3WhHxR)9RkYvtJKnc;`MP`B{r39-$NSZA$P(xqvfl^f z`+mqkOd(5FT^fWh1wpAq+$w~TfmKoOnx3|12;r}G&({lCepg@nMSq34zO{`23waW&H{@Bz687Y+( zuDAL#N5DJF6fxCkSL4vmj5#ufb;)isB0ZYXP7RpIh*u#r|A;5T2JUFn<>b4uG*3e&esD6&J(`PW>IY zlTDRUsOR%2aQdbGSmjiWKQ?>ndaiGpx{>Rfr<%CF#c1`%3XFETbkfC7mko5;NSFKR zGESF0T)&4+mK4SisCNTO88Azt?=*wA;`6lm>^0oyo8M38Hd`b%8^$Ptwfr-q2h)R! zmQ2B_`Kl7HX0^bn9)gENgk2K7n8AL6YcqT2)Rh3b8uuRa{-@^s@69`c7-z;ly$u-| zE0Lt4Nc<>cg=OLkE5@nkCNpGlA**O~R;f_*9Lg85E{jC=N;7wvm3tw`6qMCW5uF8JBLVs9Q}d8NFl4DpOvgg{1C6^u<~Jg*Al?e78oF)HR8n#aiSri zma~FPsD*5qNK{P9WG;0T3G9-A+5<^uFn_GjD5kMdPTwm1u_B6aU1-dsOBG!fay?=! z=K7$qlZ(aiN>qm}Cq8tq))W^{5rZ1}l8Vm!e08e@bm zf5`QI<4*}5B9s>ze@T}Y=BPY8FEW(hx6oj1e08@^7xG zqf93wh{+6|UB-&WTQNWnR0?!i_~H5kZ1N5k15konG20Og7tadr>Ur+PAI;g;JW){o zp~u;qb6tv`F6~P;`Q5WR*o|JTk)BG zJ8~p1IlKq>0(Zh78D?wnxf7p1!{;mbd<&nQ`0U1KKR$nq&u8$VIY~r;{$k?FT8`je zEx)rX9PduxxtOuIJGW!x}Jhy>3g5_}1f;ERa_U+N&iQyu=;QR4e{?vp&HfQRi{@8_62mGLrF3bh%NDvsxPBi^q4Z%4%#5jJ2F24KEfOUy>vljd(M(Hg!?`UqDdm>dd`yewtd{hcu;vQ_jr35e zliZy7m`XYiU@{Nq%$HRH7-^$WIpo)yUd&`$(cpG`o5Z~XQ}t!sX&qXI*L1(fyno5O zUxaZ<&wb`yH}3^lpEBa(t^iQg`XuOlAuDM#u~Epb5xGiD28!kYV`b)ofZno}wQb_X zOlpdc4AB7+QrXHFMgANT1>tAJ8a|5W0-ee5( zD~$-h${6R>#=~6iGBjQT$-~vgUVe?Sk1sSH=X#;>1Ycx4$@Rs?Q(RwUfbzc2nBsaX ztrKm=vs}O3nCAK&#`9cXVI1Q64aQ-v-)O|R-fq0e^$r6Rb**uP>o*%mxxUT-neL>O z$P#9Y*~CU*Xw9^S|6|H`ttZnuCI)* z;`%l52Cm-{Z|3@~@m8+i7H{WzL%fsgjd4HMTjCqI_RsN+{QCI)d}(}u*T%Q-W${5? z7a!*JV8xe%6JHU3nBNfB_{#Vmeq($uzbU?t-yDCOuZlmxZ;3z2Z;e02Z;MazhWHe3 zj6cJD@n?BUe44L`KhImS2X2cW=C{XV{EqmGygmL3?}#7aYvV`xy7+6nGk%QUiH&hr z{5bE98{8j1!F%FwaUOr0uaCdOH^krLcg0WgyAzDxlPKmJ6J`9~L^;1NQOSD~bNKy< zdAu(%pFfbO;{AzgK9E?*1Bu0aQ(`IKoT%qp5{R9cSjD#{8u&J>wu6aQzCF>-hZ3DU zl<@Q6#0LIgVj~YH?&l+k0Uk+g;iFiF#}dPQMSmIf(eK0Z2wGSnp=i1)HA+Aj% z4s-1f5;3kt6EAXYU*Z+6?N1!x+8-v4a_z%d*0skI$GG;9#2Z}uqr`EpeKcWk?FlUP z+Q$+YFCS05&9y&Hyu-C86Yp{D6N!^t`(%=F?NiBOu0543D(InrlDOPifb<>pW)hflh1PPrQ|f%UQRyGwO5jdxc0r|VXpmkGRC#PNxsOnBj8}P zzfB(D+TSIQa_#SvuW{{Y@)+0tA^8T^UQHh7+V_(N*IrAW;MzYX-{RU2l5cbEpOWuz z?T5+txb`D9c~wDd=1I9-`G20Io?Qd3Ex5>OLexaQW&>e%pb-o@*WNISx%OkDjBEd5 zlymL4QOUJ`giT6M>iXqG)E5v@uOgy;1rhZtg;WmPT4HTW-4Jat+!f44CPzIiHc8A&R6FtEc_ z2EYykMCH$=rN5k({&Q*RKNkZ=e+UESjAXhS3BWFYY=Ncf|>u8ZxTuSGlFiH@F( zj+O6U^(Xu5KXH)$8g3tr1BkZk*DO*YG7o%MY^!#^SdQnt_uUhX-}@bQpqfpc$Mr|1 zs=5CDDWLUX<5sTM(AmtRQ){`tYf9s~#wI6N3<5AT18s=jTNd?|KNgD=*S$JXS$C=~ z*0Y6VpaZ9t4op-DkoYhkt6zp$6k#T?f&=oz8% zD&MRS;@WS`XkeN|#LKARy)TqOQYwTtDJdycnGihNxDMj?sq0Dn-bx2c9bDI^Hgf#~ zQ`o}qW|P}ktU^@zPO9^+^5|&U%f6EWqb=pKkYkJPtGxMN9x06e%VV!UQWI^d++VCk zdHMe04@Cc_=zB%q+h0Br?Wq)L3+}6|i=nS}@0!CRGorpZ6Z0e*@4EK|h?pP%pPC2k zO*M0U51X9JVt5F2MSW#JQYZ4>`vQj8WF3p0XW}tBT27d3GV9V^S@eUOUwEXnU<8TZ zoNLb72q#nQ;=|caB4Ot;nW3Oub#p*8JW74>mS0g0gFK@ zDh3%kD|&DF?)T4Qk@DzE(e8Ny@S7dc@5mB=5G_0Pk=KBu-M6yQ8_cJhb{~2bQVKSD z^;YH_hL|p5>_uPt`mi!!n1hlPX0cl zkA&eEUOJmRpT#gSWfw;qDrNVg-{)EncaP6!6(2ME{Q?%d$a+!n)rL7T`L|gNWL7r# zUK*hLTjoW-FIxCg%N+FD{+6oFt!(Sn;?eF$s@Q1hR+AJwGM`1((xXVLiQuUA49Na3 z8)g+p7DRpXqpWkkZ%$`-)Ew=d(U$q0QFf}c{LQ&(uYoG-xdrF@gxrlF&0^jnlkD65 z9dj@nqVrCDv>5LQd8oWeK=u*=xqqsb>wQzrgtmUJub%>yT`)C;i=eU>%cf$`?|wfj zU$OiBsQk=mldM&x)wPwU{-_-AUuv4eFyopk_czJTuZy8X%psa;3kdK^j7~P$$zqq7 zIK(WU2PEz$)XWvAsf?Ba36*x!=WH#cpX;rbc9ciCXi+6f6ch&okfz%*QRx8|yEFxx zI!M62M8KYtLgIQ0h4TpGcy6IA+EE$(C&CyV_(YGPQ_-ceInnQk@(6`!&<)g}F43TB z7K5x`4lEi&K-9QERD+2qId;xtvCBlVLxly!OpwaS8(8deGi&8D1r;BA;h92=z&%Gt zOLp%nXOYrF1xWhltaDx%Ej`5OYa!7ktbUU-89IqY8_K`KCd*l@3UWBHT7V+VU`C<8 zh)vF7u`4j&-bd4C|0du?Nf{EaX4yKCM0Zl9U{9QnJYt=)Q3(!8c?T~B9mufM&!2%nPPwzX~k z-ZHvU$tAtM4fLC?^qW0d%Dw=e7h_9)E5?YVZ^f(x>w$cB0cJM7wUw|7L89fii!l2> zS;EdgRd}k1O|O8X4AMZW&kWv5$4lrLIBGGK!r}O>JF>Y0{+vd)vw{vbeKQOyM8-@P z=6N$pv%G>k*!0!V05gYDgwF39((?7zuBcmi11r$qVQhK{yT&ykvD)?=X+Vab6tn4C zwpi555&SO=envJ{aErP&H!cP6Su@ylwW8WL*0O?|*tFm2gv_n<23y^37_Q(m6{pkf z3q!16J)2&MzH&oaMxy}Mc5qREb(>8;==7ZLS|ix(g%HZIZ?LUrXxotKX!Ufp;#h&7 zO?Nt-Ai0()R9J0=9-%ts2tNUPtAe}O^e7wlhyn5oo)_Y{*Z6;g`890yX_PDULDMG; zwFusH3)15Cq_b7(mFXKnc3h|uC~4nd@FCmB z>=-L3mCLMK4)QV)bml2z`+itxD;1lZWtw~11`LsP55OdPOxivL5v|S~{y7qxDZ?=7 zkY;4#!SUtpBRI>psm%gV19ri^81#yj(6nz_lP9$_rs;y8CD?Ze4d{@ItCh$y5C!*Q zEk|RZ-=--T+)TJ8cQ0dgo-%}&uY9(>B0(5oDO?NAZ;4ZzCu2@E^fA_yCyQspleRoS zWDlim(7<&$TL-I)V82X}#d^|qX8>wT4Vik&w6mItJp{WNJ9QZQ?j~;w8MDbPqs{(# z=0>j0glv0&X=rNc<^C>Ti?7SKw#jGJXd&%6*}iLFZX@hWGDG7!Pc^Ju*UEOxDuSh9 zdOMuy8-(#BcttT~LrS-Zn5~v`)s(~P%o5CB*52hdb_dRRDbSLP%JNBYmR-MNPH zwK8v{t6NmX4GxCjW*nwh(x*PHFOBexVKSzwC=-oC!R^Q`Q;!CB4h@4N4hDzEszWdp zQ+Xb5>Kp7I9YlWV_zXkGLO&4Y*b-$8QOgHOe;EGHr~+ySH^|Zr%YM=t6sD~*7u$D5 zkxzjpZ!yP|9x;XYb7{&ezNV4sOpikTwCUn>Ql1vMN8~4`33(^8@V8^5m>M1Omd791 zKAaYTtV30f2>sJG;@dtP5vydrQ5#gd2?}i=9*qRL{OvLvpRBlpv&$Y9p+-g!p9Drmkz^QO;CD>hc^o6vPr(>!dPj}FB(^%sF^I0w*w|A0F%i))x*WrnwulQ zobGFwDnx``a%&K5|5@CjaJWLz=AC=yd^NWV#AG`20zc}tg%oi>5LkHqm2^i;NL62i&m;oDPOp_B zTd+)`dzK;5Fe$Dmy47p((yf~998L@xta6`J1We;m(p9jwOv)Rl8I^#NlxG5p)evFen)_{#v>xTgb?znSD<13oyBU309?crwd~-H8D%FdrId)V_AMxK$Q5&yx`jf7sAe65 zOESfT6AK|^4E@_wz?8cWl>^x3kSI(_^YS5H8P~#uWco142P&+rwaO(j$}-(*3wAnR zVP1xwgyo^;YOandHoASt?z~`@=P+~lS<+Scv)BCD+gqEq%HLSS!s@{y3T#e`19`S( zs&=db`)Fj!0Vu5`$BSr- zFQX(=StriiC8<)nu($GIh0X9z2Y;rjc`XVgGACmQN>JAv5}q`@ z2{k>0qE7?op?V;lK@WsjRMFz9^3k!7hi)M6VqRb=qpy-9&S^}xE;*B{TfnERg@t_Ou|2r3jf)0FGXGvZ8;nQxz#+EN8v=y zsyLU>A*$G2V&KtYDB>$o4Z=8K8TC?c8u-*7s%i(r*~8Qi=XR0cFcrKSyu~(AI#Y^* zX$VoVX7=A+$!uRq(N?dG_Km}lyd5qk9}Pjs&76WG=qjjKF0-Zo3&os$%JLDSNFOxu4PEt(eDXs-FeUqYwx?G74SqtLh z%z)29M5t6RuNm3ajf$B^V$NKW0p&TE-8QO)xxUhN=%%8-@Lqo*GQ-dTk`w^R()NY72^Y$rK8^Yc;g`D*iEJTK627<3 zLgM`qj}`0)jgBA$!49$T+o~q^LMvX&fY4{hLW5C?oKBT2Hw>W5FK)E==1`Q4&H zEtPH!;dsZ3cnqh#Y~QU#Djk&O5Hw!HV*Eix9)a#m54Tyogxr$TYUiS`#nc-QGFq^X zEmezU7$j()vVRDo7mhbl8(v{?il*;)M+hEs))6oQDs>i8&gWOuco3JugH=j73o6s$ zvm2pKeb^j4D8)3RmCRI_%zOUt8gA3W@}Cy(OmLi; zC`jZGZop&?wdjj_7;zed9(Jp2A1_i%qfCH1J#7j-93BXGX-q?H#)9Ctm<9D#ngBUi z1s7OfR8BO4B_rKg5K~gieBFMQGE_Hi;#;RtVLv%L@X$GDTh*L=k6Q!0^&YKsYRSBs zQ={GkEDuP)vf(h#$U$`k7m^vE#)aw1%}QZqT37UbWfzKKgtQQ>)tQo=oV*-UqhAzG zc%y>z=}CH!6rBB*My21`9O?>e$6%p2nHf58`R1lmBR@w^OA{RLrZ*ut{mvv;9Fth zX-8RgHHhD8in7xcIoQ7CMQY&+>4n;n4@HaA;TwT~NMAT&i-aj#_D0rzEhNTDEir1Ez4QtBu)48>v3I5JJQ~Ht4`d2pDuNgJDNjK>=0zeu%v^dze)_J`L?P9m?jn^!1l7n?`>|c!hc$QGF(UJL zD2q-Qq_2jwd>>MGQRXR5K7KNL656LYPAqf333d~tI1tPz!kDSRK25bybB?EA~^5}Slz6W^&{I_*nW;7oL+GF0dnPWCbE9jii=Xr zD2HjILUn?OOvDir%br?HX&`!EATg_EQE2D9?0^jD1Hqx#2vY$dODdBmnJK4i5Uv1J zXHzn_lpm)lR3Y%7bZXMj0k`288Hm}qzC$F#F@}q5LUlHkUY04V&R33GF{*)-R*aMc z=PSguG5yNE8W9tedMPhCcAhm$N+6DUnTJBoen+Yjw4IKuS1A! z#ny%*!APDw>CFH ziv|(V^R7*HhB3C;OHwDpn)4U}Yl(BZrWmD=a`BRT9%5XmCHYvj>|vkDkA>KiUK&Lm zE6-6S70TJDd<4VAwg`rGR+K@z+wGvZyS>zm_!HX7$)o8UXie=8$Iry9O~pqD@HT-X zizQ4QX|C@gaCSjOq)y4PcJ>J~cFEksV6&k+AcbyZQc3u*d_PF!J1NX*Fa)PBR9CGMLZseB%Sz)={}!nlIi_SxkP8a*fz#*& z)k%|_s`f&ak7h+`NhWZ)@_ZHIbfGfPL>BKV)I(=ry`oP6&lDt4H6ihoi?Rx`+Khl= zAu6YhQX^Q%+ShLh1Ym3`=n+-S?GRBlEV?71VJECH<@yJn7tx)6JCgEGsd{4+DrX}I z!yX!z0lq{(IAvgGr$*s|y`XY__(Pkq*x1XDYKY(%LZvLwGc+}SYij-(f(cTn z6q-pQh4^g2%?Myel~lV3k$K2wU^S-IfXP*=hLg-CrDm23lQE@yQsxpGwdsm(V@jPb znNhCC;$$MtpUJptN0R&C8T$p**-eh46Nk>kulQLbIPFIhMQ&VtAFu75K66!fL2? z^upPd;0(-B<+5t$8*WOzKop{+aCwVRiK^g3R1B0ff%q6EL48rPf{xgps8GClI%>1L z_D)B+8%gs!+NLNF!UM4^sfYr!*Ht^ioDssulqXz`BQ)b>`74}l?hJ%S;KmKxJ9fRI zC9C1^ZAi{FNjy{rx?U}emFN~8dt{lafkFFUf5?{M%1kN|KlaU8c~qw!E^Q`plP#sF74#=!pOYE?He^aCq(oytL@IQln!@}EW zUXi2H{)m93>>u;e0o6JQ(KyuH{NM~o*SS$@mIp&_{&bW!s(^>X1Ig*xCJZ>m*squy zB%%lRPn95~=4(?BaphN%59BrBJ~%P&hiID^>zsx^VHfKd6?@$_GzPzv1JIoCGSZY3 zgv3`Q0VU^0)I2xEUX`+x1xE@?C8AswAms^Fnxd&xHxw0f*%apM4|YV+w?ei+0uEW> zoI;n}T!aflxuVXb!lr_;lLq)LQKTqYo)@JlvaGCu)GA~AbOy%*gYvivi{L*Mswe^c z=10VFl7mjFW?NSP$_<_svqMT<775SG5DhUESrt25FQAmdlWwSsVGFO8_U9c{Dv4Eh zD6HJ22J141hNMd%7?j!eout;xqU6>9_6k$Q%gU9f71n|efqL}~3ZVkc5U-mGTfI!I zn<1yi`7GucPCL&b=cD@N(rf44O7$k`69tR=x-hW?vT|2S2ch}A(Ay9H$35$ZsAtZT#mJ{*zGhw@<^ ztbGxy_+bcr#r+D%_-sQ`Zn{%##l)(q>o48t0~)m^_yjZF2`B*HIIYbwnx=VTp&} zhBUMGX6kTGLy~-BQx@iL>!f}iHOEe#v1}>K^$iWc?Wt&H-g%nZcgXI(+P&SFqoCiS zv&Zs{A#`a<^a#bvAbBg#)0aYKFH5g+Pnf=McDCQdZi6K2{?I5?6Z(d5%4^C@!HH&+ zZ>1-f1S2F4r_!ODB{mA?OYm&wOL2ZB40A*5xjc{}W_nXyOu)<%Z)a9SY#aJS6ez1E zDZ9k_n(zvX(J=QNVi%gKV0QMbtw((gb0XDhJ;+STq;ZTndp<3hZ>SIR%v5WD)6ha^ zho+@%84N);K{{L#jAc&Dqiic7(fsZpECvQ`#ZWi!7+L8IN+^pKTJ=CeQlz$g_}XXX z8EV!-d&RBQfRNVe5XHvH*$|{#YeqtF+haR9uCz)@s$m`6(}>C2ru2k%n`~jI^)_o+ zwhE+m1y~V^3??Ok8cfb+kycg^d{_DaxmHH6av0Z2XWn-36d6d%V*Kxv?0A%&BSQue zePrj#?{GdG>bG9BV-T=%`J!hCFSfC=K`@MifuZ&60#qEXUudNvi8}NP+XmQ`4d6%m zhJ$n)2<;eYxH}LIb-+?e#;joCBVLi6 z>b@MQQbB60`uECI8(C!+M4&L*vO^-V*-asT-^e!LO?JoJl*6|SR4fp+u*aN~cWeU3 zHX!}N*){5yWmU3SBF7D@;eI91H)OvbaJ(lH20CrO4>;bpcKNz{+Wq$XP4a!JFHFJA zdg#g9i8jkbYamuJUuRV~e}s^$-ShS0ctD1c8X3dF<_f>p#5E>05-xR5=yFDoCx=Un zlIz^dSR2@Z{R=c~q$_z^zO9PO{%a6{&IC~yb{^5PDraKM$JV})EloHSBbqhFE_0?3 z(inRcSm}K2Ib_P^&YaL99PW#lo(#qiJjBtCoczxf&Q#=0D-$7?*h$2qnl`i>>|r%3 zht+v(wiw$h+e1T}X?)uW#$y26OR7#bJP5kY4sJldY)V0@x6T4{1O>Cq4cqLltbw1Y3^=B6DSVn>PCMG* zT&q~ivB~f_P!MF7TF)Eg9vrqueS?Fhz-yQNQnVSOUqa6Bx~{O(>|N8-)|{H|X5AWx zcKXKq1H+ca=@^zw3z^N(GnFr?iet>4uMJk`w+DtcN9@7I_N~e>K)^($k`Z`{2vnQh zjrK&FkQ42yw7Lomg1wMYb;M+e)Pe_KlU*!^SVNR!_W8D;-d+L`M_vA<$G$BrkqnJ| zM%Tg;8DX>#LCNrUK*p9YFc6b_vvC90?-QYq@s7$l@1pE-M7^xSPT`NhxCYI|JHRqa zDKxJ2#|jJ|E^P9DSgeS#BfFcci;qWfK?v#l=VknwpYf|Ii+XZ$M5 z_*E``{ghge>)VkdM;^ti34A_^4+VOvzz6Y>*&=*y#AgjYJ^1wF^B_Ky#u@p063?pe zvHuQmHuV_Sw@iJQ>%pl{aDA&W>5ml|Q*?QTF3-|snl8`N;u!jE14=q-mZsrOUHMsj7E|r;_XoHylwW@*S2;S+yW$T4a>XBl;)*{+ zp*H_0z%%|3D4LbDtlI(AATcHxLy&tms|7>XJ|!|=5UdJ%fmlbrh>SdCz9R4!wFpX? z9vD;%S2GEfJgX(0AEX!OvL}I$c6>8%?_j4+or}k}qbna0_d@n{aW7);iF+|yho;?* z@YH`U?j`Jh#J!ZQ0ithbW$c6EK9ilpW@QA8r83Y|xeiM7l(QlPkEXw39F1=(L~i^Q zvXaJhe3Y^BOk@LFACdP$g1eUJXStHawxLVfJrA&l-uYzo7rr|=#&Lkvck*) zMXGYQ?B}LM>2v|GkRX(^a{`FaDVs*6uNQDM!( z+JglK^T&#fVxkJ=^sUk#n_hBvzF_<3A8Pc=;Hc4iGKQqN}iwCM@gQbB%h^c-$1hx0JSOc zkM!&(T;G`Z6#R<>V_*4|XcSWzD}F!?TdxD~5R~iJa_# zIFA))&fFY1&yxe&9)<$U91sC`Q4zaz8s+RjJEL)Vk#RndVO#=a&=~ABZU!=pwKP)t z=tYoT?4TF>Y2bW{o_&sdKL7po_Zav+2L3<6z=21KiBy%)!dXgd zW(lpCB}A_-&t5aDX~|?*GOw~%%vH2v?l4zOuKmW?!1XJQjr=y_ey$f716=#Qv4v|t zFb26^Zw&MKMucBxjPuKlhq>NvXncXOhgTVU`4z@Kex>m^*Zz<31g|lkup3tZ#Rx{{SM;|uCFkTbG^ebxW3jn!L|P}-s0MS(~e@D@ebEJjrX{Ii*b_cx5gRQ zZ;Kamy)j<8|>pygt5{FOTozE8>sy8{$v!mGLL} zjq#`WP4P*7b9{=gia)~};?MHN_%vS~f1WqR5Ao*sVeX5^cuV|6z9#+(Z;c<}ZSkZ0 z_V{c3j`%U&9)E*(#Ej$w|Q6m9o`*(kNe{%c~63Io+#$)6J>lu zqMYBAsN{Di=J0zG^Z3TZe131Dir<&0=Dmr9{QksZ-j`U)A4uSgIkA!tBv$c2qJeJ$ zL$W#1%C{uic`(t*w!~hQ^w(#M^Ab&71%)^NYA4!b!NaA5W zn$Y-IVh`Vu*vrSk#OzEw&L2uV!QY>Fl0TeyitkEH@<$R={L#cST+EeWL*?-VhRR10Z*%RB67O*Bqlx#p_C(?&*FKhHT>E&km}`HWEaTdf$#SlJ zB3a3`PbTMZ?NiBlTze`xpKAw_Rb2ZMa8lZ*lMA^vnOw}Z&m@;}?X$^xu6-`Kl511R zRa`rmY~b3{$!4y7KH18(XOit)`$DplYk!*bbM4QP8@TptawFINJb6FY{vtWRwJ#>O zaBVs{$h9vehq?A#GQzblC&#(=eDYzgeI==J?W@T>T>DybFV_ww_i^p($;Y|&m&qr% z_KoC|Tsxe6ifezBoaEX!lT%##R`MCH#gfl*?c2#|uDy_ao@?Jp9^%@I$-`XxZZgKT zmy$1X?d9YvTze&XglpeR9_89!Ctu^*-z1N5?MU(suKjKDIM@CzX>jfD!3SzblW%eD zAChl#?bYNvTzf6~9@qYnP0lTdm7J7&y8lmT>X|m<+JbW^L<70U420Q%Mr>}l_D@DJ z*M4Y}aqUM&IoDn{D!KNCF^6maY|P`@kB#|U`xm2%YsZahuKg?Mw)StJ+uBdC*U=24 zo@;UJb+iNsx0VFq)=q$OYd^(aNBehBZtYD_ZtX3@&$a&m<<|Zalw12RkZ$d5BHceD z(*1LgZtWK!-P${#-P$ifytQ{hy|wp1y|rI~d~3f3{nk!`fNQ5f!SzB=aJ`5~crlUi z8AQTMh=i9C2`?iOK9flJIYh$GB@$jvBzzW;@YzJdD~NWO0;g=E#pHC$GGB$Y|iN2j6 zPhr(~h)sTw#bz>w;(BC3C`NmkFn9asB_Y_xAB|7T3M_tR&0AmMj??FvfrZiSXhW%d(AmNm#ww3YIOT z_1GqWS)`SJQuMkL@w54fYXp^S7 z_wGvMI&Epo1-GTw_j_jMdG^_7pXZrJcG5rYuky#z?(@u?IdkUBnKNh3ob%{xPbl9o z+t0K27w*OOcJ=|Deb+4ZhL6oo@$9?V%mJ2%J!Oy+ZJzi^YD4Y?(MbPj7up-lRiABo zBA;HCtL=wHw7>53MY$Jpe<4bsIqPRaPyXcPpC0zqMQpZ#XEU^+SvGqU&t_+_zsjkK!>PxbS3gZZxqm!$d$ah|_-sw{$ZcKCtJBw!Ef$~Fq_4oII(%A} zUX4%n__Q8qbu~9#)79LZu4&7ku9^!Rd;YqE_dO>V0A@TIj$_Zi0=SPg*T^P*kvsEZ zO*7evbKerZ$k7;F-P87yQ#GGFwd)CXDg@iFWRK(d)RO6S&8KTRcc=e*uBQ3e^J^55 zm$TXRMB3Xz=*P@%mTwS^-Ie>7+{6+$T=Nbw9r6<1s*tH#P`P{43uhYL9U(MaQ zX5_ZpA4=Ef2G^VlC90-ZqlMr*(tnK>?v`U%%Vt*sJe&C}%fomHK0fyRnL$3i_sv}Q zo02PXFAqK#6irNbpD8_kwe9=x~ zc)Dry5xg~vKPPO^r~lr3`=ofQ+BV*z_U8PS1F!_(`(9LVi`v+>yr)j-i0rfzzWo$7uq_v0tNpQ=9gzs`L9qbM!M zp!>D{T;G}8kBriL>!zztSGE1b?A?1&6m>;u)wx%5&tWWbMKR$kOz7;X>lqmh8fS09 zPklUleD>Z@zNwI;uaAQ7XS1ST!9X4sWMdTf<^D4FFVo#`w!NNPI$fRHQ8V3H zliN`@-C37gdhATyyVo`z|1|K~nQGmc3Lkr=s`2;}`1*?<;@7cPni`LvGCq71kBUhz8PgTnbS9wJ-kXj zTwC_AUOrqR{ygz|y7Abfbu4v3uCq?Ik$V9H0PgyuI!Igyk~nb-3^085cX&2D3nSK1 zHuE&g!`=hZK1dSm-a2BFccWXwXNgVPUO)EeI+kua)wxdeKusNROL}Xrdrg0?do}(y z;eUPqsqQy(e_`;>2LVFVpqZ8HTqiK9&%J7L%zID(HAEScV_pEh_x=&i1neg+f?Za? z?l;>^TyX=>K2U&(ZsjZo&lD2$=@EL75$tk3%fqaNK7pjirhxy_uLXiEbGjk7w|*cO zl*lv*y3E|h@~cGIL3AG7z_L-(_WkK5(eI^StEREI5YJ`zjh?K=I#vg&Le5~B$gr6s zEDyBNB&J>N-lp8+byx`?Qm3oGxdcrgd%Ql7z9F|~^_K#_iH7}{nM0>|>W9cQ|q_tg)<-LCdkz9>44f@H8-wbQS8ivEZYeAH^6mEw-bGDu4(Jq13 zN|wi}2eiJnDNw^i{h2!g`HRIfArklHAV=@5Ykam=u8$+PRi#IBoyL-zs(-QjP3plf z{u%8(SU1|SzJz^u({#Ls3rusbia*n}s5;$)>C==8s(FFWFN$L{d;n$p!D5)azxX`h z5wkU+Ka$xvt(E=j@$%T?Z?g1Iu2U`YXP!9w*xns4cAudxLuQ_;$>auCpQ=9!i6jYy zg=QnT2~w)b@+`f+@tICAr+C;|pIdYGBQHZaK?A32&vu{Llm4>7j;F5>@){WaK1eKw zM|E6wulr3km+<t^3ZqWVq<=vf$^_sxz#4i~0j9-jSeTG{>>E8FbPAjqD^ zO7z6jSMcoqr@^=HVKW$%OXQ#sSIR;84VGUcp4~+ZB!&d-KgiHig9@||4q+iYRiDW{ z3Qec}10%PsNcYLFxm_PSpt6-EF(;(pQ>a>u=j#pI!?3?|t+1e`<~)sDI~bgL}`Meb;6= zRzfe6Lq$tX(zHKdswXax3NVhgsng81iSY59RA;?;yo^4;16scY?iVM|t+W*f>E& zew=4d%$}fWFhkGga3D1MReF}EXV~66I{VM4QQQDfPvZdR_|xrJ_n*e%zwGI8`bE;b z_#5;V2TaF{U!hOfJ!OjD;@Rn^|BPqf_w7T+*D`i?3HrpKOD$oAD(pgL z8d&}^tod)H#{tXpWsFK*L61pKOai z`Tz^UqvUd2QCsFze`D-{2l0L#-q)4Amtiv0YqasNb%J2l~6kcYL7lyW9KZcYL7l1AF!jcB}Hsjx`c%Vemm1Gj zLD|5w-dc7M)^hpj67bt6YT1QntIk%lxmI?Aa8|Ni`&0}Xgbl`WBbWuU-wLp~P4Ivt z2Pa$3G3Po-7FfsT+HgfgR4EVDY^l;d5ykO1+zI3}78qf3TM!_EYHiu1*cTUM5tr4y z78+Ym_&IudTdSp0F{7PqFS>M-mJH;#uOP6%Wo&M%wnsALfu%>;JJFK9PquopPkIC? z$b*R>h^MDQ9|BsMR5;*^3Z*JQ3sl&sB{iZh;3}}Vst}m;!#lVI4Zm9huQ;*j2FW|+ zi3e0)iAAkdfY;@0?q+zII`d4YxDja)hXx!l_vKY=?smjP5jYr{6}muTzKP8Z8O|p)fF(mD}W{ZL~9ER zG_bj8cHEhI?tLbtN`hOsXxl?!lwSuj`^iJ4idIzGfXh4C+=uK50L_YS0)qU%BMEX* zM8FqQ@+unH!gILdRH|sj2?+oL^=vM8PV^~({FUL7Jpo=n#olucqh--H6gtLb0LREB zCe_RI6bo%OZw76$--clS4fg(Xz{kEAOBOP+9O+ovR@;JCtHA4d$hrI2fkoq$IzqAk z5_H8{P|5AOE(=_6P7U}E77=)m&ApRx&mL4rYX73Bi@4gGk1C;Kg2{)9JLH(z_{e2> zFIOfk14iR@7n{4+9xmMEK$qoj5rH=XJ!~#S(HyK{sZIFyCB;7xLu{^tz0EIJ5}9KU zhmRzAG#SOMS)yKy1#a{j=L+0t2!;}f-qjIK2o9~xO)KB4MHtXW+0Y_TG@%OsgrnPT zq`ah<5x3go-nkPQE}Ft2n_8G;Fpe%}g#3oV3*61-Hn6uicVFELG2^8Ji=|TWcH|QZ zmlO?;MIW(NsADHzvPYMcMgUTu)hBed6bN4Scf~+tGIlsZmu@KxD&4<4M4|f<=97Aa zOGauXX&zu*%6H5=0nT* zAS@_~p?B+w4-Xqbm9d2*a(KmFiZNX>W8Av5!JFFJs%{~*;I2`4JSwj>G^e~8YC&YU zkQVg^q0otyl!AL3K|Z{q#UmW(cArp1kF>tvMM@s@!&FEbW+AFFiBd z*(6!zK-0Z!T>UomE%It^M_e5mL)@p*WtWjktl*L~a?Kx^h@)OK8lP;A;qIR<<3MM4 zY$!d3*3G5gAU_DDi|=a0FA|bd&Olww==E#>>fkeZ$pFwLHvK;FSG)u(_fo|Cz=BD+Y;U!727JZ{N6*r#hpr2Ohlc8|wX z;&P)G*G92H)}W2V7U@)EAhb)S;iS=uFT$r zma9eHZ{~BRODZ7mIDsF+<6_-Uct)<$$!rM?fruqr>q3U&?lj_aA;dx)k*=bqYOJKJ z8E1lGN+z&oTv$D&AyF6NttQ3+lU6??7>7%UMYjN;j-ju#sbS2PcSeVYBNN8>^o)fM z5iPuiUYoJV12%#v(EJobH>uB@X^;m2QH^L|yFvqmpc^%i_rtjOY>tNr)`a|ksdH)& zlj+-2M0_lYyvqq0;nWCi z49eW1#!j5b2Sfd_=!EAsm0DycudLpoZ*`PX1LA6N=l@Xk-?dysM4mcFzQiILu+J9of*z( zki?ucIC)JHeI~4~7@10tRYWZGE|SfMtR^U>gc6hX(^v=TZmJj$n|le}h(uwQ`?i*s z36(vQDpsmJcGmgLBy+E3uG!R>F!8vMZu91HIc;p5(UX7j)f1{9=wk1Q|IE|K1*znP z&9ItIwwxb5TsobIx^){<{NiexbB6oF%HUUdl3@KPj#rw51MR7A{D$&)hZt;-$ znldzNGpPW9sW^i3`dIw*3z7uoXARLA>bhbg3a>w-W>lN8(F)E7dJvsaAA(q*XlPYl zaxCU!7$^jwA6VL~%M?U7MY{@e0D%<-X?-CdwX(^!Tc_Kab{M2Bx}-J0#4^ zqlUz0BOtgbj^sr5V8jE`IaQ-p)O{5t94??KGnL(0NPou|0N(&fBjEKWzvT zQ!i%Ml#c!`X@o_V^#oF6u#E5aD2>p0HRqBtNM{Wfj7N*1d4`MliU@RAzJl1&c?U%; zVtg789oo@EC7Xs2@iG;SQ^OrlW)6$inbs6cEJV$!>c6^{*~!}K2mN*$A@~zvGfkA2 z3_)UeN4ph^_-*Uop_%?zJRR>tG%r0loj0M{)H;G)_9FF#8B$Kh(vqhWZKiWt`4Ma@ zg)24#5w~cQ26x4jbTRgy$ZcV>YZ&M3u$7Y)Q+tYQfV6H>^iXvP{Y-NEnF#*;RA?wlsl}jV#GO6wRzI+qR`-fM$gh_SdaLx# z7>;-Rn8$G1%TB(%TIYk(73-ZCN(lZVH`H)Kg`uv{C8Vj1Ry((x0Q!vwU9d5ZEp^hJ z{4!2z$A=*L(Rfp>iEW0as5~^w9crzv2n1C6ER+&gz@JucF1<5yq?BABkxnMgk3-pr zw*QpNkXABsXwgwLZtBYBF)Mr`BjIr$%fBVhp=`!ckMPnxpnh)Bc1MVmZgPsIRzwOS zjfq#(*cCytJXu3;`XY8gBDScH-6}iDtM%3>1@MTkO`(s&0|C#XG_;m0zlO4)-%1l8 zo2uXf>xPP)GW2jc=Ds~)oIi@GS=p)gM8us#& z^kFGD|1C{OORNE;iJ27XR82QSNiuh+|#GYN<#sY%@E!zhr#Mi;lj=t`$ z-o1S>Z5Ce&1)Gd$cj-RXzCj56^I!XP{$^e95VrUYd`vqK@N<`*eESaw)^XksEk=Fc zVmZocYeC*-Wy-c}PB5dkR_l!`zY}&iEE>@FC>2iN9%&?D`j1y`WX;#YVjS}W z6X~%`O%{n7%xyOa*qqv2ep%AG1RgxtW+BMdEiI5)V?G*8q=zUkTB%X5b<5M;Sm(6c z6$?ejBZ(5@O!^QwA{m^L(726PruXoWOcf`zQf-cDkP8j6PCQH5d>TxVT#qrW7D<`3-OFgsu`|ywgI|3^X3^Q!hCnA2%tUU05 z*04w4l9+zu$XM#9HLV=Lc=d&+29V?{g#!<$rEVvk?rh51maUXX&&pE$NlYAFdwNJQO@8o6wZ`qszilFwXRCmf+x<_r%_CzviPvH&Eo5P)Hs73K3L{g>N=?(^V$5PReC~mx#?kOvqfvKQ& z;@%+=9ZsZ7&p7jyxtGUuT3fBV-fCov-`BUhv*huQHp67FM*ROg}qd@O13Z8$71YBKb@k1mFK8ZGB|e1O9Z*xEyBe*Bf^C_ zwq`=@V}39r{e-r1(lf$=tddr=Kb$(3Yi$quLBia?(J4HRKIL_N7lX44DN=1(jkP~4 zuCYtyo^o>2U^p%EN_WBJxT9KkVB4RFke@ZV!AWoxj9H>!RO`4h5D5>9t*7J@w@s%f zt3AOXu{$tEe<-!`fCJ(hof=rw9$G5!@`?L?Hi3m zaM@JIBSOsW5K+e#gQ-~D4r@}o{y`!Bm08L^sT++ne8-Y0G7Wblj=_*T7qZ*H&d-g) zg?dr#{0PMkVX-lnA03O}7((YPuy-PfJcvU=oQO5mdxl_wRB4T7Qb-~BYxEGZ<`R;6 zty0#ZI|CiD^u(~zDs}86Ye}n_5yE6rYoC<0gh#E_(QQ)e^Cc_F^;n!tgb$fo*_3Wa z;(YLo{etf7#^LDX77Y^{RcQ8;CC9LL!3?j9xPmwqepC&e*d35wOzzM=#dU zu9I{)6HSGn8PM5dyYwuN#@r&vXz!@X!vm@5nO7Kaig8LaH^@W}?Vpe!qI&fyIt9lJ zilMtB5k_qOA(*yFv39)plW@5mqf@VYCnnXwz8_B0N)R$%kp+~3A5r(*9D8-nQU)9! zmQ?KtmBrCix*H0_TsDPX{3QcH)RtL6$|VR4s%*wi(tBo6 zaf5;V!qn-qa^=~H+k%fkdWFY?QGq(-cT-`ymx*;V#;(*%Lj>(yMnOBXH_E7;>#KCj zI)pMgz?8)d&@Jm?>Lt?gl+x9emZgsnh{k%Ni6oYGI)frY#QdyYv=$z*M9cM*R66Ac zr?5s$KJ3;65|er-u!uvY0nK2v4^x=XPz(cKlD=NJV-3|UTA-k`pcw@8f>*&Wy-92U z(R-YaC)yu?hpWjiTPUp#J2@24i4d`)@D&F?In9x$b&!77X6 zGs6(#sOZtTXBqldOvYfaQD~`Mduw@N_0k#gTiqdJgC^pgMIVuVeNkAZZuBgURn}S< z($o!W;Dmi@7-vGm<&{#cnpSYrC>4=j6f{ahg%G4 zC?v+tGZ>TSvg*U`V5F+602y8kk`RUk*DZ-g!tt(`7jMeOLcRSou4%ldd}05ZXk#ITK9fqjzWGbHPVBJFr=l^0r*BoDII~wSc+pc*?Nn6L91!Y-hVX~ zK`GWlu{6AgtdYT>pOv+5l&63WNxIuXttY17mQhwry%eXL;_iHmeaQn-N|je0odRaX z2~>FtVtWx5nGy|5N>_)sv?R7|Q|>qF?y)cwDeSTDtE^sO9oW*s>_jzs4>41^bv()J zBX23zJrTw{Q=YTzj5!fCv9?KL1+dL3if|2lRA<#vb1MJsMPyoqr2I6uv@FN(D>|Si8 z(!oVVdcW2SlI0kv)2p1{6PR^gK+ImIWG`!xrNN_@vdxe(M|)B4#tJazL0Awwggp;H z50p5t(}?A<{6!T;^?RGj8a*H)|Z;!YN11f7mBni@Uec;n9XTIrUQLM1cg3 z(p?)4ibydfGC%ZC5_cvK?%3Pgg(JpRkBTbDB<&R7)?pqmR&sV}#0SG8xH?FuIYW`S zQ6+Bo%0Rol9_`8zDhVE>7_|2eUc|_7BamVZLLS`Qfk<4D@Ps)lGICaIsy5jswFeu6(jJHt(d_B^M(+dKY*kVk-O4-tCuu!i#6I!Mg zS>Bj7>@u@?xrT|XT4O?%(CK>_ZscfxF37Q4OK_5=W>iUwxY@H%UnCV4B`tN^7hRW~ zyqv8y+BR6x^pcolZ03&xd00?sdR4C|&*>}-t4Izlei~CT6abSecC(hB*F{JzHF;l4_K#j(R9I}*`h;c^VX1YsZ+ zqc-WI>5Z(4a~N{W;`OPRu*$YSjIHd2sTd*ybc|8-f?=sAtxm;ujK-i?4EK)g#d+=! zVp|OZ5LxQW09}-d$z*rbZL^zrT7qakMLQ(cqeG>B0#_r%k+4^z-#s|T8>S0-6Df%p zPRSAGK9Ud!Um^)?cCL3sJSNR<(I2`4Quv*>pqQYj%c%+qoYB?*gP(Dz&9KYDi zRV?6JUld7QfEfFzY=tMKQ7KlZYv?Zf9lB!{`jaD$EACyQ@r_dv*yoV4FJ{y-h zLLgTZ8WaHShz>_3wtMJ`FxEHCpe!k@3xTjR>sh{54GKvIf~EGu;1lGSH+inMaFM9c zAIKxeyvaf46p^*Wpwz2D0XbB^v5k4yEQy&TENhsuf(H0blXCzeD}>V|CW=U~BfnuY zBn{CiOq4!kL>i+jp4ftlvY2&!SOlZ;8)lujIBF>X$_*lwB?w{%2kbgAQ;qNqSZHEA z8hQ94sxTfOqs)gToB>%13kijWl^TPxh50R?LF2j{b&pI8iOVYw#HJ!R(@Kq+Z#5&b zl%~m2F-__$)5NUqJS=rIO;8IH`vNsAG>*g?s)8}E#Grs)%Hl*NAe~7h;sTLKJ%Jft zIuNG!8jLR>F?0c&xi64krrM?{TFNRcnr>3ufMISQB0ZC$%wRo0knJ}VQp*5?A zG+HC$>Yjy|`o`l3iR98CNIMU1>O12&8EYmPN)Te@2Ei$k!9{ z^=u~1@{rjA7h19gQzWY_vI7D~{9wFjSUbaFUy=kBX_1tRFu7_eQ7bu$2rNaY28)Km z6(O`gb2ZDaP$X=!VPRi~RunFgRjTrhjO|zG8GooFqxdfmsTmspN!J-K&-*Xr0R2!S zwLKyits)P-Xy~!{7bVpT61^*XY|Fawk`~MQ9*c-qmC!F#vI`pZa;2zbVKVjnB0OK- zQ{pA{nAFMo9>MW)!v5{VzF_dh7c{W)X4&^T|JjAoN z7mkJURfSK5@->C8hw`W3V#>MFE7?&wiQV{7q_GFVvv6Aq+b)LTST>{;)ejB zI2+2ZFMfx<{+O!$0u9YW!)%&g!LxBT^T9wK@|zfNsrx{cCWYhlLtR$&FhGDtpOJsQ zE&u$j{Id$ofOogbKN0x{ujmI1eU|>>A^l|N*V(g8b`GbOWGV!vF9Y@Yb^*#V3#b&vzpT{#mr<#D>$Id|m|kVY=oz|TpKtUSv*#@R zFGp^39i?@P{iXEZ+HN}Wn-ytnQ5MqK=D?_v; zQJRI^z2Y&kzTBCtBx%{iX5P#47dlHz>gFi@!Vkmc)IW-5|2LvPZ$1;czOx(^o?MI;%54F3;lv*Tvgmb zzjosno4Fy7U+v7f#I^6kfBKoh|MmF)tNBsWeGQ`~}_n*Y%T1}JdB3kcO(+pcpGi>$w&amss*Sl*A4~6n83hyV# zAERG?5XxU$c!}Em87+kW5XxUyti#vh#bV()!t#*vV&QV7l`$uo^PHqbr)n2SF4lJZ zWeg)!Cd#qGK$JI>4H=1c7h-~wCv zWEGF=<*W1lUBfO^%C?1B4C#0=4d5j-b{EswT})%Q`aGngeeBrG*8=%V)NnZ9VGyFu zc_6UP;^+TA!jXFG#A&t6#%U-AHmcAVmNKixP{*ooK|zSxVflw-7`}Z`{`sN&^QQds z7KkFe+avx+!TT=3&5Ap)iMyx(xmiOd_0i9)Oj)z!Or0S z3N&#s{sWC)!H-<)MTb`ONUM_CTnmB=z0$Vd1Mg$ACGD5!yLCg?Sb8i-qF`WqiyEeD z?|$`|j4_tS2k3DXyAFNp!mjK-{Cx+FKP2B-A?Q|?2(FaA^I2eO<%Y_8J5@UvODBfJ zHjF*>Rl$6hl6+cA3(Tb?q%I{}!lfjsE!y|wT^=ok3-QsU!Fix-CSZ!O*s%3oi+16URNXd87u02I^o^_WkM6!;Gc{J%kgr+%Y` z4o)wlg=8JAA(zqeaTzTiH##V~?;@jI0~zHWH;r=p%Z+l&3;TKY?N~l`kz86)80MLu z7e;yJe-_4gw!aYP*~vnRXCE$1@$6LLF`gYQWO(*S;W*DeQaHi0j}}hy>|=!w^6aw0 zM|t+$g(rEoqwp!7Z7>b5=o(&e8c($v!n!lsChG)Br z>-e3;^*q~CZ06Y=#p`%>XK@41_7*qs>^q8Ec=pcXcAni`yq#y?Rt)m&V6ls5L&cpu zySKQDXL+%oXZICDJbO=ZKhG44_wbvG_w((=VSY<-l;2t$$c77P5I;w${# z;xGAq#aH>k;%of=;u#(;zR4e$XMAYBh7Zrz@yL8VADM6BhvrxF(fKtzI=_xTIKQ4B zo^R%3^Vjk5`3-zxeiM()Z{hLz?fjwn+j(L>$dmJ3JTeslg!p83oIcTFbd1j%XXP#XM@yyp2_Vdiw z7w+Mi-(R?&XZ~Pem}kDRFv>IkZDEXOzPS+RnLk`e@l1YUif8`6g~xd2j}|gK^W4I5 zo_T)Z1kZeH;Uv$zu<${id2!*RJoD{^Cwb;O3!mbdKVF#OneQ&l^33-ZKF2eEvhW3- z`O}3tp82zdukg%E3t!`zKVSF;&wPI&&oh6q@I24_VBy<5^TUPj@yw4FUgDV_FZ_UK zUS4>aXZ~{Gr#$ml3qR+XpDYx3=BEp<@XTK?{E}yWw(u&?{LR8^JoDcd&hX6tU^5p7 z@|V3S72^LlB<(r(;`+cc9L04~5}!zdJ=Gp4FrFzCYItV8P{%V1g?gTOrO?DPzbLHc zng3N-!!xG~>v-muh4no1w}ob&`8$a7%-=(tXI{k_OXeSN#*+C*=#QCyf;i8-263MG z-w@}S*Kx*@c?04+a|UNDnSaI^OXgo7&NKfCah`b-@;vizB+vg3$@8-$&l$<{0Lk-e zlIJxf&zF!qUrO@4j^z0TB+u(fo;Q*_Zz6fVlH~b?B+ploJYP-n{34R)7n3}{gyi`e zlINF_JYP%l{4$c~>qwqoPV)Q;lIK^FJYP@p{3?>?SCc%yhU9rO$@3PH=hu=ve+$X; z>qwqoPxAZ*Hgh}6<1mu++ocxT8j5fubKD@)07_lFQRw39XwAF~G~dQ%###Py#*TkK zRd?+9S6`u$(>rT&4Fqv|XIMmyGEv0H|Jh%e72@}OVm^C(cmljWTaZ~S=Vnls|dbZ0{@Su^s+i|bkHve$do1=1JgUJ#fb&!?8U zru$x<-uq_m<+l9Z(Gl#LhR<#qo?f5pe)G1@m5s;O;7KmI@{J!|lYTSz_?vJ1_?kyo z;@x!T%3Np7boZHD_nFs&>jI5WkR2Fb6k?6X|AxN5xcr6BRi&2Wxq;;yFR%<_sb#;J z0^m1Zx(aXvgr>$Peit>~coC0TYJ~x*U%*@bf8Kbpj-J;U&);}KlzZ!`y{{UNa(iDr z)&1Jnr|?2l%5}fiZ@iOW&^GukwG&SXoh|r6oo+`B+3EipX!}0BTyyp#C&bIU4;&bt zZW`T*5~@-^oio{ zb%2qp%vH1ZV#Eqh(5K(#*@@ZT<(p^!kY{7F-{;v4WU{(y7W@5Evwue~U*m1XMm#FE z;@8t1Jp2CQUG(Vy&wk+PF#S4&XKdzAs`6J0UfX-@`7?ssay8Ajb=EZ=pTJbgJz9sc zyuBIE-gxPn&bsNY<{EsS?yP&`MY8s!8nSoRVbjjCe@4H-fr{yJFB1Mq7 zFI6AZ^OX+<8lUZ}Gpn3d58n7o^HJM(%#YMc`V%EwXw1yE8jr`&pWmb*$ZQAN)A;cE zd;S|p(v)6{=D=sEsS?6XX9sp+o{gAYZDOedjnBMi4&{#L)4K^lJi7Y#KSe~9z5Mzd zKL7sL!~^1`SMcd{^XpyBwN0tZP0(Mb0>4j1F^5$tN!mXJO0vDzpRY?dSevey3!GkR zHlG?(2w!JH1!D_@FP2E;%fO$$3`i`gORfGoJ-4;p_8oJ)MlS%x9>hP@Avt|aJZe~~ zS-m;-%`X5FORtu4Xq}+6G1p77>A>Ch3hBhyTul|scxTUod@RDISe%Wq7)#)D0#EEe zLHU3zgioDhvoLFo&t`e{t+Vgr*^RTG=GmKOzr?efX8(X^TWPXvo-NWZEIe&bFGpe8 zA#8d2HlE#DjKc8ybew0mkxA_8;v@9+WAqiK-K&c;^c9QN*5bd=*R%LF4|C7e^Xu^I zv#mUP^Jm*J`98aYZ~5#yd3HOS`3}opBd1rnEN_%Eq4C*A8%c)W-rV@?hUPbZIC9P5 zMkUT0X)?Vyviw`0Tc2Jva?RVmf!;MfyCjc4Be%Vns?WWEfv7wCq3z()kvG0o#ZsG( zee-)n=679hvT^E)+C}R%A2|yG-PhHIlU9g51?!>Rz zBoX;2Z=KEY>@BmO#fLBA1DjdF^3CAOV2x-JE6ox%Tf?`qnHbBr5G8W|n0pxr;D}41 zg?Y21>_P6;++dwTweguj^i;rVe0H#*^s&+C%iv1&3F}(!N4e)jS+T~ovBEN(mCrWv z><4KHZk%1uvmct>fU%mr8D{{qINQ2%wvT$VkA&wCjH+y=m*uY|Jguri?yqvMnscY| znI%Esexheh?gdB>yE>42ff^Ue!BZEp*=0}yX2Bzulga+t*$raV=L|<_XoJ@M+^dbx z)HL7PvnF+W<1_1;ufA^$eP7nI=8eC&`eph^4Nca({#=#!x^67%U}G%nce4C-g72Kj zeN{QR&w$8laxY3EKht^fi5(Z4gp}F>js;<(Ts_-FlmxG@p1q1Fxe+HKvu}sr4VyW@ z^4E*@%E+ty?%Wrh0{?^?8X?$j1jpEeC zXLqa^neJSHNA<>|XZAH1AD`XPgttwnFK`@@OT~!H!u$4y*)1?E&%&&~VRitHR&1u8 z{3y*Cvd zGv2PRcqM9#q|=^NF@%kjCy@h1qg}TYVmw%Y-8{Ep2FpQ3@3Z^SQcXY&^Gi z5`7(xJzVxiQDtwh6;A<6y|F$Qu+($mA_lINW70&Z;tjcNt4EFMQ7SSnpM2~yI2bz^ z>fRr!>D<%Rz2sn5d#Jre{5se%I9QGUOPiZJ1`qTN)^>H%-#UB^9q8{C-|>OI?{4pt z-|>OI5A4}D*saPhliz!KcNtG=yLJrh+1p=pFtls18vkn#_U`Tt9jv3@)N<{??)~(g ze$#jMvt&sLqSW^ycuwYXWxz;2L zvOu;uz~)+=BSX1VO0vM^Y;HROS5ZS-wzxD@5>pb6JSPl<*xWYUToqL}yHpLyIW6n$ z3LlB3Q!MZho4bkKAUjtcWzWR3j3z8_mn4UCBV}b3Q9+IwD9T%Lt5M)ro}0?K8CjJD z+S%MDE$!*11LbxR*>M5`gx_0bkIECDmYNdRgUslFApI3=u1zysN@`NL4+?yNUVR;d zyvZJ!*K9-)h*lK&EQl(>S|Y?oiQO&EG_0tIdf_GKz#GzcG;j@@+XyTL|GKc1Lb^SX z2p?gAx9fSL6UpBouE$i3JJ{R?_7>TM3;T(rdaC&CG&vC9Kr*v^F48`)T{ygg;_`|p3pBI2YuGwxL=U!Z+p=jZT3Cy*xyrNDHWuj8 zLAn``U~|$y+Rg%R(?PlgkbbIz)VhfUx;2nmTLI~>wU9QmKo6U{)gGJMoCr=bL5Nm) zSvs(s&2eVSfoN8)7@DAY@ixA>B4h)qQAz;ZngQlN2j=(Kb69u)M%a>4eQ_1ryMeWu z!D_{@ZzjUn0vLLrV2PFy$gTtD_E795f72w0d->`rj|f$T1g0u3QJ{&2MYM?ii{XIZ1P%*D=YUVjnFNFbp20-PYFN@+-Z-+>_N}oq@5yV(6F)x znJp)mH?$b;8kd0pLU%J;?i&vf;IXp7K^iEj#mO)Ll*G||&jKxMZl66_k2i3GXvDr1 z!9zmETNb#U&4rmQjkPyqJ0YnBZ|pa)xdD4bQ%xI0UJ1BWk;RNogqpDg3xJKf?0JAa zfPx2QE)>dIYCs1wcd)s)Q~V;IQ;S3c-O}rbO%Zxn{QKG5L3a0|0np_-5hTK%?#z+{ zSd?KjY45e?`t#_ifg}e42fRQX90gP%n$hD^1oQesez~fafm&z7$Qiu zld5lV(G$3%nhIjvZqJV5O$;c6$i<)#y>!vTjr_Q#@Iy~8T0DU0gcZPLY%a?lSq!Pv zc9wdu%@7hQ$Voi@0+onv?CD4BADJ9j}!Tz0@)3K@iw-7F)#+>;UN_Z zMzYMPJ*leqK#CoX;20O>wPNv5dm`{mzdc8kcT;rfm0EI%OeCbw+w2Jvyg^!dAd9nw zMJ+g%kOhK~5h0qkUkQdtj6Nk71tLl#R;-9}S(<%`wws+H*mgN-YBKxsQqrXSuX2kD z9~Q_ShoZKFb@=WRgnIh}No=@Nky21agoTB=+CW-{Eg!ST009z(02&J)F#^_+qH_}X z+_s2e1{85Mmc|J6S1oRdvfQFInFUeA0yS)I&>kM$qbqdP-r!&NFsFgX77vxuv{TnS zpuUR~6*WyFb720xgvuyqcPlU#0uWZB6&eZwygvRIZPifk_36h+df zgpr^y+Ob4A92f=Mt#6gYR)IctA#{~F4?s8=#qMTy~HA>K+mckj>0n#IuXgo04J= zS;~tLfVFB7f>2cNg_W5|0F6Pv&0*4x?&Hj2u?KSE+dBVdKKgKPXF@H0KXGSDiV^gp75bv zkK5BBxsQhd-KyRCA5GGO4=&2GgoA?c#9(|ZnnENb8NbG(HvoS>o4cRA(?45w#TL~7 z?O$5Cx(pBx^f85GtGi`s&~?1AhOuxFotD0S1A`q&*xu zdWU}_a56X$iBXt4B^0PHpznS%A^6^3iDg&FaI4h_ytxPyLNaK};TVA3eG!|{WUv$^ zUrS94RwD*~QJRGWZw$N;PassBl(x^63AjCd zyqApky)Ek{wQvy{X{QnLm1e`YFdGi~H*6ZG?37&5ATm+PMb{wd2UqRI*c04ZpoRV_ zHn+>3c-V`C-uusw1F>HJh7QZ3Ra&v4v^JrW9{{XH9O?*I@^-PQ0Mls90?Zx$je-IU z9cfb-N5!z0P^Wxv(FFvN2F}dGMys-tFO!SfkO^3{VRQmDu}FD`&AvcO4zKFlx<$%6 z0HFkJB^cnL+ZInBf>F^Y($m`)wKo^^L3*^wDD;CeoZ4uGv!!VB;%oHFfa8YO4*xa- zfmazEL#xZa7yP&gLxvTS2n}py?*2s%UT;zaurDLjdNy~T|1;!daIuD)pP|2AE(Tj$ zX|iWgYEfv2_-ImvBTiPjy0cbJDo-2M9B(wXpwxG@$dW(A7qqapq10nAm^{JW<$r~= zcWLwaXPKp**T6~a01NrYV=)#c=v<39X2jtyWs9`3@B?1DlFe2hq&=G93#H`OyS;rB zwr*}^RZtTiVE4KZz^M&#=zxM76AOT^{HtQe?Bye~b#ogFV2j{g20RFX>|F*N2nEx- z4EPU=7I`c=s^lVgee@BbH!C{g0EB#~9E(Q{?cZ9Vj-C9ZJ;^Wx3;cS6bOTdbR9 zI~UJnbGGEfPW9xtP*!Y9J!D9<8Eq@tGfPiZ9>A6tHqh*zNRP7v<&cF-Z*U*7bIY(? z78&DmcIGU4U=OhG2_DEV&(DRi%2J0KD&S%+S-N1S%=#BXXA z(^v9fCbx8vC^+-+8it>fkJ`<&n)((WEaPa}Jd`j~pRi`(bho+OOe!IlkTOyEq(tS` z>N}jeQmPOXOg|c)I3&G-Dsy2t(!v5j7(d~xFZ?7bQpMU4B;%dPDTAp4MOA4ktr{ey zO+iGPr^yk8@@6|9t7xYy0`uTFvVj_=LUYsXve&Vn!&gF#iS!}V-yTG77WfNn>;_}E znzD*mkc^1@vcAoA#;v{#^lhy=X*ah!RF&CVDzUbmSsOJ6k@Z64Ktmset;uF-G0GX! zAlMa?Ex7frB0-(a#3H{QR2+ZsV5`N|;+7Wnu!c2^<|~PFo(;2;cUIf{C>{E~7*jD! zJ|ETg#zICT{(8yC9d+T|^Xw{aS5=BT%M^!2TyT3O>z-f3&dDlW`iPWHIxJ~lWk_W) zv)n+vR|~3=ot%+9KLW5*J`t&8g48jmMSXAV}ia&G8sN37BTpG%G*;x^$=R7`eb*@t^fu# zDLgiWh>nJr5YdQ|HhJ@|MFqIomM<}lj2+|Z@{?U@u4vwJTCHo+uYJ;Xa#qn~*~%gf zwmz2@=z%sjr3&D^6Y>#J(Tyo~YiMi0Fcg7ycr$pxyHf*iF-8fk&|1X)mz@mZ(vLSE z2^H6J0yD|3)YLFF#y^l6laaBfDqWV1(2+%GwiS(ejb<0dH!n;r8Fsbp$CykM zO2UHJ89^E>SO$bVt-wDd%810)QTz7F73#zU+`u~1iKM)|fJI_l+x5w4G9sm(NTp5K z#JqaBx!mU)j{LL%h6`E}cb#nVuM$y}gxGL3?Fy&D#+m{PiS0bMvbsE9BplPxbo9tX z9G{}m_+)boc}s2gXDVuU8rp@)ItKYcDBEOks5~LLG=WoZLO`U?wyOS4RVa-S<&+W^ z7Gy1U@+u?)F6UO;1PV%;QhBeDuzSr^B0x7qlvgBuG{uS+RPB8b+gWmDt8~U{l9)OX zv!6dZ*9k?&VZ$046^i7f{lZRV9kH+KO?HpRQ_@ADViL*WAt4a83wZ}{!asc=v}@1E zNHQX(Wz6ns?ocZ|fy-r+k+H7m$Ouw8s?@7q6oz}a&5hItNF@#$FvLV3 z7d*5Lc)F2rGtg3 z6$+-oD)$K;`exEsskYLghZsTIwp3XPf`&cch($29)wo3=weij6aSyDM@ZiDC7Bzfp zi`^})vTxFcOlp(RjpX<`9&~t3L9zonoX@bw^rRWVyeC(;?gSLxb8Pez zces&W*RocL>arx`CHjFQb&Hl^qb4m%lH6U>kKzESrTRZ`_(qbN>gkxrYtA~8Qooq0 z;}u=gcGHkUKX6tt6cKrIi3uwVqs}!blb@Dnp&iGyqhE1ULYe@924##@Of4w=G2D{# zB4!jVvXfU`#eOoH|w{X7oYRqnvccOrVs7!#&8POAnm3IhlAtsa6%~)?kiiWHEO>G?1YWj zl95dX(dm?BY0@67z`Y(;*uL-t4st}~i%I*HU)PEx=A^;Ni*KWL{}Kmyw8$e?f>`KX zB%2ReO;AMAYURjobC_{xSFTc|?3#NCz1)tnZ) zHJcg}CLR~kZ7vUV5i&N;xX1WUzIs9x9D6d=W-LN2G6pP#ZSK=$rbLl6s?hF0Yz$6`K)p$GR8{J^sNv^eyYj^(kdphIkg(JIA~@j@|b+pUuWr5y%o zi*aYqWlW5vKD0x^%sgsHm@xrNaU@^b9lV^oD6I_^aPgaW_IBII)1+Dbt#Q30Z1Z zRoBWG-z~F~wblB4f@-~cDwPPEKJ#7-5_Ow-5x)ffcW9=}9n7;gE=w;P_R|}LvPh8k z@&hqM^6ZHvaDh;sPPE_;tSWV^r#iqBj0d=dE911FF!Ez19A*c2n%tS_3+mjYH2RC;| zA2Z*Ag<4<9uy$+p@A=1H7>!+++`lgf_{zaj8;jX)dYi^*E>&710Pm+4f$v@ENa9HM zScGC#8tiAaR+l0yOX>)dk~p0|4~zGQd?F|=)l+~A;$F`?_vfcVLs3dC2Bkg(#UPZ< zyVVaYrq#V-5Ay3JgWf8=GloEnav+d zpMOF9w0S~045yGd-Pm#{Q;^RZyCMjBMR@r!kQ2Lf#^O;QyH$3QSL>X@x?&385nr1^ zABP75o<(VJ_90;Utuz6$+B2alxWM|NmeB~7bg(xdmaLYZO4|7eX*U(`4;O*3_4duL z6II6(5w{Nb8$DX-^p<(OA%sQ`u>5goSW8=>g_JICdPoFkRSGN9p6W;aHDkTc>!#UN z>_khl+ywzW`9&XzZq&O(MGGZANgtMi^WW0w{5!j11Cens3k3-&^MUhXcpoOOfe6fc zi9NfzjRgkuEZYYq#Mi;lj=t`$-o1S>`?U!NArHYJ-P}BJ)31Gl5c=o8_UZi1x?<9& zh1dX!>c_NP3-@K|$+!Q2U>)a^(#5FnTPzMMZ7p%9%?W1I*6J;K+~5}~yVDUCODqnH z2K2R3xL;_RgsD@NTTb(}uo!Edwh*8aK7l1oHZir;+;)S2%_&b0o;C|Xwr**GsypVR z6NxmgLnkbadhws9yRpt`w<{Kkjzq_2{rioj3;s*L?XElY&h#?;1@J_ONN=fCh%0(gj zbl$^skSSquzc6F=+j$kt+&w`X4_b)DQHe1Ewn)Mi)i7SeRH=8u_i_mF9<`@oae@mz z=p0Q?9F`UtqMYl_3IJ`F`6~fZ1PigFmx`YKv{rkL4l8hn2vpYvnKO}uzpZlB)$N{9 zQFwuxmjpcJq0PvMV;o$u^zB7Tu0Mm-I)@0tNO;$ao8?5=Nv-<<>ZrK3Wc;VqN7nPv zmK?yvwMC7{+;x_Ew!6s{V+_|nYg#z~fJjLpqEF+%18S)pW}U5{@W4SglAoN9t-np*TXi+i z!WFs`M4(225$!Z7Ex|(`|609XAt*j*%?QNK3F+Bd&&)Aq^w#3@81G zWBd80WlnhB z9PUg*Es7r@k}B0sXB@ExKBg4~{HqSN#rh;vH+X=?CUERn`O4hOV>+#^)?IJilZe81 z5>fm5c6XLM-nZ`T>Vy}KEn-~7xSJIw*&z+FMNzxL>Ua!+wZvIGlA;vTE?%M`Qwn>j zYL#rS)I6n+#n_X6Iz!&&FW(K@telR2bgtl_>Xxf3S zl2#yVbn0BLwLRzu33CHS*QRmwDZ`ez*n(Y1k!sUwto>neja@4D{fXER4TjSqZ^V^h zC-11%9oQmfEcscJ8=M4J!I&iqMzxMB0|-(tww{tti~~(`aAnpPc=^~{DcQrJ`swWZ z)JkbtIG0AWzA*z4c%)%8OOfo%AOJ4osx@ewi+ta!VLxefqbrp}4&F_yTYVCKyCr;6GMpXNGMMFzyooL60oZn76|Pi=uVpCR5ceWFGftYl1<1r$C5IetZL}W2u<#~Q`$i)XTs9T* zh!Ar-MAWeb61B$duqL(ZAEd~y%u@bI%?uk%!{scQBGa%;Q!2*6ZUg(ZoFPZG^CJ{H zgvG{OesnB?V+ftIz}|^ugmPE-oFSMXRa&E&6jEK0Wc1L4;WwdU3|WWn43y&P>vd6C zOIpp05GMM2KC+hZsI@w}O=^91gwrun ztQ{}@BwTLC=+x_81X`ED4gGMM_Ke(!irEvDTWUAQUY)a)0Y`2>C81oljoK3`i=(M@ zHzaUa?H6qmVc*k?t=M>zfkPtf*nqPZ;i8UjYcnp62oDxpFhpF-Fifwa<%)=q+gq@0 zFXNy7=u~7(UQK}}_+xuEg$hZ~k3>M?ILRg_bz12_1kMd|m*pegS4`{6BH{UA(U?62 zPeq@^&ejhot?^{gmKgWklq8+SiUO8uMTLzuq+Ei)pvq?KB)w-A6*m|->Jz3;msL7G zZVNsF=@lLmMg{7S-|d0vUMAMfm_3+n#Q+;oJG0*~g4t@9bu%W4!55QS*K(1EU|38j zfmc);GSW^6vtLAvnD&fKR+l3yjMnl(0qXj{hosdMXn!v}%?KxbOe3sj{!Jek^OcEQH-yugoA|$ zo7~+|VJF6}ft}2XVekMZniT1JP)clixNRo05TW>E<3KA7ZbVqR!+N|=YvdUHL z3VW5#u~;%v*~XvA3fHo0>=kHWMER1(x!KH1vesMK4Ibd%AR{nD- zaW-J1(mWM!ZnND8QZ7!yA;}13Xx9C{tos(1(8MfeuGV@ZxHUWL?||}qeE15jtxgKsjZbl9k^#VO4;p2b+4M(NSD=A zL=vmZgjCv`c5o1S<@Nd_6N3JPt|Y7-GL`Uy(bba8Jkv)Q6M0?^4kLFjbVFsAFlUMS z9u#{EX)`6KGoV@AfDf8#+7?iH{*5RTnET6{IAQ?1w2OdNx0JsVG{K-i4|b^|#FUtm(n*J! zo@5@>qa8xo92Pq(FX&|AioxthQG%4E)MKsyeixGQ4&eeWF8NLgp_@P=z zLvBrxPeN%2Wd{8*?cMY~lJJ^s1CfW);0-}ENe3jdachp&F}Q80Rl$8wym-rmQBqr} z-541wwe5Dr(hrQmDM-Y27p~4RwSO_>V=!cqZxruez3^f$OrFcCr9$Tj#@MuqF*W?U zBq5A5t+=a5HbSvk!F1K#I1_Vp5V+_Q{m) z#@mYNZZt^i3aBECIf~No-F561xspe5HPXr;*V)QiBr70WbqV|Yw3L*^kS!rL1n`Qb z+^_&xxzL)YQIq5=V8vLv2^(D(>lQ@`5|gjeXV-l1$~m$_VbpIzg*+XcXdcxObQ>$1|zS0hJ~x zGzViiG84N*23X{MBg3GwI-GlnOh~L=p86Wkl})6RT`++1h`A47wb*!fi~&bNpnLdG zge`~Qq?~~Q;aWOfKrz%1K6UuGyf;a&M4A|OwPDnf*d>E^M-nlZ?_vp=LS_=4YYh@R zkT*VXXaY7uGD?{nGIN3}7h}5arS^tLw@PAP$=1m_E?Z}#_%>O59lKN%Z*>TEWrDt0 zmc5*v zapf@D3CcR_VZRlqxgE9|<=bWX3j=R82QZss*uVzoh;FIa zpaZ3*8Z}I!(B5!~3PJw{i}e*?%DKZ#1AeqeQs_uvhewqljyj}Z$05!pJBH<;J!uyd z^2Uh9q6M|f3nv;ejGRG#Jz`!X5v*_q+F8-IWh54rTxneeq!PtS1F=gE#Lns)9g3?P zE0mCY&jT=8$e>R+$B1|oRwWbW;Nj$Bt1)9L<3?9#AH4>E7VMlW2i?F4E00l7X&_Fl zWR#XV{{URCV6G5LK+HwSN^M4f8TV!l-$VG6D`gFiH?GCq+vxCULpc62U1a7Lk zHXIaz4uw`P!gD|L5bW_=5AN98+l5_Rt4Bo@BOS{sc_o|Pah)Dj2E!wWd_m4gLy@>q zC0;s)lj}U%b&_x8BUI87<7VVPQ+C4T6bTw-N)q#A(0p6sZXsDs4GI=FNv9c6hOXtZTMX*EJ7j56M^-zz0$f{B zRGN5#bQ=RqpB5XFNo>IFAXPc~1BWDYzgEdG7C|PL6c=ii$~klZO+>=uL26A}YXp$V zC=_TrVydW^ES9oUD11vr;Sx&(OPtec#K=Kz>r_~98aB7~cFL*_r_>q6bzbF&0aR$e zsN<|K##AT>MoIlzO3K)*vd}OtKCsElfS{uzdfQflbChNPjRAhr?1L1zkLWJ!-f} zN0aWAZS0Ob41+)J(IH8K(6vlPRp}vu-4CV1V`2@fkeSeh6}K6M!8y22KH9155tC3! zZ6&bdv`oY8_Se3J^%eG-qM%8E@p`%vP2`R7bUh0LWm(8xuMNJB44j$ zE5z&Vod{q}7jVr2kIDj>^pr*b*tqnB^Xqqt`uF~idQADO|a}X&@yaBm) z(U42yNszHc0>uxdL}X(Z-)oDp0ox0B#321Pi3LeUlhcgQ)A6avIM!`;@<%26g(UIt z@IYjQYC*p=7c22<*SV3uL?z%Ph_o|$NCs)1nuIySppx;nthbZ!zp@u7(E`1Ij8N|> zK!Gx(b(wSjW75BB0$zv@_BwA&{9oVyX zcb6nLn`vNqv`mek#SfdgK9H|sj0912BfPn=<|9{^JQ5HXHsKBZ)DOkT0{bwGXhVmW z55>qTe27f^7YrdwjiWr zGwD=pFi!gA(xF%nJhg`bXK);TaME?az(kw@T>?y^Ndnm}Q;CB~lCXxM%065d=nRW( z%0)vl#IsaiOzYAzU=HUsstlO&dK4kLTDgvq2XJzuq(~A(dTKc$TkE9g6QHGAkxy0b^3LlB3Q=$prFMgIkFt%gtNPKixWF%FuK8t`* z%hZ=pEZ(3#b|AK0Y+QrJl0tI`2+VMnuT^NQD0jjx>*w<1aC>qHp{FqD^~qsKeDy`s zOl{%v5E__yn&p==wqNze_(Q#?ALIE5KCcl^7W*r}9z~T7_S#v-R+slfDy%I6;*t=e zg{34>>sUaIVhya%BUOSMB7%XI7Meo&y26@JeraJn{ko1HeYmiJAB`0@@uTs=7Jl@h z!ghW%QMjES9V-O+(eXkTKbkD;?OvXE@L-eyk{t>^c>OWsxFRKHDUoiTdy@lFzo&vw9P* z`8-I^8_ef)=0IFs#fUttfrJyi6fUgvM6NQ7EsL8@ux9CcDs7-53yTTzPQh=>3)k@M zUlnc(TEX z?=exhLVltGOL~aiW{poXW|h zA|%<1y%koqCHNjv9A^P!t~rPuQQVt9kcsnnk$E@*mN+jS#11UxjE&6aMaZWi+jAy47UDimpKX;9`ETWmavy%v^!UAtI7&c`|k-Vuy-Yu0xj5 zl{_Fmb<)p=|X>%g-V z9OOtlWA|d`mhk7_yPx3CbhZyqBA0`}gwS(_e6E+zZ^`Fx; zjnX(Pyg_Mng?(Q0nsB|+SQOskH4DS4*PI=0_L|eeEnc%Y{57R9HCLFv|282ACeh*wSO$DcDKDPcZ!0VN84Mw@TwZ zvA++x>mu|aPT$V#bCOR>EyXaSV6S7VyJI z_#1(F5m(C4<*y_#U9n(6+O|@vE#U&&pnT zC~g4IHvKR=Ct?(_qK`}iS(-%@DGPSr1p^$!25@ksbWpZ#`j^mU@TnPnkvhsg99ijK zV)ntzPK*^LPe2kqtz=T23}HihcpypyualIC;)b$;g888jY`3HgpphV{0|hHY8M1n1 z7!LY1jHThkVSJ9q_sT;1<_ctyoE+6s&?`%Ki;X$hAjwKm4s6SA`ezM?;)b)K35*rR zIFWoZr9yW_I0xXlpg(!6-e9mg?;1F25p0a?$OUgnUcoZ8XSw6~<-75#h zW#F79|GiMW7a^VgS-qk3(JU359lc?J&I9JpC#GJ>?8H}5az?Mp3000^1Nf|7%TmaK z1hl(DJf0Vi_r>FX#A7@d6=WekCWWO=LwP)wiH9m4KM;?1#N&*3j6;u*^KS82E*@-m zG3P*r36^8L(l{eL4HP*%(`!x$F9RVDS9#4_!qr~$MiP7OChp*R!dJJ3K}K&1{{Uzy z{3EYG1Eo=566KAl2dXUUPC}9MDbV2Cq3OayOx+Vy~GK0sgx=vYbSrASiqU zxbF7I3tlsq@YR6G2VQeZ1Qh<^$fsU&YIGFdqT{?~Ui1!n1J=urE(I$Qt?-(+M>m6? zh<=9>eg$46`i|GUBl;Km{JYn@Gv)w85`(s@Aa)zLjTpqYEirID4--2uJw_Na_B9H2 zQ(#c=0}7s};3WzOi^dL7@D>H{QE;4sKT+^!3jRt#7XsG$kj)&{of;JL`kZ*YEgpXp zk5NEFSb;mlW2ty-7LV_U$FIcWFXG_KvUDYRgY@U_%) z8XRjaVkYPX)iUA=uVp7psu=*o|1P8E;o&T$aWkQS&8&4MGe^jphr-AVZ`6OZ+7aQa z745z79SFGYV`Q@K!$9|F0DJK-8^aul9$$rjfXM$(|AbCNLLyNP`GKD*>|8r1pJ&Tjd9Lp}$kiTFbKte4M2c;3rKu&bb&zL$+;0X(VvYy5jK`%n3u z34SRB@$=<#6lP2yfKeCf9qOd)c{z%QfipQSCE@L6%pgFtm(sz89@1aj8gK*uOojgq z<`RD}81&BK;R#nHTehs+y`)N>6;h=D2?;vT&BF38A&tw@yZ^vBkCCMN7)iR1QKb7A zNsP!y!sR1L0Wy*lAoEB8@`cZ>09havAWGw0Qh=-rS1OIO!)uhrl5maJtRe--98!Q( zlLF*kQh=->1<3ux;;bMRXFjnw4~L)fn(M?5O23Q8#OP_TpojALYU4Fwx0sHXsZ9vMYMuCn@NppbJ5aQIJN#m0t6~SS}_a=Em~Gpx1H6R(s6_q)QnU3u1C& zUk43}eVabfP)+RD^!6b}6zjlvSu3-fBROhkpI2{kBL|}a9NAcab^%xlWyeSXY4k%6 z7Sggx^a4!~N@7fGo*t-#PdfkY7}_s$c$=4~u9vESXScCoR;v=prLcQ=%D~G}`ZyAl zK6P+LqJDtU!k+`$4KXrtAW@77s;T77$@3;`H;!Uc7-zU7W=&%QxaNZDUdZ#)iJju= zjdeb0>=x}@drZjmei9vt3a=L zDmt8S?rmQ4k!TUc&7-(wUb8;>1^RrHKA)twZ+gu~qd%p$pHti$^!6c@g@pQ8Y$QHo zS0Mq~XuQEvxH)zoy{)9U4fF=Nxt?%jUF-#PE%q}?0HWLyi+D{3DV;~L*4-)Q$j`u9 zaP-6#xq{s!;FL4X9Z)X4I&wG6spCfmJ}0mVWct5Nb6iG)VMDpCTJXpMMlOcHQi@zq z`cWBRtwj<+r?EjL;`AV)np#TjqsrlIKr&9`UpCguq@GYY1Q4$AvKUbM1m81wk#}BC za{|`?F`e-(yvD(C|7UA|T(F^T^{DVHuemj>0KUUuaz;Ux z#*Tg5Yd%g~(x~wFNpbx$Wqym8Ch!@fpd4zv8eCH2osnU*Pa#@wi(u1?ip+tbJ@OzW ztR=HK=a-)HnomTal^GRz2~|bj1dA34(Peuo#f+#vPDtfKgTo|1MRwQ~meY((% zXt~$)MjxinN9nVL3(H4s*le638Ppe>4aI}}%Sf>OC3vF@W+~f|19S46c)TqhUE(nw z#20BZ#bdR2JSiSRB}5c?z&JJ@D}+Uqehw(i|B#XXRQlK54HYtuN>Bs@zi#@MoIa#S z5~NHJ>H5$F;n?fy3V)3-7*n<)WTSt{bD%2a|3^skRh zVpmb7Ok|*Ea;AX-A5g*sWT1b^8HV)8zLd%OOU{%=UU5ow)gzWhXjFJCxK^kd8t)0; zg@q3nQ81T+MHE1BIEo~JQQ?OuSWkhUg6#yNfHe;+F96`T>GK1xSq6U&5D+Lu>mxJi zZ8fM@glGb^mRd9dj@JjbMP_CQkQ$ZgO@JDKeT7>q4S;r$aNEluzarp#o826ptO%ie zb_(!!PQIi-Q2kk&i2Hxx|C@pTHv|6{&A{&8+sOfBH0eLCB-O`g(t3=B)w{Y@B@naQFw)-eH&U2?F-O)Xg;FOpOV(26fAe+ zeA0R>2!mN3NroLyxLIjj7;aG-7ZExv3qPf3{}SG*G%9d((@uq-QM8V5tD^lSY$%Os z;b)b`!DOZ$5{5QoX!r$1I}_fgXlKJeQ?yUQ;I`ArD{dHhi(C8tugN4~W>6 z#zB!ZMf*6Cu4sRb3|F-OjASV4hoo)!FCc04cqB{FIwRSN_SXpJ?{ATbiuT`;Ttzz_ z$yc<$M+y|J3l1t;EaFtO|A~OlPAcku zMouZ}e?>YKwKH;BQU4n0Qq;di&ME4DN139Yj@lLV@6j|xJrhk=)U%L&)UIfTqJ9$1 zRMbzS;}!L@XqKX$i)Jh8|3oJ!>iOtIMeUAa{#X?AXNzL~Qlgl@)F|f9PD0awDCTcq zbe^IOih2|+4c~Q!MY|$eqiBxk21Oel^(oqjXuYD1 zjBZi1QBhUVGNR3jHagm(XjewJE83XoQ;L=u-Kl6}qq`LCs^~L{HZIz#Xyc=XqFo(* zR?)7BKBs8cM%xuFEBb<>T^HS_XxB%7rf4@rO-0L&9#FI!qlXpkrs$iBc60QIqD_b% zRkT~8#}w^WcoJzj(G!X`F&b91Nzs#vHaU7q(Qb=&Dq3#zw4zOkb}8D_=s88pi!nvZ zkJ%ON_E?&t-4RPyv^!(N6|Eqap=i@$nTj?&HeS&RV_AweBbKdbcabc3cWk1f-4n}I zG-oVd(PqX96m3>)hN8J*PDLw<6)BoKR-$Odv3ZI%JLXZel9*S~=ERmL+P$#{6m4#7 zg`&-iRVrF(Y>lGLhw)NdKoX@V=2NtVv3f;Y6x*U`We_kmZ>(9-%402xro^@@+Tz$# zinb)SQ_+^jb}8C@v1b(R{#dJ`JrFY#ZCUJDMOz+wPSGBWwJX{R$fa6EY@ed7jQvc} zR>e$3tBf5`w5r%)MOz(vQ_&uZ9Z|G3v7?Gs9XqCIYh%Y1?cvx7MXQO06>VMYq@t~l zol>;gSf`?Gh@Donjj=98tAq2j%^ZDBh#mhI;q8B9D7VNN7)hNN7)nOBAgIL_!OMJ&N|luvgJ^A`;s{B(xwAi5)~FzC=Xg zDIyYI29eOd0wSU91d-6529eOd1|p&D0+G#LVs{aUd{Qsa#7NgTcLN<#-=3lws zKZY2TOkz;R5Q8!Xu&4ezV6V@EBA4<@Rn(sV_Q)@ln`IRB1;C#A3}8>)4cJq^1=v%+ z4cJp#0ek9q0DJ0p0ekAd0`^n`u&0IqdukhCPu&C9Q=bLwsow+asow|esXqYhsm}rS z)PDo)sXqklsXqejsqKJ0^~Zoc^?AUa`cuH3x)-pgz6jV;_W}0QmjHX}%dB-4GacUL#9DO70iUWr^Zr30|%uco|FZG8XXiPaP~8OWw<4 z!vU|kJq%6imEk>5=Y;opO+EY?0VdR`aD}*+nODhmyXdg3S-!NRMH~fOMY8(X@b&z_ z(I1&P?*Huiwa9h*VSN3@UbudBVgWC2MLy>;TMIaj7H~W*;5b^qasTWBj-v$}7yhEx z42EGd%%oKu6>j&MJHiKPJ)t0kMr1BC$IAu0__#mk3XUgdnQ>%!z>%@x%)AAb*qj^iURyWrsL5_WL*L?nBl!0A3XdlEjq_%{dtDpC&4 zUWw-l{PW?TAK9E#r~X#nJNhBfvUqGWeYaXM&Vs<~jKq(5@`D#B3Czya4>f=2bMVi~ zFQHw=nhvDvhaOyh|9-ra|0Ua<$Bdy$qh`!d8XL*a?;2y9(zw{z4ksrAOXD;Q3Vuw% zUg*S(*ObO4)_N^7uf^=>$MpT8dr1#e58osM8rdbrFgPh28SqbLt#>jri?MS36qR$Q z>CSV!Y+&3DO&!Y=qcGm)b zL@zE0xI58^0KBDod8bTw>@ELH{bPKnYQtx2YnoLPmR66@jQN-`L}_d`uw|V_4mOsN zt7tzr3TPIKyyhyS)N5uLiz%p}0IKmUqYnGgXr{L>DeBLRuTy3a(VrVXRMc0DpVIOl zqBzjSSB#G--)TktMJQEiye~9DY23zIM>F$!#?CmWIeh~hd!2LkBPiLA-IQY9z;{!5 z(}A73HIy^X(OJIRH^e`i>Sr+D5c+JYpV0mI74)XouV8+Ap4pMoZfhyZz9%7b zN%z*<;-9*+#Dfpf$bG!_!cM;fIK5Hg?}Q{r7G<~k<`4a&)h*fgF#i>JIGKM?p84^1>`-fzT|Ct{OWlfYXX>>x zw*AF7D>yGZT`w=t-7|QpvO}e1tsL^v*MkBJ$qJGkFo-n4|eyv{M8WpHW{y z=bXNkYb%3A*(*FRRU$2(f6WoY@O%f!OlRFk^e*K0de++TsKjHf9dji1k!Nu{ zSI=8Vfvo3cvc>Vru!deMlF+FYxuhdK&!j{iF2Tf3LL+vMHH8T;McHCIdwAI<#Ii+r>?#)h5Rq|6`?HEk2yeOX;Upl_MNNRdDCH2*&12xMe z_M}0VFgKeC8;YAPqEBGc1I$PV8)Crpu-$;;>2?FwtnCI24o(Ah;Nu2t4Z5+3f`6p| z=4jpc1*L*kn>AHH|RdB-q7@U|sn!>^(zF`uabJ}-ek@SP0Cn}(V zJEUUx+2N503`k4cS*wGYHxfoDNOL@G=G%Y^aMJ^r6~YmS!8dY%j3UQyE61(OoWPi( zcTQJIeB)Y_5|BZOVdZ0Z_g7lc;BrA5hAWNt8)JBLlbH!)D=#|HcWsL@v1xU|*?x*= zQ1NlR_!MTs1Pl6f#ziP>df6P`_?EIc+S~%>WNfWPO!8jW-{mkulx`@aq3Xs>ifZzy z!}(}*piuGnZVD(Kbl|_V)dk(%=ezsq2mIsjH?HUXn9WSgHSb3Oa9+EuBlC=Fy3IFS z&b(u<+MdQ(M@pXAZ2B@R^r8|EB0HwLJdzGW=Gbmbq~K0P{W5DUXC}^sV0C|o8D74^ zSJ<+A1$I=9oMVn=EhMZvM-Lq^4^!=tsR9AUM!JCl7EineJKX8`9!gnfDIwDyDECXF@~R~RpnAuxk)(*B3SEH%*+FLmy9obyCFq?JK)LE zJsAmI22VVlwPrCB&KamL1(Q;eRrvN+o_Bn5UaSG~&aj!c)66e!I?xr!$j0`vXBWB( z9L-k|qMwm{_Wc_+7wB~bXMcM`em2qqt^)ng(1Xva*cl*E_Ok~GQ;LrPXAc4bm~W6= ze4wiV%y$KWDwab=o;^rQ;~N0X=UQ_1kV*y8Wf~s|GGOQW)6O2Er*GsLSAm+G1EWe} z?f@P(|2oHBbZ1CW_5j~XncP&L&iv(xbptZpR#~}W(MdZ4$rm+xX_r{U*4zZ zc7N?nnhTLZF3QsTIMUs{kP#-GLrC;qqlsV(3V>IQ5WRh0QTH40*}va-nP3o@)M@+& zy}_c8Y5W)7LIc3Ngust(58Xt;Bt`oLYn@^<;Yxueq218418jba>!5LMO$#i~{P@=`1>erg=x^x1mC^D2W92{A^f;z(Vj>5R z=^MY60oDB0nnWoI$hkaIJT1j{Rnyi?wkgA^M#;QaPwQUj)Q@$H?1`Htl1N1t%Cs5BlR zhO>eg&Kj?|)bP`?Y@t=sDFM_g)A$asrSU9%?x$k!Q}CY@{2hcaV#DE`&`_M}q5Bi= z2tj7MonwzXX~X8zhRsjfun=Jc_5kot_hi%F&+a1w=o*el0rUM0ThEoQ|CX*s@H#gVq%sJ+cPm@JR;I&chAet@V^WOC|#`|%r;HH+i(GzV0s*T7Y#o9XU^?? z~t2N3KW z`N6U3jCcwv8zVm~| zKyhE7eA_AEUns7fKHs2EswD*F{Tm_Z?cZ!0gvQ%YmEK0eecNPuy9;=r4N~gbwz)tv zZA<90iV`+bmdEI`(Q7Jg0s8zhvM{>PsZi(^fXP3h{k>SxUE_ri5O9sBuPo~;i@x%$ zuYCH-5MTNcIgP+}%U%V4u=^@zfDOOaxQ+rG9lX}S(XVTbV$2UeyacyEu8G(QERgqM zVA;;VverQ9IiexVkxq`G=^RCo)I#eB_iU zyT$#Xx^*J+r3Kv|3J@;K$SF(LUBh!+X~EL4i0egZ0`9|9o$o#_AdU}~9@pK6b>|G- z1M!f*IHizSB%2Ai59sa#P5VetWWLF~!~tEUPhQA+x5}j>M_djRo!-+!@+`4&8N^2S z3%#Yr!!)`CT9_m?x|}35q7auEQ~B`vmKZ0>_s5{S0RF<7=SgbnGpjq#uF! zym26}W0Kx*Qa_aAJ`tbweEt2L@-A8FzR#fH6U*c7>?od*0*d86sh6L~c{Rt~mE%6r zQ!Mq>RQD06zdEvL8QmxJ@-E60&*APAIb8XUz3vYu+BOb5f7sDXI?az;iMVdfEKi68=-Q4*zsO-hH)z-YSOG-Z!A|DuQGtHrW-(~ zP6OyFXyA18bmIkLoc=^_z@*bdDfkRsOG11$DhTC(AqwS^5RcQ5zYZ-VA$|#j_{d5K z@u6DE4FA7-A7WpdiQcL?ti-@;WXl-4&sqW(v zB#(9s)!nDCw(ipb_favTL2^X?D|Gi!%mv{8@@3K# z_(x(Kx44g@810$je7wge!i!jp+=9Qm^wO|iemv(_Z-0XEz0Cr1bM?8oslQ5{pI=yd zbn8&P^k~kna^~k#d|7Uel8=oL?{gVG&~nFIK1j5Ul^MzFKOVqA44wqHtdumfpR`OzS(@B*XU(@cO5A#KMFN;`B7w`m;%vo z;pPI`O9isF%>}8OovB;%byu1V^WsXkxn@vj9L+5>k!2j_lB&w(LmDhbj;5{6iD*cz z`sUo!F4RXP&v&h)1AKDg>Wac6{((&o=caUAeUW6JosxNvFk$FXzMv05^r*1m6uM9F z-wC9B$`f>DNstbs**^;L1WV4=U(MCc5HV00NL{5rAaWzcUH53(}*P@4PhX z&ol2^y0kJde9g~6OM~v6e4HR7{JEdV8S6L+z6AyV;#~UBPF6AFBk)`X7_T=%aLRbI z?PtVQ{Sqqvw%3W5`j9?P(C0~f?t!F}wdWoR)&OzrsRJI_vlVz`Pk=sm0*}PL1FW^@ zdyt6s{Fnk<3iU3=qATRpa)8Dx3qaV)cx>!n3~8OOS89Oy~R?M7O!5lAJ{j9Cy)u0Io1t z*1K5iNM_-*|aAzwAA*sw-181XKg zYpKCCG3zjfEOsN^*8naQsf&CpE%He zWw895rPsjQK~gJB?8pd~$F&`J6R~k%-GsV=?yi?1{E9J4NbPuV`O>9ismf;I5m+1J z8l`a=jc>VuW3tP`%P0b$52-#hg^Ox=o-%F zyJIx<)r#)2qT`*Vh3T`i%kLMRoyHc?o*#mQyVP`5$+u@x5D%pxfv zbd@Q2uLnxINI0RcPX}DdSw5JsV$G(bIfi}!hr*)yof6~%{)MGtq7+SZWrBPo^F=%| zH~y9te;Xfvi(8Mzr+47Z73N7XYa9a9)zO0xVT@kaUE0&;&G9^}(auzLtNmnd^ zR4*<9lMQk=qs28tF9!8+g5f0^0$tL6z7BEi3d}7QdWbIqotMEP@Qe~zI_3OhTCf?m zGCDq3M7MjS8*n8F(v6%&#vEKtW0c|U7z3w|rWk8*V~nv8m&K6gDc^VkdL-j1+yrC% zi`OhLa1JS893Ub0H@Mrz_$~2lP#4}xX3d;XI`mB;++%lt2q^GDx?`&-bUW}{2&;`n zEwq{LK49_Yi!Ugt1|54{6Q>tXbZjF=Rd-D&ES})l_M%9bG`)C|W83$6LT+Jku45ZF z$brffXF0Zgg~yLCEFSOJ_5^>=EG*9SZ`ED-g~j>)O{h70dU3XY9RWRGz6o5!uy!LY z>k>Yn{(JSFb@kt+Lx)kE;lCXfluR!!f&NjfPn>#AKLS&u%TrkF@ekKsMTNyhAiBi` z&{@L}%PptSG^XTz7+<`}V(~8A?)rXf59RSaa#0wfYHYU!?e+dzFF^gn^(G_JHH3(XMX!hvU? zDgdUOO*L^P;6c4i;BKzxgB=3IBEVWRQp^&$ZBI+V{v2&xNcF)6$WY~La4OT<${aynBkW!A@7IP9N@cI^FJwS3O( z7wO`~YH4cdO5HLzUI?FTYfbVD>4O5rMafY;wV{A*>zL%!#M%aIy#_p))0df?rKf^8 z0H#(*CdSo`^xfi`7{}a}UL5umr*RcFmN0i?QlJ;d zEkyUm8yN1JY5_kraHI9%^cA{1zAJk^11`@cswcZO`LCe*>zO$h)yLKCamq7yp3sF# z!zUb&mXfm5NtTKl*7~r`geepIc_H8X=fJn~bq)-&z(Nu=883IP6)E=Rg_)D~tH>NI z%MeJx`W&D9nU(lS==;K2C-ja&wn@F?f@QghH3iG^6F*U6V(${fR`eQfu&k(8sgmAV zKm&S5aSM3{nwZl&MOy1>p=c&{6^va4jN{l7dGrPn`h+o;-pVL$1--%bpu&J;QDFor zz$I=K#tRf2rr>=Fq7;0JAOtqHA~c=?Y{QC>ivsvZRB+FNQs^M+p@ZO_1+C1S&$0Nz zG9}TCucf?OKNgr9=ZRoYBdNvSRR9Y!BVD6Y->)`wvkRT@h#Cs+mnKOg4+3)0YFvqHLABq*8&CTW<6cEO zL3pHzQ@;fSxq`3|6)JY}_h9jO{(S!2^_Bx!^M_jpN&3JyDKyyd&}uduigwr-LjlO@ zDze+bVQVNeA&T)TgdN@nOCD<-!_0+z11Ab_7Kt3HG*+_KAT#0hg3Z));I#g?;Ee20 z2#WVkF6UU}$(c@es2|((qYw)q)6LM26<+I`-E>d))^TfXaRhC#8{g!zpTD8< ztFUX!5G#S40Ug_~PRMFmp<(|`mlGLqcXbTZi#>SCa%2^p?1YXq?@Zx&xXf8xG~8%Z zpCkUMJ2P%^cNKS+cKVOO94Oc(Xr4&b<;((G$OncI(zk#lf>%GB%1#x|%5XF{6K7L? zDljV}zBiDUmx3dJlUxzpXVr$Y%s;}4U;WBiq30zwiEfsZLXIqkZb-{ioaWLGH1w|H zjHp8j0Dp*+U^q5{jq?~;w4|^Wd5mck%%xTFl3^JRO=FEOP+WkPeisG*M!_qj`oMi1 zZy2XwS~5Tsw-ckkof!Qu(bawmsb9B8X3?965|$z%g!(`-|IWA zVQP2m_$el@aCx?)`8k?JYky*iY>wt{^op`Onu)b<+M3k!*xUJ;kZC zzhL59NWN?fjpvlc>x`G^?I;DuDNC5|Ik?Cyfjxx0UgX>bOS1H%yo=P3z&vPY5o@hv z=2AY<<@yJtkI-jl!aa6Q;jvBE=xX$- zaemOd3kK-?pnVr_!cti%`2qVkg)NlalYs!AGUia=0iVEHtC)FTZ-n+&{aDj^EJ(tJ zg5CWATD#E{!Wpi|gES2;iD@4FEz`T48^G6mOW)+wi{06G=vKqPB&b=7M%16kNz7HdZffwe!^m|*V;sWwIKs0gI{BC z65t)ew+-meWzmryzc${4)sj0MX0&im2YU77ZW#Y>P#V8X-gyrg;GCW?<`EMHPV5@!+0?HYo0Z0|7)?s!^~O$u7l;%)j2|oN zSB$-i_6L&DZ!msM#ePf0AYo)1{{{W9@d;8x45^{vNDYleDhcn|Ay~|_LenVKNvYUr zPlRydD?9WMY7Y4n^{Zsi`a?*iR3Pjfp`BQoQpKS0 z#?YTQa-PM^Wqjwx_lwSb?8nZ|JCm?;Gxf6kKz?KK0{@>6x@NHFZ4xL2|A2i0Evbhb zK^GcO53gmdUCdlA_X!;|klQj)dRjkr$lZA!EFWzLoXC*C?G*keh3>A$<6@0UF0yBj z>E)*bDIJ4!7+~KC43`B~zj8=G6JXscfL?(-7GXJ2#Fk=J&ByZqjmMR*XSB;`PVHwQ zqdqd#lcBrQQ8j(&?ykZkTZe5sgOe4aqcEkGrx&I)45jk+b~3KQsFki$D}fYD$sX7^ z1d)9;4S;eBGj1dV4y@5Y6n2N9Pyk~1C1WkUsWew#$6Rri>OpkZnF*E(KV`E*PT7H; zDQhV`3(nAe#@gcKV(QrT6XLn0a0L?TKKEmr&_r@hu?vF;xZlK{hUr>nMovtGMcoBkl@5k7DUr-y4GXKM8+{e@WRZR`Y*p>%n zvhFO=mmMqI58Ic;T|iFvTfNZVT}o8Q(_y*xs`c}1P~?dLNUtNnf%tyj#-|s{WW%Uz zj&1u02;=K>i~F1OSgL z5rD9%p%ccxrXJ+aeR6p}WXmsOvZNelbtMf)Y7(=&)P+1RGwd@|`_+>@v!|DL24>aB zih`vl0bQb8=_&X-0O0xT(11UFv!@GZoMTcP&HvsLp&w2X2ag4oI|D!2j`TxKEePo^ z@ROYg4>di-zj~%karSfou3&bbbwEQ-{xT29>BSS9y1KV!6?T!|sSbOC zm~d;|tbEvNXL>LNbE221p1XkK!Dzkc2)hAI8Rt5JhLof2X&NMO+&=pdD>aMf+vjY-70H zeSW~%<`K+XK~OFJ_)F`R%&g$}6>Q*_zrvweuz65j4^}i_U?|5{@*G^l{kDG4v9~mh zHU{}sORu)pAvSZB;IeUsE|8uL-__2h8QIszwZA-2w>qiV(Zg%fxA zvkM={PH(sdZj|YX{C37JVz-NS30_>}ZGga<-<`e-r`cY`8?V$8EY5!k-2nGYgW_>b z2kaeng%h(Ih8%*ffKX&d8olvsaaT}uC#}aZly^?nsdu4^Q$-hb`>sM{+%ghjdIP*_ z^Wny7w`J_=7*=@FKbXEVvI7~^r?;C9441M1XdN7Tze-%tQ^q>H(fyMn44kytY3w1P z5!XGwVf+#atn~-Xtd!!(Ny{blnC=$CD?H|CZlux4Z?Ge;$#J{;8n;X>Jl1fv{@+$@ zT?=x~Z@FhS>ImewgD{`zn2PBa^$4~GM+eVX5#{-X_U9kR`9Dl``%i4hAHhkox8%t3 z02D;0M=6a@6XW+Z9Ru-@xom_1er%_4FQqP~0PyZH0F@rr3R7#993#MDj{0#*-NO>Bi;hXs{Pp?FvtX)nCvhk4Cq29B7lhMmb~R*Q~*+Pff~v+xZBqiX9I+@pID zwQV!EUL$9V!`C*m;R^YEp)avx+a7{RQSyxQfi%aquk}Reoo9;SReS|}GKis-*|v3a z8gM9KpXx_&6hTmgj_HYwgL$<$VA(i%c%aLYUdwrqrxub+;uvL5ayn3)nD0lq!LE`t zJv)q>NLvFw)MFF?H^TiE2U1JuZGoa56l^JkiJ;I)?11uyuA59JC9)2k1XL71rCMMY z$)rPeI}F?{@(b8rkN|qKgN}ymz&Td!HKQAEtkuuJ`jcL>d|?YeqGL_m_%wf+x_Jik zm#Q=8M1)(G9&>^uUJ9z?yo_sHdi`$-o9ZA%`p{>pltZP}2f1 zc94(pR}Ij*g$8^Hb{MPqDHXTPe3%25Xo+Lb!W3V|OMdu_ybK>?wl9o8q>2s$;w--s z7(H;C^>p?Cn+@L>A6o5@Z)Ec zYVcb^UVK&IKZWOb1Uc$Z#*f%lh^0Cc^IN&=*yJS77W&?R8tc$A7VjrcrLl7KGz)dr zavz?Jygzk_S%=(@;z{u}{B0%LqnvAyvK~FIg0D;#S|`65Q45WfdRu`MoI3+s0zVqu z9Cl+EXQRIs&ZPU?v85j-h4|O$NXJ90^{y0HiiB=tAAkVDUZ5Ou$T{+ikctlS&`1Z; zN{E8;w9tKe>xkqia?|Z828Ao~G$iNA>Xm0hMxp<-qxs)#L`8&N1?P+51JFV-J(c0y z4kRf0y;&<8UwfhE0Ap>%2HYp}-YH)*2Hmafo?02W{uf!82Z*6L2fuNa)Ya~j%3@(X z4>lYl_0NOL5#c`;Y&a~w$i!jRQH?QpV0a`Ou|`Dw{%ZE1W{Z0VYr+J+^8Mi zi6=0khQ+A!aIj$?uM?H&PG3TYp=}`-J08Qe)B95Q2i&F-bnk<9MWIV4}n~d4do+WsEU*!4=ar~haOQHC(z;FTSB|(?O&C~TSIRsjX5D$ z$8zXA#p_Xq3fkbrFs5y!(m1gVjLjF@ZbU6@z@Rv>FrS(02)l6usAV*OfY!s00iEBp z29)6OAdX0YV*=LiNlen4aC;DT6zoB3!lTH;46Oyj;CV+F@Rr$SK$8wF%CDHYp6{rI zWwejn=MFmWvJp_e3`i6}X+bNWN_-r0aw8jmi@#^z~-t=BF5Y1W8#(eEomPHwpEW$wmLGu-DziXr$|n>uBw6q7!b*XgzD`zJy1K z9yQ@c1aZs_I;;akg{tYNA=VrJ&Q(=mX2QFUs0$cxsSIzK2j2D=c;0lPz+l6fWBb3M z15I>T(y{%!)E7x5onCOZcE|P|32}5W0JKn+0;$lR{rC|5M_ldn5qxw0q901sGhIQf zjiq8rJ!w_U^-G1li5DkZ*CnZ(K*PYS!_QIZk*qNZdmXwu>=umDp;H!k2r?pX)-$**Csgw_H0bv zd$KMM-ULHKC#x17m5mK_cc*9jKhXDEWE>uXAmbwq?7pCJ1C5thZaOsdv@u`Ner4QG zpS3gxkMcR#$;^#>dtgxFP&$v2gSc(=}R4?Qzp5j ziHm-VjO$S@+N{cZqEOhD0z4+<$;l9kXz{Kla~g2RLSrf&>c5NL=2OCATF{mB1~(M& zP2XcCsJVdbF1;)7FBkz1^9eRum#R^nv*fdSSaKxDlc@Ye<1+-;qgxTeZ+lEnVVMWO z*KN-RgZ@j}C0nly)MVQ~#D@6Lp8fkz#5A4k7-kiF)*vkY+ETeRjLyHQsG47TgIydX zT`ymsAQ?s$(ocicg~58&5zY?Mk+_A%7NzkUq_MyV$1~H%p*3!mc}1RR;V=r@@E|G% z4fvN>>t<&9djR$1A$vC8+zsjM@x@Z~;W+4COOoR|a0cx_H`nkAiI4BV>j9gidPPr5 z!<{k630X-?KTv{aTp01zsF5%$u<6b#u zyi2pK3GAHTbe?S~fLKH-XR(sJL)Y>SB}bz}X_Rwfd&fYO>jp}AO|rJ=&6U&}z8%2C zFEkeL-dxAbN6?$N`m07gO+^cn*(2-fOkCfsucuGn^_A&qCm#P{rg z4VeP>=GtfT6JyrbbIIrgt@UpNHwj`sO44 z14isfh{9;!k~b-lE%6HcQiK7u&q8AgA6Wr2AG>sCotY5gaIPU=FKRrO*hvhSI?4OD z`F~^o9!u=sc4ltjb(b#`_YesBLPvG&eJE&v2PFaGP@3B(owzcIPLQ%@WWfG?H;BW# zp9#91XcpKrBcdaNo zAx)BxKAJ+{lk|8R@CcB|Q3;^pTrA|Tp|o3Q73o}TLl{`E!I;k1;znj}wbqji!f{WI z&MmlG0Z`a(v*;8+0l@VqWdE$f`S?ZYi&Twq_Qf_xl21QW_J-dz8m1*dwh$?Q|fRNBeS;wb6KxAgMB`n_2m zlcq{kpDlSFsOL}Hw*vE=3wjEYIxKIQf_@1OyeqrY1Xhn5gn!gAJV|xCF{iK%UYYpl%alL2wtj9b+YOa6OkRO;8}TM9u1R z(@7g&W98*?jVP8#n#Wk_P?$)=U%(yipc^~f;iiT#E`loO^)3yTLZ0eOg{O{`IY`RE z;WPX@mxFVwB-!YtU8$v~a()9@Dd&(>Fz|TZrJnwYX`t$TR`qML*qbC6qB=V46rVX> zh}zKo5E}8)r7!Eu+3!)XbSH#dB!nOCZ7`b|*AlA71w(I;Q%*yI-SA;b(7mQ=Kzf;O z9H0q*mo7#6GvY|we;n?wIKBv9!AY$32s0b`B4FWQGxfB~=W~1^pXJ?xv1=@jf7*S8 z9jg$1$Y~7VP>i?Mp#=obczZ_1>CV=s!}ej!Nk3kkMaz~Jn6-;vIfsJKJ&EkCLnl}T z!7qroT@J)un9~t&A~`2-wmn|sb5tWa`vJAXQx*;wKem}o62QU)U<8_CMDQg-zw|}v zsk7`@QCf*KeM!1L4wr|x(^5;GLYPz1Lb0B7eG&Xj_(b6QjPpgCZz^7b^=8n|;gxb( z^_HhKy}Hk_ci1i)#sq7?20T9=Odma&9C6PJKxMq>8C69#I=C#a8=5N;E$}K0KhNz8 zWc#8XTfKHZD|7HMIGaUB;i(c{!e2k;cG|>tevWAz?kipMOmeq)?t}y5kmacj^6cO} z5jA_tzJm_%i^zK3I&MuEi0&CL^6SnLUMJA7i~vXPwM2Ms$-3}ruPSB?C-x3!eG2Jt z$qph)O(fFnApfTw#;abl+z3+uLHr}*GetW_SD5UGfD>y9L5;m51QpX5`YFI)hv3`! zQ3S@PBa!>*WN!^>4mBv+AK@cpH=8AFv;#Ki$WS}Ekhh)%axRungnnRjL$9ZngA#5vhxwc207dZ*2oT~#vZ@4&Nacxnhhe!OR4p_EbiLm zxUM~yk zo|439XJ(0n9NeEs^|6k-P)myc&d*ni6UBKK^v(7&vkUCFJxfX>Afaox04Fa0bKTlA zKe#q<9GYpWR~erueqZ1)q$j!0xpJ%$g&Rt6JUBkR30DESCL$Vox-9M_`Kc1zoEG%+ zC0vdUsgGwfAHk!#?{RRv44tF*t@Q3_{tP$^-;TYbS73=-JTLYn`fkCy?tE6Rv)&ch zY`4Z$o)*0KMQ}L<-CwW7UQ##XkSx`U?0or&F?QhlkXny+2tm=QXF{(}Q3PH&7P-b6 zkNz$&MkRVc_cC#e@h)vmIocj{kzqyk#ubic+T`lyTf|t=k2uyi@VuRBC9KhbL%v$J;iJm@#q~r zu!lGA*@(A2P(6-`0Ym+I&-2IxuJ~&c49u-`6Zw9zOyo*@yRqDn z&^rI!?aJ+T<#)RZx?M9c8t@@6UO%9o8Y@q0X?%qClFNS3>7z}|Q3-7`-KpX}79yg^ zb%y7%JGQ;Yp@YuAzro*d3&Lo<%&tC$YXs6$UAc5W+?U#sgc>;x!f8G*2r8ISNRNwj z=L8Q9VchD$r2#rvb!vt8xO{#iJ5*cBL#rA0@bEk8%KKTgPOc_))uxL zaNVC^^k-C>{Nest!SZ&i9o^l|TY$iwLnrw8bis?|6OV%MQ)>@O5#d?ARM=hOwZUNe znp_&`7i6J(aZ{?J`Kx?+a9;NX-8CcN-lyZf5%&uL_s?)KT>Y690G2My?R?ENAKaZYH6EGRvBfxHaZAawW_fC(xFULc|B zBRa)B*?3ZEyp0@7uP2*qsqt&NGv$QRm=*p6cqY`OXa_@IB+LXm!dKez=nYmDS@-n_ds=5z6ZLx@;$gD<)c082zfn93C&*fi+gs^+c)S9 zxUq$PZnJq04gq|$=WV4ich84Z3x0F?BYtT?bJrj&BfVjRraraXr_#+C^o6o$P5^yHoU`pc@<9eWYey)ywTCo`i zf^hR3j+r--u!=(ogHz3IoK@5glwirEsM;b#)#iZvS>2B-q*??n#EFz`htN^oEqDQJ zrYw>d#8>?G<*bC5@6vSm;3sO8;P)>Jo@pC#Dm^Kv3w%kuuCMTtkRp0Y`Uk5&=iE)4 zt=#rFs(^{_fV{--X7RiL#5xfI>wuYPp>{Qr@RH%A$10Rx`HyueuDrr+rol*Ch==z;-$qTxd$h=& zTE1P(OYmu$78;^lx!}{hmqKb?3OXwj1dNI?AbWh6IDhe3_tuf3g+e}$j_r$3r_?#5 zq2u(o`xo4^eJXk9@Y5JDELoB`xBi8Y7C#l z&yR-4vPU=CHHDzn3WnxDH$+_4lc81gww~B8c&xuc9_xjnpF)ul`WrD~V9v7J;I9&F zgZ?8(*47kPZx9!Dl#qmv^O~@|X@KI!m{oI^#Z85A1Gzp}06*H@gn-gIyneYGDiWA3Upm7#8 z1s1jiXs#g!YM@$&{)XJ}4fWgbF}UrA;( zQr~kuJ9TX{T?q?qc{4Lxcwgv{5nY19!SQ9tQ4+Ou>YLM3H)jOxn{vA1S4lxMy+*-( z=yquD4QW;szMT}gf0zNv@mWk)FMVcIzq%GfRj(?7w&6}Fmft|nAT&5 z;Z@PJ!=we{i<2M%S}T}qwo`xOz{&HiekIWe23rV>U{0EZpfQ?T!r&wqL^gweg|pthKf4YGKR5#9o0uFrKoIF zQB}=`HPu<3in>)5HCf8k{K>hjC5^Gq(rwHMay6c*pAG4w_~hD^a6D@njpVeXWD!4- z$FHiHTwJ?xU7gIFL6K#T*5TKKCzn?E*2rk|BOYBo$NT7pDyzWN7t}6)nO2s(_JVZ& zTfplo9;%W#F05g}248jUdaH$T7u1m0z8D=wA%9`WD#Zt)Ab!rehvrq+`DA19L6=so z_gi`6bi>udHY^a`GT2)tvK3hqALSzyim9>vpDZDZ{gNYqYoI+Pk zMO|IIuHn=SO3YbbZFSQjqi7lUYJKgyj173u>t5o;uXz-?2Rv9b(>v47g9m4om8Ig} zpzQ2fWlKxT1{S&LnTAjAQjeQ|_rR8 zJ@yB^^U6~3Z{UM-=Fjmym_{MBJn%vH68ff)zS*up?0Xy&$u~Rp55?KpK(KEjbrepG z;cd=9Hu_5IZA>O(-yFzBcBgcwvi2!#qBFaGLw4Ss5@>z+A?7-#*f36S*}962zL|bs zZE>}a=9k$XV(t0tHeMz_x6d+)7tm4X+gMS(-sh>>xT*?2d~LgticNA}q(E_XeO2Ym zja3yrCF`8brnTBw`!qIHw3hcd3ZrZu%1&mth|%R=q^uZcneWjW3~d=r$E~8k?UyVd z_6!#i6zF>r5(=P!t62L@ z?AnXXwOm?MtPW!BIV_u3Jat;1J+Xd54!NuCmpz|+$MJq#&&KuHj|ZpRkvHv5)cP6* zmvyO31`N41Z^|>dqSFZAH%>Kd`Hg4#5!JMppHktuC2T-yruwl}%+|o# z?_vd?t0vc)iuDgwQ4O1zErYc$W!}$Ker8==^+W5$K<^Ov-6`?7(+i|h2O`7q%3Ri7 z%8D;mw}kbCS-TTd%4tzAr`P4L^VP1C-&nRxi8L;`8JyWGE5#~ca9-A4#O~>*1I4xL zePT)~`Ix5U%Q1NutXx~Q3ivVYlK4@MgAj2U*b+XmwdgK*lJ+~;lzv*DUF9nUG8U|Y zT$8bvS+KmWO0pQT;7HbfJIn2-#W{5};xb?D#wtEw%oA|UG?w2_IRGFZWA$TIbJkZ@ z)l-|*%ytcHpT}nRUv?ILf%Tz{wf^JKl3^6f zu0ejd3}E9$>yrjD`f|%vuTLsBjJR3b^hv3zpSx1a7bKuj5D+lY0wmU(S~HKU6AMRJ{>M z3Q_`J@LofVZ-u0seAzv0Za)pUD%O{6sHyh(sx~gFs>8OY7B<#TnP%Zfv2i`M;3b(Y zkF|T*f_|#REOCC0YRbDTzVGJCf)?Tt6L2_C8gO<2%j>5*7~$fL)xKU>6r6YedqT7p zNBGs%g!116s9b^@%FnwUnV=77U(7uHbgifg)4r~HeU-HYah!Y670_x@F3Ai?LRC~d zb2aLgfoVrYrOR9+SThHqE!X-qL#baw9BhS?C``2uy(++ zRcuAS70jwy4H|agI4)aUx|g*#vWClS=VIf%BUf$*iDRb9Pm3cGpAX8<2Opf0fMBKp zzc^X@7WT+xcH+Y6`e&P;b~)R&sP^;Vf{oY;MXdcBY-hitN|@B5l{K!K>QxW-pqlE7 zK>9yBdC;O- zB4){y`!cDo_Dhg}+5K{oq#pFvUXti)>%bKDPj>qfR};wPh6_cW?8_mafI6{B>Ns|z zo&tLh6pE`$Z!Z*G@W}cqE-s@C)EAeKhb7@c86XFeTgs|x1kWRykCR5!H2iXSF4R=E zrg}Aa5omE_w}FT6V`cq7VoAmN%9<+CpVC@?T~%rACczd>%@;aQr==csdKZ`SEFk%~ z0`ro5!R@4N`7wZEQNK)?=z(m6!$u!1%Vbnq>0s?8%+=4Z;sSvH3aYjbn7Ed3QGb%2 zXJgd{@WBMTymx)Gi#}54+yi!{f6;MKRYj$QGTyJLy$P(Ba#TTdoXFazv)lU_>Y^&h z=&SrSSa`9FR4L%5AN|uEq>&73!Ze1dNgzAgOk30`_VDB6j(PO7tsP)J*DEsbY?F(2*KOO1KQE#|##dsF1 znD#Up&0~}Msc~jaP32%uvM2OiI6Se z()G5X-dXIfe(Qx8vc94Qbo2tnQay$WqHqtJe)*Lo@0)+5hGcRsYgo~*BtB>Triz+s z+PWM=z)rS`HT_RjC*TfIk5-V6C$n7y(R^JejTe1A<%R^wS}-ubna zRkf@-d61V;U;XWtF8aozN7q-Zt6nv8STHa$IW>7Rc|}?9TXnVTd!zRYI|{133OI4jdV%JPDtr|d>F`&x8U6Kkef2ttQT^5a z>POdaKyRz7H*CtT-LQ!j_EXwrF?n&&v8Epr)ypPxLC>M=5Ky|Ywx)(AX;W|GNMA+D z*3>={_vjF%*!JsE-Tn16ZjHOtN-$r))l{u|SZJVKYw#KuZ7x-7Ub2$_chZ27AYQ&V zLcDO;HB}WG<5SGx>GH~WtJZCBRjgVgrLWTdM#pjYoI3Zq4ZcUInM;kTy8qRxUAMvS zt6Jopw_x?^I+zA%Be3#*JK9DJ7Dp7HNL+ot^y0B8P3BD@8L&ZAIG;hRKPByM4Wa#btago{<3)9`E z-ZJV1*dc5B6Hr7Y9B|fQwk)bAilGU1T<@tEXCgQ+z#U*1)X4jx6{8i@mpjkE%HL z$0skqk_0wL5LDDCK~c~G0R=*JA=wa+mq<1PC<+l05(E;HO^iiwPZBKYf!4O-ODk>K z?grc1mTIqUt!=kLL91=?<+ikXTYGzU7lhuMRxYH~n&0=CIp^%|*_X5Ez4!n5e?B*S zm_27+o|$=O=6RlZo@eG*tZW%oh{g2bEBt(>!>|++OK2+=Y}+X5j#!X(>!o#;VYIUF z_?huWD7bb|4Kzb0v->h_)bjdHco4)AU5f)qEMcW*Te?e(!(xX?JqR@$h)uf|lK=DMHI^rmWIA+i?DnmJgVkV>RlrFYDILjFGr! zPqrFe68omxm>@QZ`sDadk2j`+O?T9_rCWkzK}fdh@3%({naCEy>CoK@dG|wB|Iusz zD4kDHyTuGn9vEhZc-ua?aDX11TYr{B75x zw6$3mH2f$Hki?IY`@kM`HW=@D(`xOZS^WaxU68?&m8hn29|hIP zQEAUin`QNORa@Pol1xI-l@0Y<+#8#OT$q8lL)K9vd)iq9A+yDH^a0G!v`KvjNdkf} zC)cIh)yVc-o@3G4C+l=(+mO5Mn@@~TSaK!ROubpR<$@MXbJA9$GU>*a4h?K`X3f@p z7}9}iwQcnh`?|oc>ss0}Xt%{hCm9ALtPEOiFV*QLZ*%aPKB9HEH4{ugCui7oX~KNe zIxQ-^D-2!W;hJ!nVC`e>ZDxA_Sk|tJR+W}(ooAhZ5 zns3PGx~MFj$}Fy|NxDN&+?_2j%Sr^SvaQz?G43_@5_Tif1hw3Ywt%TAD>@`X6Rf8n zZ6%p|HIrvkWkSW10(CRv4=;F(jWc@kZbm(!0s=2q-+<0iW2h#}<5)t!*H8ythu|Yi&DjF!2ssjYvhad)vlnc8fP(l8K7! zE1(WBmGV|`i+jxETX2fXYEVc9QB3;`n20??k}D_^9%84ct7sN>+895AG|I6iw$gS; zqTn)U9!O_X2839^G^DC@bgVgpVW@-=BokOxZ@45QO8zw1RnUnCXknJswSqp{(k9Dp z9U6=T7(`QBW+vS|V_}TQKsuzBnWlzT)1YsXBbjJ*9ZlXvsYZwa*K%Xk;_?;C*R5lB z*-i&Eqzd+w8r>T?-QYDP-+pb&qz<8CGTn;O)sis1J)$hF;iguy=lC+N&jU4*-EgO^ zZl>XiTDz@j12zpXH)7G)YQrm@pe&QJ>9P}>HiT6y)EBKUuynmFgt zbkjYRPcGY-NpEmqsTI46mb;qkS~t>R>pHKrPL$rLD>J-EDMv-isXguUT^cZk0wGxP_i>A8#6!7(e)$O`kC`VM6K9^{LiAnUMpSItRE1y zqxG=%vOPED*!iGTG%wz^p;gR3@_wsp-Oy+UxibZCx8HcsT?&TT(yo?Pkf1%b<3kWL zk$Al`T5m9%b@jtBR%=_-P) z$!M!BzLi(t5bG$rARe zFPbjp%`HtC>{i(xo@1|#(gEI=(WWp1u)TDaDR2|4{^yo;3ya^y-vlS&*8Axh0dVrPL z46U=pt&l?E57DO(x{`}h;7luWwr5t2&=x|p`mkgtxz*hs{i2VAH>x)NXxu)iRlL@C7AG{4EyfIZUk!-Rjqc=D}YBc@Jrd&2pM@3UeY zR!S%1A_skgs@o@o^BCJRJI8*DMSh{OI}7Wit8Q}!lK`?%tJhQsQ>PL)vSzZ-7~48+ z!TlUQfl<|LYv*iZf02O)PiYK7X3v@oQFn{|uy|YBH+Uh#L@P}<|I)e}+d1u4G<%x1 z*0)9{PDcjZS{$0vDfG zWWr>2Z=;O|F=AmVF-E|09acgyjMqjgw0K*bOL)2^5Axn*y)`5mT+D;=#`bM@ON|U+ zF6GV&0PT$VbVo~!`98r~Yimb`7It;+ ztQ23~w<5}C;zf$DVeYBPv2%!^772Y<%o%Pw_XCtM?ni?1pSX|geFk;O0X)CD$ig76 zmW(N9xx*k~(x8p;R-AP5Wa1(;tvFVHA1)0%qz@DZBGza_XpDwrVzWxq4!D@Cu&+rh zKf3=ZliF(l)5YaR` zwMHU))Kh_al{tgNsv4DJ*Eo4hABsb<5vB@2mh6r^$wEnWgY9j$K5VkI)F112sZhPG zN$v;GZzg~lBZY3lPB|4@m#(W$S5K;H>?Tr7HBu9tu9B3U*-jq&?ml)>}laElOle1b2bgi(_@dCT6)s(OTYR zw5p=1ZA0b_YsRT{yHisJEax_gV2_t{kyKR1%AE zqY>M4bB>+CR=p;fXW9$s! z=dV<^)9ebB`bxS=d%?C1jnXN;!&WdsDZ0VzJO;teCrB<;lToFch~62b6S&OQzKR=p zgv&rV9Zk1GJ$wf2yzT){S0qt0q4AU&2Rq32#|UcfZfdbzHntwYLf5{kvA!N>1O+{! zhNUth+D!{wtleUT)nUv3Kt=xBH058kSz$Hpa5-!9l4{tEdko~QBndjOkLE^^0((ui z>%-H$3CzZ1KXy|DW(YfHf!b|t!Z?@V3c&)&wpE%*B2|G~kvEx_7}`w?S%%IGETmh| zb*VD5Qc`P1yqFMTf6m9ST)?9?*3qrQ*5^wWl>As&Ch9hsQWyomlP$oiv+cPVt!iJ8-e7Pa@PRDjP67#(V%RRRVV-DqkU`uvH0p9C& zJ40~=HV#^}3A(HA*g9-2T$$b}_%R#QEv(1(O|CV$pzRLmW_rTd7Tx8fzL0g=O$^%q zdYVlgt}JAy;>W%@p^W9~%7k&nIWpXKE@^(f5QBB%XmnCz*KSD=u;}^6w4m(xBvj+@iT6M>?oa#Ou+cT=tcpxP`^9Td17<+Bj2C3*F{Zk|C(9@Ls3mZuf{ptTdPqnA5o zGSh4+h;fl4U4BH}OXb*W=PYHwk=sv+s1(~qTMJc;qp5T^6b(zUDNH-ZUa=WlvGFDa zhiq^PO;R$wN(zDUhEiR}raKs0X@JL2MT(jeE`OXe8Ct8jFSJzSYiC>J(G*C6zsteFR!6GQ~)Q|kur(9e!sAm0hY~q7m z+q}5kDVa&u(AJKLsQ_bNU%$=l5I$T$9r_NtGA55lW^!R9<@sPSpPSeOmrmvBL#!^i zBv6Aon{Yw%#JH5j4W34Md1A9jwUm>msgtGX5I1*j21NSgW{O*q%;vYrg)=kx*EZ!E z0gm)zWkfLaI1_?bLbxvZM=ebVL~qQi-OR%6vhWOCoi08Q8ow_$3nx@>0_czcK9>9o zqfr2HwAlJdrMoW7`Hz1fI{F$H7C;c2UbV>;l{Qvf{JIQE(J zU_jb~WYAMsz6F%7LSgNbSCN()$F5DA01E0KsG!3;rG}X(NqVf&(0T!By6U5wl@`0| zttz*4U}LuQ#^ruyqhr48XyF#=A)23Kv}_&ImBLP+=0=CrsFAizhM%Q8kH*JrmMzBg zbPbVd`V8 ze@?CiY+^ezClyw}$?vX6UQsG~V!c>9UB@ii0Ep$K)P-GA0T=g4 zIg7C@P2uypbJyaCmIf?1G2BC1w$$CXyl$)5wI%4*(w8g1RfV#~|~(iY8NwOFSCoQ_t;0JLb?(gHL|rA1&(Qz%o7q(zyV24XtmU{R~4 zfr5z>HCM1b)2xcgG)2ud^m)04N^>j<#MEWmgy?8kEjz3_Dw~4FN!uptMK;LXv|gimc5H>3-YI+`<{)M8$cPeC#$78OtG_P9Wp zOe8mDKFBV4FTddi6lZA3n8IUUC}tYn4I!JDW}qNcupEm=WBPu@iYgE$c;*vWHmeE; zAzSU+bzTHyX?=rk?>xVOu54;;OC0HD>#>kD2r;yVu>+3XrsYcR)BxICQb0Gbs_SJk z=DTgK#cjgE8O7*%)xJP}JYyp?dHPLRa6dl}qL8RvPW`!|^Jth$_kLLlrK!1)I;>al zq(iG!p_sZ^SOO{b)^w7qiOxu1e~w#ZbPKo1b-pRN&J;E|ehnlq<}%AYCCfdZW${l- zQ7%S&Nc-J;i)AncG>T69t;hr;dazKhMB>51_RZ>l2IqjgIrxF}Rp5-z0qEE}QoDA)h&<3R)A*0YUvJj)gA@Q0r z2nfwvbN^WQZ$-97YK#-V4T;`ZQLJWzOp&%&CU{lj(kJ2`g8FM2gY+>gI z+UsKoA{C;4X=@R8-b&q)ToKUwrmRI|mIE6t0s~*VFR$Q8S4wzd)`*XCEQSkHt5@D! zn-1&fjgk>SgD<56MzR|HOFES3R%v|8^@cW(LStUewA9Uj*A(=|_eD|EL7xmtg#|sg z6|Eh#pRg-qoL74wP1a`m;xzWR8|o|YD3?b%thC^ZAt~y`^|y25(*#a1DA%WM07=PW zO-h%3Im^;u&&+SEVR2zkiWbS;kWkZ$^Lz5TGvPj#rbGnHmAWJ3!7MHqkl0JGMN>LH_mgH#udKhP9dknrp2gKPuJYrn9qHz>aV8kIapTKQqMK>gY1Fks z4);~5`;9e9Y*Xe-GlwvwG22Z#TrLe}Iu2lxiQzfOEZmYMRET@220f~6Pwc-A*(L8s z_cXWT^i}N5>hNKT18d}`TUXoYZVaj&$4`YXM6{WDqHxhCR~VQ>o=yYFtLN8G#0r=R z9=!1l5qq3EeB7g>-e^hm>a|&uTW>JbWV&m`9J}HP-W9VZlMJ1-_aI}@TiZJ5Lzbzs zxwq9}o$2NP$n-hZ0W}Z`kd<4Sar|nVF1!Ka0Sx-Wq@J!WTzk*P#@8q8`sSwf9chNYicOLm9kIw4Z{4(|sm+ToK?)I6jM-8| zn59n1oz4~PGFeK9g3(fqbSMT#*zPt9oR$S9vWrAxl4OnUqyHlFK1Wxq0a4Ms@r`lS z;u{{D&?=~%M1|6xCB@Qcp=eufNe}M)fL$UxE{OKB`fcrCEavfUv)2RBUehjehCuG3 z*1DF)rVS`SXMoJSf0cR1vm(*V_*_xs{7q(?3(OO8EZIz*LI;x$CDC-b36BCgBLNL% z7h3@78Dl_|B+o&sLwtdaCSZpy`$Q|0b4TWIkvS*2;!+W-NtTKTL8NnyDcPj^i$v=Y znP}nE(=`>cEpi(i&7=1TpkmznC~G?Jzd4rHENpFWYoz8tKP69lrE(InmNfcd7Q|Hr zxN&XbkIGNcMub{dG2aCH&9vl|LhH6f#Q)w8vt@YPQtZw=hRF zT#`kE1LcKV8Y@6f>8OMaH5*BA#a9wo0Ujj?N^mZ#Ycbv+#I~_Kd0Cx+R)BMvxWh?r z^2LI#Bz0afNJZCJxpZ#_G1X_06zIh_a|mUSdM}PoGB%44h@0RUCUjbccxUqP7(J4% z((1ZURu$EQ=@pt~q#KNoQqA^_mxYSirLv-gt)TQ-HuF!A`H#bc!3sXwBvn0+ zt&`Bwbf7Ss$u?@NTKk4gO9w6C@XK{+a#OuTbcKFaOm2LWB{lkt>=vhmR2(N=I|V46 zG)O`;HA%(#g$`s!U2zw*bn;GQ*ox}YWY%-Gnqv*0#M#C&N&KvmxrH#M*J1;}WccqT zBEx7CsFRUV7rB$oJR`Rxol*>H6`pO)DbNNR?pf-?gtTba5Jw&!26 z7pAF4oQ+GTauOL@ahY4n%3b1H0AeRBzF>9FJ#DxZbKRoa#T7V2HX}_z{WD^u%f!K5 z^8i_Snj$rI4d5|!US~sni%}%u+%4)&Ho@aZDY`XkF?|}1$wA)UO&eqm*r=_2kD*MN z`i|V10HhorP!|`Oz@jIwz@?8MWq$t&j$~Wg6$L53sB|8{63MhwHllHL8!;Q(b;qT| z*&mZHN`h&zCo!``Ggy6uP0Z_GG?&ixU8R!VPl6OA7;fq{V6uo=vd9itnyvT|u!$0` zY;G1}Zn{MU9-q8?4AUB)B8%CW8)ajjhQo#xm|R&d@e{?1J>srOfFxxBF;Y^ODu8q} zK~Ze2RL?3Zjmg`I%%cQ`Z={8cPa2v%D3d)XG}0I1lSB1&rW1PCe3|agGAr}QuPM6H zIYAX0Ei*EM&Q{gof=XTE(*eOpZ@ZBLdv){?GRJ;iYSzdoHOjfuz1|J0!n?rLt8ttYv9-qZfB^a0! zi>j)AOG|w#2pS}HeN`=zWA-AxQ($B@>Gwu`^ip6YO^Fovxi)nj34M%;+>3j zw{*+gkasPW{VK~!0%wC)KeB5Inl1rH>YU|@`NW1OMqEh|fWwl;)kOL*uz5!R;BO)z z60F30Ei;UZqCLy%y>%jI%f96q#Iv$?dAZCOt#-j(Xj4+p!nD0;)}*`>2fCPVpQbc!nWG*?5fCpy-dw%QnEP`M~F zE;r#}92c~<*4-D^nGPJ2Gb%JPm=6E=0v^bpxiO*MxVO6TJl+79Aso9Ea-^tEUiKE!?PZ8?bY^2fOsJ5+j&~t;~(&wr2@wQz5 zWb*}h#yIy+f1JM3_rZG=2ODjge`mSX`L{jBhMfB-N!&AZwF>wPF z*`of9NT#C0MbT{57sv|cFt(c76!S+=DC=-hh_Q+Iofwn;;U9;~qEx|%O0z2z>A?Rn zl{gV5)qi@cb%c#XVj(38{e65|o-Ek3Em^^QS+Hqal7fXu8(Fzxn^ZbeP1$S~dRoEG z3~k2m61?ZI@j#~V=UvcRUl+w{+YQJM3`xQb9OBQxvZ)vN%f)2^4cVek05W{RihFUn zOg0|%Qc_+|S1OAQuW4?_$WbY_|C?gKaF~VYyQ=!k8a=9$o^roU>N(|AjJ`3p)e}&dLLXnstW>(6+4*#=AT^BcF zC?}?inNy!#z;NL*%lx6Nm^@}yIaXNN02)7=hOz8IREfFrag@PNqx|`t{Q0r?F>1n` z8>0VumwZU{4GlSrNyzqdG0?f%r8Kfv0Gry!3IA?S@ItMTo?7VXbM*8DdipUv{hXfO zp{em#3I-_{qTv`$K_SMD_1(pS!*t>_8dF-XJ)w9NZ7~`+y0OgzqsHCxM#Z*Kjct`% z>b&}!qp__TZ}n}-#*C(^BuT@l^}|?Sb5;=RoyIc9X;vv|nyB*vq|jeMV>1y-=>|5s zQKy9RwXm@{{*y_=5drgKUSqRyCv$#dvshnK|ZL@!$-yTxlOG3=(HY5gctd9bA!NyLBIq? z5K#H!_`d_CPT~K%`0vB}|3Ck9ZZD8hsdF#U6Jl_VN?mZ#iADk9h?|qyPLCqqI;@h6 zM0!LKBJwMxr$S8hH~~)gbMUR~>V-4P*$Ie3R=t{#!Dz@8mkTR|^ znYf@AosSG7DkTxpqg;q8lu=R=A$z-?RzdI@j8i>ysSng1t+X50GgKi%ekC0(kL%4+ zM2{BH=81E?Ks8eGDruRDM(D8~5xdlI=GS9sichR0dfMC?y~s~iq$66;g|)~x0s}^z zAzO0m^#UMOgXmUF0+F#1vH6u^RmVoRM5Be}OV8bj=%UJsW-MzOUdYk1YSaM~lk#cSs8ydO|zY8I{J&s=z-F_{8FJ`qMbxYVKY`6HG$d2Nd@=XVCTEZ@6+we>8 z@94i1Kr@%H%a}*}Ue1cq_8}n$j5LWT#JK&x%p&nSk3_P3q6~Q?lI3X=DS%v-_o3x7 zPw?V+xonlT(G$Fc*#ATgqSg$O0cL7mPjHsj=?Tu(?)L<5AgS#}t;Z9bqwV$t=W2UA z!Fk$4o?w~wuqQZQd(0F3xb}o6c$3!W3Er&vJwccDq$jvQd&(1Bs2%hKE41%n$+?fY7E!4x6r$b-j%0TFQ^z<@i{u2e~5QN7-JPFU_%BirM zE2qPC6xW4(;m5hs9)5-^-weM*aeqV*8Q}?zj9kpS#zr9Kj*Tp$;7(K-f#8`J>80Sa z6g*DBS19;81>dCrf^1&oXB50d!K)Pfj)Ff?@SmQboAo`I6U-aJoH~D@{%R`3M(BzT z0fISL7LiK)>3e}}cC&cR!@jTiUg521M7AuJAGgpx?AGBy0c~{r0+JOMvSIN} z)GKxvQjJaqARSvbvwBJa8zpYI5sMkk)uSCOUqs^siipf+r2@2LG4^QWJDj59#5Azj zvZ`s%Be7Lv6ftNZ?ZPrCj*nmCFE8qtDhm~`-1t6=H*B^fvdtckmRGTPn&roV%#Mwc zwzcIL!7X*0sGT>7ibltuvr5rIAwbd2&Wm1brPB9{(xarnDBB>#4wiCsB$HTppV`)= zV^Clj15uBIjWAhlHoIQ7T&M4Gl_#0K#JM2)#U9(rxR=CSsjL^bn9AmFG|DEd61M-Z zVhePUHjW7EBqB7}ZeFY{rPn(=L9T7Xs@FQ`mB)yMzG;h&Y}#)UrdGQ zZC+Fiqzss*aZn0D*hjLFrcg+Erd#sRlFmaOLqw!J5NhM#DZ%dZKBdU(>=hB%s~y30@Z_0O4;y;0r(P34Sd6OQM1h5%a@uQtiySu)X?LR2^`6${bjix=)DB2#4!uCk{ zB-GYQ_xY-=l^wC$Bd$E4ZRA~-YK^?>GHnZ2KB2YnuFEwqSGH@NysMa2%Y&MaE1e`R z+^6m4%Kh3NuI$tv;)+jum@A6*7+1QqC%Dq1^>Jkv>$`;oN9*i3P1isQ^uJ_^CQ1^- zg(d`}zKd>`VC}NL@3J5`a1?-qLlcDl_Q)SbDrBGj{J-%tid~dAkmfjkPISXb<5)=J z=%fxgX&gUK>oZ$&m}#!>kc#!=aeaqPN+#_>uT$4}EZPNH$#P2;H2 zIPRfw{2c3B!h(=B2yr7)x81<=grtHLVK#Q43bo6rUywXISYK9da7^lfdS4&0Z0Ho8 z7h8${jbZw~I{ps>|A&G9H4N;3C70;;7$STZ5aAm`gl`NHzPpmxU8lkBs#K8Ob*f=^ zRel|3cU2aG@G1WSyQ{C82%nn>-*_T?7ZU?JN`$Wjgs*Ed5x%R4@cj{luj^_MzOE@C zd|lUofpuL=3~T`ySl4yLz<7fap40qX`B&{puKY%OiYvd>4szwV1_j(9?FU>r ztUb?_BU+FvztawL<@ef)TzOM_g)1*=$GGyA_6Aq}O*_t&KWJ}pjT=|oB zf-4`_PIG0hHo%pC*UoU|&)OhYj%n|6<<$`5%4?xquDlU)aOK%h0av~c%cJrm2%$bD zG=VG6hbD4gSE!gPKMs{}0J3)Xckvq2+iTj&qMRM@;&U#mU88np=z%DD&*nH>!H^b2tCBNhaTqlhaTfQLQn9Wp+5dV$j^Pg(39NP6MBmK zc7+ad--A$B`aT)@0r%~OI>6T(3Uc44LWj9e4ZX;HdqS^p-`>zM?)!A;4et9)=s5R1 z6ncyMJ{x+Q`#u-axbO3!6WsT3=rs533k`7JBcU_g_h@L4`yLCu&wY=F8TWl5oXdS* z3_G~*iEsh;?GG1n-krT3 zz60So-1lU7KKFe+?Bc#}ge$o3o8c<%dn&w?`@R*f=Du%-J>2)5@M`Wm7+%YL-wm(l zzNf<*x$l{9BlmqTyoLMzCEUV&&xXC+_x*4u_x&JzKllAG?Bl-Y!adygqwsF-dp^8} z`+gjLi2HsLewh1y8h(uXg5f8)?`PpY?t3BZ=e|SXC%Nx%_$luDdH5jr9SMJr`;LZx zz1tmnSJMK*HZyOBoj zI}_Q$eg73{;l8sGFZZ3pCd4-wxu5&~9`SMCKO#Nc_g-W-_q`w4!+rmZJj8t;L>}h8 zp~z!gVc4uFS&=@jWJmm5$%#D4mE6cvTp1QQ$d%!d?{Q^B;SXZ30)W)FyIel2*)>60L+QlZho<1(u*(4Ov*3qRryUHE_jIt|gW*6)ZuS29}^) zr&V#~dawlLV_G#=ro$CSnL#XJCRl<}N-SX(v4q*g5^f-ta3ffPG6yU{nFp4jlo3mq z50;?Z1eT!O43?m{z!H>lVhNRC3Cbd3301@r786TYLM-7HVhKx$B`hPBu$)-J3StS> z#1d{LmavjoLJhG553z(=VhNmB!YX13tBED7A(n6(v4q=+C9EZua0ju3JBcN%BbKn9 zSVA4^t71WDDTQ#J3ekpW?z9ojonrHG0eO~;fnUs4*7s!=g!a)WZ%k%gAUES1kTAhW zJg?s@)`9$F8HBdXwERgw<`MxZm(&PyAth0mv;Fm=&0|S!C?dIGEXfUHF-f|XVv;2F z*Q&xK=~|3Q663G61d~MiYu%!)B>%1z5w{drsqvxd#$Gob%ww&Enc>?62c~v95%Xe3elqva?l5eEsd1oP?^Vw=g&ubkc z=DkXZ4kIz&J04k)I5L_zQYNzfICH%~pT z9#hMUhH47Ym%eHi#AK@B>faE5L?!P9CgAl1y;Ia9)QsIV4ytf>%}D&@?XE%FT=~1p z^VLHlBT%CLCi+ax&|n+L@jH1}wSNvZ1A~7(>$@~72nB1cdivIcA=q6zq#nl@NQk{{ zx#}UcaA@Uf_2s9thN`FcmOFkxdimb+g5L63N6V*^j{0c%G*V|jo%I#7&FWz9_QKwE z1!|7!D(GG0I8t7eb+mjUSq8pBiq_upan|fEWIwvdK_HHnSCR7bNcmDcPN7HjSJv#+ z>d_;vtJx9P9YNc7@=&r|8h(#4eH3h`B!u0U0NF#X|N&x#s5i4c*GODMf)ySp7KA-m2YXV z`R4fl0Ov&wO3iZr2Vyp^VZn=OP{sIQdJm|T)$+M=j{fV?e+%_r2jP2;S}AAR)9+vT zx_U$;^}M-rg#IWd|252?!@E{eQ*ZT;rvUA})qgbwH&U<=UYP!6ysL)wjbgz|7{jhWo&*xkx<=& zQQ*)?ys%RP)THWyn1-me)OG>t&fC?{9fuC)X2bPjNa)*dr9%5}W&Sase*U>!dDvgg zm3{u(xzemPapjDE8(02IyAMth7<%Qbe?J24tMqz+E9bN$^zF?bC_B_<@Q6}FAfm}8_xVAP#^0X&w`g(ayq{pjLTBMf|r9| zJnJf&m4z{!#Dc|)l~oseC-hbqpLLDP@;XajdhP>SF8UbB3NGTxBZBr^LD?sINB34w z+{{W}Dt%4hLuP_iU_I-*fCaB)jMF@Ih>^Gx70qJ7Ni>NCWh$RuHn1Zb8Oo=X4eS&T z1!d*4+DDqCr?+jEn4jp{WPyYWSrAN30F}dgq_T(+;489#BLH4ii)pC-13=|W7tQJk zwvX=JSf(V)6~x`^nlC~>HKnwnc;rsv7@X-0ak>OmgKo$#0|GY1b~{!yt@rhM3Z6gLhu(n4-62^VX60y_ zm1DW0`C+@*?4QMzkbgNnfi#8u_i*JNKd4fR|FbmjzDiG^O3nV~C;@_2v;W`d=|4~+ z;6Nb!b+3vJ==`z)@3LJ%?^v})r$sfnYWaNGyCc2j^HZfs9w9p;=Tj{6xt#gOapgb! z#k}id{ux~PPd|Ldr~AvP;ap6%hglG8gUF9L@&wE~y!Xy2m_fVV+>u@Sra=g+%8u{M zH(o${%I=)vtwzRJl)7TDNVY-aRi|jq>~^OS#i!`~lRrAz#k{?q`A1SCp`?A-Kb?Yw z^tyrq*tItMVVB?RzmF>${60LfzKdCK3M#9bUiK6Cd@6NnOsXxYq_$~-$+mQQqV&|FQtAH z^TQ9DACP+$3r-cpV;(Q^j#ppUU0H;lj8-di)#@#3r6edsOs;1^7}<3y6`h)xO>I>e zSLd_0EF}`9P8h1GHs}+Lvsez-iIM~J_)_n!yZO?j{nBUAsKWN)XDi2Nkx(WO+x>&5 zNNQ-GusKVQaX!0zsmiNR)#o1rx;IwgTA6qFtk0sIZ35`lvSXE1h<7|W|HI5R37tm<7h*bCNt9Ne@TJhWOZpDtyX z-ty_uxM@b*v}jy`jDwUb$HTY1h_zo~rneR9_%gHU6G`Wl>cH|{XYaqJ@~Ha^@C(N0 zc2ujiXAD-VE*m6~Vb0L1k|Fn*;@c!J=;Hg;$vrYkR$cWY>_Vmt)_$5fKXUdN3 zaLN~T<(a9km%Z=+mB>Drb?5DO$N?-eW)JJTCp$PxAEmc3eK2=+3%S z3>jco(D~_WFhMD5wZo97<$(0MPb2zGSH+vxtw)wWyd*l;>(&q}9vgmgbVxk(1f4xj z2xCTqM8LV@5aM{MCLv~j; zl11^kkM&kgF`A@4M1!cqDwTcDai7UqR0P1i3tOlKweR;o zM?1X+B~%C1U#YLFpS&NjLyvYMluQYv5S4HCUxGk@g44BFLzti@s4GV#166|9p+}1m zCIbag`9=a23+HLkr13gnaP!yI2fs%RdgLHN!5q+@5!9XuqCG2EaE@rtt-A|KsC!2$ zO0u$|4f~t`c)4e9Mai&g1FYWDhY9eb4=!YT0VJ{N=Li$w)jIlm9KvlqN?ult-8B@?NtN6D)ni{@Mm zy8*&!Zu#em8aJ~b&hLpD2WXnLJNE8469_c)F8e@M`)*9NGex!6qS_CtRd)fZJ%j)6 zTC1KVs-7;Zepj#Bfi;S%p$gnAgjUwKfd%13EX0ce^i+r!q+lv9>7@;iIoTJALAsV> zPUTi3D3XDR+|rj+>YfRZw#~s9<@kDn^C@3f01~!oE{afe>viExb zPMR{0Qt)+w-qx((LeX<<&EGQCpRqs3N-I#`5FA&nEXb-TDg18MUJv4+G=P{oUmffo z`oIg`?I`Q4YR@fw^HlaTS-XbrFT-F@s7C1tz>2X&Sz1z*gP2$U4(-mXtPG%?U;dfO zq3K+>bl!{J5zl1J%G$kfDYU?=)osuWRdo*z?VLoj_o!Q_34vp(+t}9w$+}#Q0hlb? z;Js7~gi$38gf41UI(j>+0DBuY6rw{?7DXE;LRGi78lj9w|0Y5Thgi7y2lQ}&C8JKbyZ;Z;SznXzfdRB;IR?^I<9=d|7{we zAJPE5Mo<4nPyfl46aN3=UAX~hjv@iD!$<&I>tTU9u6&#IUBiOq7&((viiSW28mZ5v z+YdD;w?L&r>lXU+;WXy?wCTduvzqngvtR{dU%>=s$biHAS@5M}eIpspu`tz@i~Yek z7Occ`_3B-(-JherAm^RP?9Yeur%>-NLKB?dzZ8U)Z7vi|XxJ6pk=xMya@OWCaqqdC zFNk~3Ru7?R^hf&ZTu*Qj>G+5HFQEYEfN=P9Zcebun4Bk|H#z}?zT*7psyydU7fChS zS@+sv?`(ZeJO|`UEu!=L!zSY8+_8p9?zIj1!;10J(Cc3NOcu18iwaKu5JE7D`jQtd zi7h(CQgo**TIlR4fC#(S-9iL3x7wJNFOtY#Jw^RFEf5z;)4xNb^wbzB+j}wfL2YlD zO1lb_(B)F~_^At>KfTv6wH;zM!#6o1>rj$%rgySTjtg51u#!3fFk zMqF9T#GO^vsNx;7qoq_=(bOa0Kcdk(q}y_;i&jRn+$M(YRFQgzL#>%&P%5bfHW1nf zTmYdiuc>!?5ffh^p$c2-$B_qI><++>*yjUzqxWw63TZs7oAWKYQRJG22!5 z#;E`%(#u|S?)_F2UUUKxLROa?b(KI)9y;nOrXRvL5qcBjD$y6c(aY9Te?_Y?`KHCr zpy}~5=+wF(+QX=4Qjb6|9YYYn<)+|t||>Yq00XxEkG!^H~W80 z0zL%yF9tvoo(Moawl8oa1yJbh3#_4FD+Tvc0EOiK02Gq@0*}$tcL62vE9|%frvNH& zmMbUwL2ovPKhBkR!nYvkZ@_DR3o7s5hhqJGAa(sud4fyB&!8FMAcB4k4euYIeE$Fd z*7qz6E*5eX2~`*w*Za_dXM4xtd2cngmJ~x6nh1)mb0LuW`>e@C&^PM2U9oFREJ1+i zbf~MQ=!)O7uDRLXS;VtH7A+ztJtehYBxB_~^*sT^V>V**FLaq?Wul!tltHz?A>n$T ze-{OhpjYCk&JtZve1-b-H`ry4xZh>!aP^q-j{_LQ2fWUnm$5vQe|(kRT+W_@5COaY zQT)KA&Ys`G5xo1IVa}dkAg%kI9B0pDxEi1QJp$#OKEyeD3UL%@iL>Ve%)OH@i1@FG z_&c#s5KjkI@k3G(eikG0~`aarW#*jmkTJ5qV0`z>~L##BAhq_Pi@AdYgLm&aiJ&ln*((-ydWWwKl7LvdHAn^`8R!x)M*f`kZ_NZ56ri$ad~J zDqoW2drBtxI*ZVlFUuEHPufOmhg3M8e9%acL{_M(oO@ajTS=-5vXZH<3-D)Ma~$3a z;-W-+`SSMPp1c_a_AUeO;^1Anc(T|13JCA}BGcYwXY}O`y%(TL@x$3OLR5c-^1;BG zBQV-zjo$<736<2o0@yOEtSP>New3lq)eguo?7P`}k$gyd)y3psnl^P+D&;sVM z5YOG6v)InLc+mT<=KzmgiX7vO9HXfiRPA~`EUB!MhcPc}MKNfNoIRhyY*Q~&-lOH< zbtvyqYuh_f0`n}_+e`plj_%GWLpwJZU?dudVCbkM*%#H$qRgvfnWAfg$Qw&XRK$HZ z`{^voi*k(+Kq?DF!=|$R_ptzk^hW}>f(QoghFlha?QwnJJERi%1rb$PboK@Qohx60 zUIa9eln4EV5Cr?N;at?ep5pGI*MszWl3qbEBL@obbO6fdMF;KxEk3Zt6I^`2Pp`iO zyaPCR4aFyp3oc=~Ku#3fAdt?U3AB!HBD6rn!LvI2x_-jd$nFz4?T%-%PYr)2>l6$& zJ#V%Pbh{h?An+@s;Fl!^2`IWd?c_^&x|FHv23~z^pu8?@hpJT{(c{k%>(YgbAI^sZ z!E+S|Fc;?xJ$eF-c@F>0^rbQBnEjNl+(e|SeO8i5CFlLJSB!R23u|BLtzNIz9_y{% zcxtwFu39n}jik}9eZTvK^%9^tNw@5FM~pnwEw3|*(iRAGB{8pl>z``&H zOb8KRU9tLq- zfXvo$-fKe~N7+_@b!}AeHu$sXUan~6u48!r3dWG>6aRcGe+cD?CJUGjY`paqwcGt} z`cBI>5otb2Dzex%0G9Ln<2+bB4Q179ojqr>*!~FfS8?UbS_4;_U?NdA``bZf{G^~I zOLIN>zbw(dh=9z^_1d%a`XhS%H&7XWi1H0UObd*`YhVgq12@oCbtye<_XMvGs31Rq z7wGjEXixxdGn)hN5y83)PyLWDPxRw(^@x6IMn6vHZSL;`h3fw_J$;^1AE(rB&@0a5 zE$aU}QMWvzb2v!7D1r@i(*d-8^MMXZ_#`D9rG!6I!aGQaoI!J0A0vu~Aw3~Rh34hj zY-i7#kVz88CMj!nzd_Vploo+p6f{0y0<;oj#O9f@g6&mM4LLtu4fti-E1W%x@G#Uq zIMjY-FCd9Lu!QtXN3>c~Apqz?hHHW_*pNW#dtfQ(a5qgOjdJeW^a>1!)xWhfI{ibMxY?(HQ* zJBtZ>3G<-#71HGiPKLa3Yo`b0NtUZdkXP;ezOFwqpcA;kAkN@KnQJ_LVJ~)@P=t!2&co}Py(1hi6kL$atdu+4b+O_M_tnW4ll69 z{XyMcLTb&+Q1rKk1}&zHF7nB`Tu=a_7@S+C;1?)b4f1mCwT3II*Pi?db`g}g+L3^Q zWGjra#!I>8f>R+N(>T-pAW?QI1eP_ZgrcN=3LLcv&EGZT+~Yz*+4f>*&yBJfC+J=X z@}+Z-vr3VF8Yqe%rD!j_u95ybxbhvcbLIJQPA1>~B~T;(PbfG>!3hff ziz}^xTs#Fpi24GT(i3*s`vQR17g#_~@Y>lIfZ=&x06rx90uNBaXXpv8JNp7p(ksNa z&4Cvwz;<~(EM8qj;hR9K`r(mwcmD%;?SBa5tp7P8dqXI8ARqY-l+x49c#6~^^MNiR zdqgb{JW8|=irqyA$f@GMn_T${4lm~fm%Il-4l)$*AgLcF+|126z_(alA~2~L*tO5u zGXzA#HgTq*7s_55S64v;_8)l7gRKn%Rh|14f~j-2lfOp(?wz?g&RsvJAp%>LD_5@q z5(r|$ z0Nwa(F>s?7(*20a+$Ws--0%8!ybG4}N5E0tC*&v}g>?Y$CrKZnKZ`BSb9nE*$>Gg! zz|^?Jx$7IEkYK#tn?x&6slhk#*wDRx?LS2nq+DI;PyUNW)69`QMVm9s$+ZoWhOGs? zF-P#2cdh|f(7`bhnhKRHY9exoU!=rPEfc5!Ic~j4zF_Oq4Q_|fZu+D3WBv(WWBGO# zKh^j@K(G6Mh12y|etuAfdSUAIgFKvopIKJ{9n)wcf731gHr_SL5BX@b|3R!;|6zLi z6_xmawt%Cc2Mgg0@sa>MSw;osAvJ&tt|Ea|9tcw#==F0XTYU#lp&xN&Yv2X0YzzFJ zUPJWySG?lN8D(q#C_MF(#krsCqG1SUTl;UOxHY_MGz6?JM?Wasclv*Zdcy#)B>X2L zL8p)qxdu8neUUEMk;tPB?c5bLYR4ldPoTrZN23+C4W_&iDX z6&UOn8AGf~HG7vG-&;GN4<7tY22}T3L?;#%z#Rj*Ndg`q&BT0loujwB0#Rypg--S1 zNfPgVe45;kXJ86T_v5EWsP5Ae_KE`4{kB?Ngb3p6(DvvFuBzuS^wJH662Z|*V3By22{0HNAw~|G)%|t zIf4qyM8mOL8oHrEJ;?ktZOOAV;@<$qr(utB#@|Yg+B={&_V4A&Km7gVt^EvF-t)iC zyT$vb^1_>rJ#f=2ZV;7tlYp}-Y`gAAv;UXwZ8gGPN zm4rw6Pfr!OC5~xX!a-LoOT+jF&ti0f+}}_5Z{}T2))!*I6{dc@-O+vIeJG+k9hkTJ zhT}MG-Ww4aqwR!xydpLRmZa*~ckD?f?zl2GMs-b$&mkHLQS|lLdLDSpc&l~vE*u1a z)pOM0BC81C3!Wc!_J-2Eci|aJ`bbOqfUb^*-StAQe9eCirjfsl0$3#Wk+`>ycE0;S zlNCQX2R`B71J5V_mk3W^Bafz^(9^NT_&qtD>}jS5<$d1Py{)<-pY1!Kn0q>NP z23~yD>}TS#Qw=W63o#IQX;9X$9U_J;9^R8DE^sfD{PSU5%R@H69$jTqtzCh@56N{i zPAI_?4L&y6{~1qkh5wsSd1??^tNn*?8=C+3^a?gqP3J2%`62oSSYJI0;sggdU3G7t z!gh?r{FKbcaHyy%E1$CC0^?0Rw!}zpn*s;ZNs#??7Y*p@eeGjW-gMc}bBeWCf@49r z*GPI&OeRkXxT0L`chL-~runl5udMHvS;3Xm96Hr-+BCzf4?PblVd_KV2{5(B)9WcI zd=rMf->ogl{o>j33t_+{TWR-p4`ir2@$@^`B9CACJf6Pz$G5-a>{*f!*EaHKu28X` z8i5BSi!#6alK@%z7qbt8E8u!Dc@fU(nEg~;b0!T7qKG<@K{NzRGdXqsd`(f97)>S|;zgx~a=^Gm)c%B=p~1cIJ>e50r;SH757 zY;{zwaPF$aw!oMGF@w@ui-C z8K9MP+38C1&v@7mrP4m_U5fiVf&eJ%z7VGN$^f|3zQD(M*P6gGdcqcPe}KqK;2z$! zI<$wfd=zGqL?U#R=6K4Y;eJW2zpy zf2xMgs@7=56ku0-W2&>N3+SwB%5MwYfCJ54vvHug>oy!{Mu0u}ZCWeu@@U<>YZgu> zcX?Rfd=~WRM^|H8c7DfJJ=z#*>p0$3%KE;*g0(t{AFSb}FZC{azZY)C$4RE#4U|Zk zaxaKs`ykE|i782GcSqAIWh}9J{gj+3?zAEoBD;grU`-HZb)n}SGs|a?1>j>72XJl? z2N&7?Ik+0k6Rh=L%e!vZX3`T-xE7qOYmT;>UK@DV9q{<-s@1T&sPzL=YjN6w1d~!0 z-*!0n9a71PjSBPWl%n;4jlUS`2n28`pD^Q+R9 ztA3!aDmE70s)^>4;f1KKDm3HfsO57=e1_q@z>J)4Jn7QMG2?yJIQ_}_?5ZLoa#2Zd zd5OBJDw?!3`e-@`OGa@jdU{Rv4WCMYMkUW*NGE3@#})gp$K`l*hW1KwtSs?kyEWNg zhfDA1{K}Wwj3f0FG{_EFM<-)dslu`=Bk-;ef5z0L_@+m5MH zEQL9!^YOPI$Br%rlC(*l;2I6P)ir(`Z{O^%<4S{H@dQ`=)A9ZTeEj0ohf%mksT%Jx7WuI6KuA=(|02fJ|A1ShN@}fd@GGpMupn`W}@v zj+gzxxrY=XNEUfbacS^~s}uIK+QtbOA>&xA%ma`1PeGJt-ibyjb#U*pA(;KtPwqj& z(4)H%PW{9Eqswb`8Ep;D+`K>AaHv89eWs}y7t8@L>m~AGxZnGa6 z&NYO2xD_FwBh$C}|BGI6yF+6j7f%6P6#&}wX%@U)(3S4P#|=MJ!?}F#L6WBS-e2Mf z;7>T0>eF7dT5feVXCna{47t)}!e*)Jf&&>6C=fnYB5lZ9l1u-iG2Me%mjqqiho?x= z09V=zsG-v+0AD{rhpk7NN83*LSMshxBDQ1vxM2xbD14Iz*CzG=QcCw=_5=CdgCE>K z90Fi2Jd}c`I`wA8_pBTK*whQyUt)V9+WSAyJvt@eUrfPr>Yj(XcNe`vX??=qOx*+X z7~|h3x(CnDJ0LMu7sE3Y&v(-E6#2Z4o~!V@HVc{`xi@-Ct|r<8Q?H#p^jW0XZV?G8 zj>f5jyUw;3t=jv2G1NOw(@3v7`7q}6-rCdBA$R)ON;qB>L7(tRaSG=+v@@sW;HyVt z6)zK@n!?`IP$#7stLPCzN&iq?$bLtBkJ`n>$neIT23n$0_0d6~qs8knGBvI&6t;zLe4 zxqc~)&y}=ynn6z(q%r`dfyo%w05)GI z;Lg`|0qd*F3D)UDb{a!Q!p>>PJ9Je5j^5WcRsky}ts+CcgJLJe#;Psw#2Zw{ttJFk zkApV&YFJBQl1k-{InxA4b=ZBImJofHe+=d=9bGirjmb&q@ z&2lSH4vSTePrR&tLay0EKokA#AC^Vhk2-BS1vndB=U>IU=3;w^0P4mk{eF7-4vC+T z9_RXBA{q2JJ^h))&%fa*kOQGJZ~;AC22nFmN>AXw?+0M9oEzXc)f%X$C+v>y41gl6 z4?IY(pXFWa0$=3HKLg*O*MH&42Z6)%^lPpR1vGm4Psp@^5Af7K65?z>yoTrYLlnNQ zAN$z3{Z){2`>DD8pfz**aU;sy{s09(qTp2uA_&58=%_m|7NqAu3HW&go*8uqaB=b4 z1LeG{?!aQf;}^2vU2>;3X4E|MI;898%^-3?e?Y1D8=K^Fg_HoYt>1)umxfDF$~3U# zGS*kif*S-4gEr9j30(VfB_4)a@NOnp^d=1><8ss*1F!sS3&!9&g`f)Kyc=J+yl z2!+!|0aps~zD|IK&lLpV(^0uybrl2F)Z5@225!NR@`p6DV` zE$R}@8JJq608Uml;*~sKQ!_x@XHLtsJO9N=Zr zLOK?$u5>^>1-)f_ma@*G9YqxR`c&Q1dr+PKQ12asaH~Doy#q&t$O{~zu8};@J^!J+ z2Zp1S44fQE%Lb=b$sP}=xx0S^cSPJF@}8)1;F{b|fpiRExyrpYwz^R82|X}I<8wK| zO?t(2_l^P7+`H^dZ|&O<8Hf>n0#jD9N4huXt?nIfL$7xcBE{2}-9z0k4Lt|) zK?et=X{p!!hPtc(Brzri_OitooJ4LX zTyx+$ZjKfKfna7h;-8C!5SIkeI4+{Pm|7-nO?s&Tlmf!PNqsy~I;0xa++eL{&LkQP z8p-jsIzywg+Ifr2H-LeQsO&8a$~*H--&`-;dZO_o(I}c=?J8V3dTs?KRM@56@{F`V zllJ(zp>Uq zopvTKsn+(7HUqyXdB_yXd=Aclp7b?(*Xr!hQZXDL7351mTvz)p!cv zIer06U$A0K9ANAT?$;nE}Z}PT)`)ARC z|LFY(q|AZw9BZFKtftYhk;wuAp>&A9{T0&A^&wrD_|WQyg^EtQ3g3#+uxo836W>0% zkmc)e0?H0H>9mZFCX<*yAjEui0LP+5iUb#;G39`rr#`PjVEsbB{7MHkgo9IVCZw${K}EE=>|wWQ|iRrzJ*BPmG$C5Y>T8 zWN_%!GAf80nItYRNnE})&Rn>6VWwc=ikWE9Qk#-5ty5fq)8xN_cg@39KnURa@(rjK z@C5O69_;oA`Y8Aw1;;2jPS>=Ypr?0v7o)rNHU@_CuKECMRn5_og5@h3h{91e($|MN{?7skoL^cnEB>s#vD*zOA% z``QJGiEQ_H=3`?k>bJBsHWgRbwc)F##eC+h>!-8bAfWHTQ_IEJ?n{{Oy}ZQI*EipV zp6?!qR0rR%<$8Rr+S}T^rA?0q==U7*JL%WgE|!gvB__nAAVaOU$t)y*>4jp82wlW73o#_&WD$dZ&=y*<&Nv*RgHEFw3W0Ob&X= zWy9f`FXP4*iKoTG**FX-;_x(n0pr>=EVy;oi>GN?hop2JF$W%ZOd)^E65e2_@Ob#16_ zt#50?hji&9MJ#(HJ2;Qc&ai0hwiavQ%=He$`BT$CPDVD)BCI&q|%Uy1A{1zQ4?}irGQJI~ABYCrN?o^14=h`?zik%NoZH zQcIF#a_ez)kFdM7wYk;BvO3wp>1?WO_YIkpVOjIp!I|tjStMCoJmPC`8$b%SEZ*j= zZ|$H~m9T>|*fd$_#w7J%i(jJ`+0a_QwH_aC%bH||rOM{kt#w|vcf*3#*1G#x)=jeO zsRuz8&~uj&_^V};W+rRWswQt^Oe`4wsu>5=`rl!jcjJ7 zrI*oH0QEMb-i$lEsNSnn54~{y`4!elUbOIhc4KC3M$IPCixxkhhU>H|TKHN_yd+bu zl1NI^C+dg@>6tOC)39oiWNrcnZn9=~usxGlv0MPNC1ZEtr=57d%iX!5UVLC4V#G)= zZFYsw?(I3v7E>myq9l1;E9;ut@R4S-Fg2`YW~pAlW5#3IHn(m>$5MkTxoe}hRd#H8 znruM$!n(G46S6EZB~1xWGd{YCM#`L*q{(S?{VLh@OWDN&Su^Gu{UIsN00_bR*H?P4)07k*K~#CXZpGMe-bzf#~Vi z%JgyU0$Illy*;Z4r+S{LGS3z4GLa`b2I5Dk9c5 zY)RFkm=Hh^w1H$Xu>=Cy3}{hYl7%dW?7G>Fp@Qz^rG;mcYFo8FEwpL7NqkzLr>WHz zQ`^Q$6|1eZ(iW?2X}g*W{7ppOc(}gnFgBr6r+5{mGF1?GUd?uotYwga$>oEf@$v4OOEeCEr5N zT3li-u4uOAyEGXdWTxL8Y-|=yYsXBHRG+%()ZN;Io7nYSDJ@>&xw{4^(M9dD#g`|; zS%fJ+F^j?PE-^{3bvJsNbZTd4WR?;cn)@LGjg&f4P`<>-t7DNkgx;5&Y#F*28fi&< zM~fFBSLO@O>PD0zS~^{^V$nqles<1Q_--wIb@F}@y$H*xkH7WN-+b&QeBW*P4t4zf zwB*9SLGha{V%=x6K>*ClD=0YQj4@-2#0RnH%g-+?R3B&+mzQ4vH9o&k* zj`y=cEU^f$yke_zNMFJ*s@w;RXi>;q$k$!M2G7)UJM`R2ITRI>6~-)ZrU*a`^ECmR zc&4cw;+1lZ6JW>19Q;G9dox{FX6|(FToO!bAUM^b}640o5iQG?uQs%R!FFx zJ>F$imQj=ZM`J+!%P(B)SsRq~QJMS#mygc8;vWVA%RghecU`ldjt*q`V;1^?K_Bid zSO4?4nJ%76|oS9JN8G7i((4SNm1Ix zXjhdMx&2FMkuVY+eP(6-s3@&#!d3_5*AWCadV^BV1#Q}G#;DQwq zy&1eCa5&4NdB(6J2qI|kIFzjP*_{@$wp-KPKjPyO#f{VynNA-&X%J|r+12Zcqa5CjPR;dUPs zqJM?;o|d(^xmx$bF1Bj^8S|P@_4P>(IrRmBT~2e6moev60`W7A6T}6F zy7uV&SQGtfpp}X3Lns%=o&^KOUZh{I!|gW)7m10nx9}@oW?j0A#pTQfv zJ0`||BEhk4@z$#p&4uxiP;@qWSH7%x_n-ZkVod4N-W`7{_M@ zu~jr#+&tv?7At)B>iodis|$h!<%7?PQ1H0A<3ie_W+I``OhB3UiG;$FA}QZYnjr3) zK^e!l!qk<*ks&t5HHgTKNP}XLCJkB>(k@Axc7{lUkvvVgv7$AXr8mS+$(i^TVAf47>y|HA0+6PEdj7CU$?4rS<{6U^_yfE89kQle#k z5&_dK@b%3tP|~g7^n^;^h7Fa$K#QJ~X=^|rMq36o8`drMwkf;4k^QEC8ygahsvW1l zDPY|$0BTLXyA`Z|Z5LnyG`9;f7$*f!RJazlcv_okv5ppY;#RgU(d;90E%5tWHdY1P z>uNT**NOFKN4YXyl*p|mWA!&RSXNe3#8n47ZqZDjVq52y&dV?F>ewZuofVnco zASNqZ8ffwa$RkoveK;My^)TiHLT$}U1KxFBVbw~(qSD>k z?r!sXS>C0r-^nU6fRWj9f^!?|U&U6WgHze!^-Dlvb)QTSnUubj{@K;bZ(6c?HOpJc z`WxBp>A*u{kd`R)(A|n?{#n&km8(~G%qZ1K*U;*1L^sQvfpioNffyC6e=&2WtEIl# z7YKS!tVIU7)l^Rq*_j^*8zJ;xlD2lIb6a?E9U*xt*G$;{GHV4`7HATyMi zrDZH{8S4+Tjp>NG0NcQBG3Es;N+t=GrKcahs!=Yt7^R6aX_q5ekcgh&3UYmzU7rpG zu2RCc?=pt3A%s3 zi`Ay9c9FNGg@hmtF?TCA28FfKi+^c{Q*f6051lpyCd}5c#A46ebSzlx!`ZtYEaqnY zYuIh+&?j0M^4pIsMo48$&MlanD_oT!ob%iul4-W~W|Ws9uaD*3EVl>K3~T7`zEGQ| zG8Aa@1vrfO*m|Yj!Ddkj>%W;*ro*frJ_#P9Zpz*PV?W`i{0%BxMzUb96BsAuY@7Z^rjT}uODeH;8(__qct7W>>yY8Psa zGH0gAx5O9puJtw=8#4YGYEvaOb86C3zb7DyvISY{SnhGl4RJYgX6SQ-Q30i6AHguv z8wqA}Fl#Mc(}t~IsSkLWCrf>L&H#2#B?&sb9kW!esT#x#wE>{&tx*6uUB0?J`rxXW**YsuIj%5|prQ;!V_+z7$ZHl~Rr@rMAUZ7{%14 zY<%0OxW{HJ#N2eseCA}1#D`c#55u0Sp7z7`iXHDJ!IeAP- z{O;^v7gl7LYAV#;(sjbh*!e-zJtxcDsS3@Jg?8oNB;aS7YAY7BwRzXIx_okA&Q9je zI!~Yt5{A5GZq5z}8;Ral4~1v&1cF|vo%jv)$`~6^V<6h0<7a6@MU{_;Mkr-h1+K{s zhCKF!zksd;VEWHiD@{AJbd;n}^MipoINjskz^bwX5t7UviVZ4L*EGuEm`xV5E3>0w z*}$}+w`2l)7OWCjwIzEG>)!W*+ghQc9eYde7b*h$sH}C! z>;ic+r@ySUx^BT@r_1GBz1rU%EN7DvOX=1XLvq#1p8ZJvl=S%wHYdk~g!&aEBDb)W zNJ^L+@nW$id{!t0{bugpizH=J4Yvn&W~I-+Q3!Ze47-#k=#f?^Hr_M|!K;X!jaO+jTpFPF zF^lVhzN$KQzJy1}EnnAa%o*dLVy=N~#W#Y~f@HGk(Ce8bCtaS7;0ks=NJUT14wBY_ zGUYQ?U@@k*LRQ#Dyp@_wbaYmmP3kI>c!^D*4hjVVs8Vk$Z8pir6~I=`p=k|^`RnQRJw!Zgv;pxfjF6w z00#UHqOA)GFQg2xyzDZgOx4C#;X9B}u009pqJVFMLD~#J*RVvIpgpJGoMd{tgG&fV zJIo1IS}lHW1iv@|h-h}D>d|yGF8X~Ra z9pM&*)dI;e_-z;)Az}d`H=wcixMqWJ1$4Ith3ORUP{`}xPuwEj$FJE?3p0}^Al@!W z%DteqNvH~m{td#2WO|Y0kq1W}j+f^R;-k1O^q>kehP$qR6*)%d@fH9rc^+^3n~rU7 z17~&Cs_sj8VRdvlgnT+o@8TB)Rrv{CaFSBINA?ZW0ggJ|hV!mx;if{mMu?rNo5*pB zi^8+gW#4$8pg?So0L1p!$h=EcYUG$4KZy5460B(`DN%9oG1n!2XSsyVo|JP5yA;zJ-oDW@~j$d zQB70S(2t28)`L&7@tY5AU0vL`;QQ~pN|2Tk!1;N^It32>Mx%xI_&Ivryl28Ru~vqRj7^$=Bi9#dSRkd9(1i0;7oYn?>{f za__jE<092XJ8v#JGIr;T2t7pn#gXblB%QVM=0XIs-?^~(_Gfnn@K`7wS@#EopCQg* zM&732IP2L8b+xUe2hO@;C;d!@m}E5wSGq#nTMvIgl%Oke^+FWzRX?XFi$z{CLiMlD zLkuBYNho6o$z%NU) zUtc0|OpnimXwGj(j)}lTs7)SEm9@Df^WhM6aNPR574`Mvy*?q2{I+$kezh#>zXWxC z0Cir)Z^hBok46{Z{U*K&IB;>-Bz!F?pc*LmDh05M6jba)VBN|>{6WCfqVY7a5lUG{ zMEzYM>IPNi1@Yq%Pndhb3l#eQHo(mzmrXGU57EI`9P&RTPWYcdXjdAC6wlQNfFodm zW4CTTjG#RTZNu&xkE+GzHz-=>*3HE%bTI|-d5R>;z#Ply!x8A6&f@Yn>Ef{CbcB8L z;d$@gJGX1qb$LHMLPZb;S`J$=7z9w1DR36c^X~JU$L}kQ{Ow3VJn)G*IkeBU^Q+>sA-QAnbnh*$)wnn=STq(4PCiekn%`+yi{ zhr~yHd{GQt83e4qFrSgPBDHV2_zw@e7qHkx@M=Zpd7~A)C5DsGMF_*TIvN3`AL6%1 zU*sEbV@wO8ncNwB2Z=rTyfyBihYNTMViRF>%{Clj&&j`jhEO*F`eyvlzcezHK9gKM zSGWkB87t;%V-xW+jjv_hd4+=zL+IIMPm${(jhLFy+n=aCNgVVS3UDOk>@%oG^{?zm zoE0{X0P6vI5CA#P(K!!UapZa07)V5l)O4VZg463k;=Tu(vB*ERpSl;pjShJBU09e)@ zc6{4)^ShE$8up{paf^Ic;STXZ)b}hVk58^fSCyYqf|I_~q9VF<8u!sqD02im_~Ib$ z)H{R*%Cez-0Cp|~M z67D1Sept;t;-q{2CS2l%yRK{Z(lweoaU1rg0KNBI9C6PJy>bK$thzw5u9RJo{av+# zMpDC|YMV6=4I)XI{VS&-|I@~M10IieRrTx1&f{A*^`B&+c}tGWSwhX8+27^dla45R z1iem}1i1;sy#JdbmO{~~QlvH?BOY-UnOP{ueaP{~t3>nXs%?6=R`l)@slEF|rry^w{*;b*Z@v4dwRay)0h&wiM$O)e=F+>}W_~>Cy}SEmMj@xi z;R4haOer%krFf$rzKpK3Iy2hM{n0qTEBYo6^qkFG;@2Q2u3MPiQ^VWno`tc9qZGuE za&ONU__aNc(TBZw`5C_k5#X=w`6G(GiRV+?i(v4jy_fP0Lkj_-_a;8Q_g3CEgy;|B zdN=d7-e>t;y$5)p_b6`}J_{fFCh+NfSMWBvqHk>9N*?TMXoM{fS@8y5C`g<4%(31(#X*R&eI7bbY;x`kGk!z40wgD^e(kNTv}6 zfwuD9aiOy!ui*MVDKU+SV;{J`%eiZS0uGC@2We*s=|MO^UdtqXV%e`xEF`0unp?oq zY)aBQ>69YiC*vDruYQnEkIv_9vE`sC?$K_G-i7Xsa9`{Z`q0BeF@*3x6ZZnNBOpj9 z`W($MGsVdW(jyS!69JaanD$~#f5z4yT(P#oReMOWm9i>`OVg{SQaFvOsnD_+MMZq-w?@W( zBja;h_pVe?tQFa4s;ptxvsLUS=4CTwCbs1l_;558_^}XPB&{u?bIT-jxE*0c9*c|<@^q)%I=tp&bdJ4DMm<|yXjtv zVhT%BjIcC!v+i~_h+`&TIlf}&b31>D3DD7c%;A31vF1$-AV=$Ahx<>CHGkR@22e-q zA%{EWSQB&ndC+mk7{^`viyVJ`!EuMf;rprMuAde;-hRgM^XDAz{6M6sUAFxOeQ<=L)wzUsw~`yZUw!thF1*6fh7i6`yWpzGDvWn~wEw=2Gi-s8$UB?jmv6 z#c}tuto!3^5XVL=2#loVz7r1bDaZO#j-S8lxa*ga63=I+#1o1VT%2kFB97Kq9q!j0 zYhHVbTv(evF2xvw_ibEHpdIkyZ{ot;P`XQamP!JpTFp6WsW=cI)45G$2-pm zf)kzD*1^J?LowOd9r-v9LM`07SKQq7gN z@e8qTDEJxvLzuea<%r+4JW_jLTJ1}8k*ExGg9uK&0@NE3pqLYO>jidACU&=s0(NOJ z`>Mq3FCb=4n0W+dt4+*Y*)Thim2Mfy^>u;Nk=3a^hd!$^>z-?=JF!b%iu|bH1^o96 zICB|w$d4ijW+=@g=#L+X3iQbnqilB@LtxnDQQTZm99>VppyZqteVTs#+BGyCLkz_|bCJ-4%xCr7?HWA0CyZ3OQmVM;%k=9NN;r;Ry~VD<3BBhdm4ZSR_b#Px zxH9LQ-gbNw=8W^y+_hi(Bi4nN1<$xSIOfQiTlbxMb`SdGl!%TK&EvR9>bGnVhnB=} zp#Z^j8`SG!n7m3CsYtU>SM5>rA}Gbc6g*8d7`#lY@|OTut=ez{tSEV<{@0H%cQST( z^Az?H8A`;x*(%#qJzFU(QIvK#5;`>PIjk12Ab)+@^L0Q=UpUD5+)!>_L7m3ZSsxh6 z`Xu`SExndSOZZ0+AG;$uhj+y2YzQ>Y#R&0P6a6Y*6ZB+hVai*-QbE$4r&nXdIpr;i;C!qu z^76D7BfltUm}0EDCnVsOnA6h4;)HAxtHdT0nN8?`(j0kinsbU#rv=h!qiz9`8EYd` zI%4ki&TeL%eRss%`5$EdcUBGG?zXPXYng8t=d5U!OFv!QO(X+;ZG zcpBDh_XY~BzYG_7EQ{WWpDz4-7eBw|%X-G*XF7iB@Y8{xNAQERo;Y9Di?rT(__-ZF zoAC2x{QM9!8XVy9GILIo6k4LD)@(RVDerZzP}HL@55o7m<($;eqVv( z_j7Ul{`%M*d;zZBxh&Sqr^H(LYzUWVRn*RbiDm)3N z*XOYAwc>cAm`9(cc~tC_bUB}fU9$?c35I>%`7}Kniu52BU^|_A4Mlt3&do1j;zL;3 z=bbo4LdJ}zr52#GQW2^?m+l-1Z;4*Xt1!pcAh6*q*1c4mphWGop;^<=oQ#RMD%~OgC*8~IPq14SN^Vv4sfYPnBCVSb@NGQlUH{h7O=Y- z_7R>F>2LaD?HKNCwy?NXM4UU+If!s8wmTb*d3crf%uQK$HYB&aWX3uV*S zPmpEx`$M6&s$EagHDxWC$|e#ZmiT&zy5|uKJx&UG;I=@}Bc{_MmWCeko1%xc)FHjp zp){rBH(BaV=ZPkalGo_}Xzi`y z*3>F=2|$VJ1t{5V*6ccJ03~}eAuRz^?V(6ROsyS^Rl^rSDdqpP6 zxr$3vrc<~oqxPU<`%iFvhEd>8qJSJ)dd6e4Kldz+t4WpO)cocAI_!0RIEI@-EgM&9sdTe?fDj85+B6hAL8$R0O|QFU)nncKUd;sK3_TnYx+en zg)i&fi4RZW?=yU9-z5CF@zc($hqod_UxbIZ_I-wa{X1_Sew04-)7u{U^*wxgl|KEB z-iB%A1Y0hqc3cby!ARy~?8%W87Q=U;ATx=`H}NG;+_@t}WU&;HlekA5D91tcbdgG`*KT}n>?+a0G;&K16`@I zgiujJYpcbnBfZ*7Qj+0H6WY+KieHwR4wuY@J_O0sAy7=)#0rG-sh(t7 z4NW(YN9E-9iBlvBfrNwfIiIn&R;xR<>j<4I(8Y;)iU_1CG0w|_?AFM;#&_jjoRMlv zMf5!dFAY^&+;CY_=@PCt5JWIG$$}R4kbbPwbw5_U#($l*pKLvC|9<$fCY+0uqpX6? zJI}#UV{OS9tbahU%j;d_@+<=l(T*qkjBbiGcU;?6)<3N(p-|^SV5<|=B{k+~%+5>*wvoBC z?RZ4#m8@PiNS!Ut&>;Kq=n^OP7KK_HX+$=$e$JNK^ieKAjTvWZ39yE}s~c zH5>dbG|VXu5>9ad{>ssTkICBPUvoCOg)JW)_&WYNWFb$9jdP-no$8XL35;R=4Xn-v zv@Ji7_Tu)t8xh1vM;bSri~?L7z_Y?dM^0z`B%-pD!WLhGt4>h^=u+0dj4d81P!NIc z#n3tK3fAvsO(TT~L=Bi78D=Y%SIhdBm?1je;~>N_aG#8M!189X{(81#q{tJ%N(#8E zljK9Je<#~AQm6>dAkH=mptDr`th9|rBow9GTjL5r96SPXu-**XleTn;%a)I*ZxFyH zB<9x4{nNtwZ-Gc3A=9SUVljYsvCobawp8wjq?uar9w~hRse(6;6g|s9{k*$b|9`Q* zQG?hqSFuoP1eg)4+RziUFN4)S%N`gtg#9_L*lZ?AV742WjY{VjcZ`tWSj*faZL7Gu zNiw5SLlZ1L5ZTv_ZbgXrrjJh7a!;EyZ-vVmN982dJ%Q zjUz=3x0UE;<-M7D3S;y$7^4Ap=jc>s@P^l)sY+rF?XRfxE9?uSUFm4ENQm|CW{-?I zmZQceuwajZT2|+l3KVRDce4KPuqQ^^UkQm4M_Ms>MN^Way(RO{H9kz&8x zvq87sWMW~KcNy#7#_k)Pnw0vTsW$UYC>g)CN(5TNPN_hHtgItd`%G<@<>8u!l`t@l z3P@8L(3o$8-JyBpQq9>B47g?F>P!?9t=P`OBW;CV)d)K1s!>_y;F7IoHR&Qc4sqYI zk*mDQMoAF0fzi>_7I_10Y6}3IGz!9LF`O((QG;+AH!8}c5ncumv33}R@q^7IL~YZN zMBj|Us+@L)-QHFiK{Dg29O#e2fD#v~E?t{#X~yyp41N>_lwQ>c22}m1XtBgpIW-%L zfKjm9Ecdh{=$Fu$!E}g{JPPBY9{ZO%%R$I|jrERH*eQVj?ByzKG(NU|q}VRRwVHvA z^@xDjC}M79nhMA?JBRiEnC<(&SK~jMf;UhI^$~9jP%K%xy|GOQ8So_xs-wW`iI(LW z*1y8mW^HQ(OJ@yt$om>bsHa%>$@<-F)hHvFLf&j(GKCm+<&>(tZNAoQJD*rFVF$r}`NZECX&pPfxQFNEwIkq$<}7;=A0Na$KHkK zQ!egiInBl5LZ*7#?e)`|PcUZpqd#-?wL2ZyqGLarn|Ivm)VZK^D#d}$I2W)y>;-4> ze4bT0Qy^e#-7ZAfofd(UDT6j_9UMawn9`MMMh`Mp9W7t7A8m=R5Qd9bAsXJ>+K6|3tP z1lGaF81c$O!KG`34ORsezZb{n5m5PZcBw!ovt?Po?c+W=l*>BL&AT_VTv)x>=eslH z=Rr5N+~`~r3HF&OPM=0YUHX$Qtc;sa{Kl66LC4`d5esjX6zWaB z;m|0h!=h6g>Kl;NukO;n$l4bqBCK+|QXq(Bs#}~pTF`>KO{lLW2;4_u^>R1d|0^mN zt*)%+2G$>!eDHomB+p!c<5-4K;uEsOm8`_3#FC{;7v5ZNl-rR@O?3RlD6vzPxQtya zc{IH`Yh?d7*#S4;GCUe#@6Cxs^=3o@qdRf3h>;3iSvGL!l*K-qXWW#8{rwSMVr1MD zWq_&*`St51sk1X2AMu(85YPgN108siVc|0r@^_S}eL5F+)5W0Jq`g#;z<`6H>hb{{ zs@^Vry%|>BDDRS95AqtKikdd#eu{Q{2?> zh%g63j~op1%|V+kyD=Ku**W5tA92LEqP`K6eycIC`lR?_1@g8d+TsiNTH4g*p!$17 z9h4IN@*KiczXq6Y_Qdr?W+9vsRwZ)3;Vc`Fru8_i2aN&B1l`X=b*H^|6)x_XF(GwC z;V7`5QI?olPfQP4-sq&tSGX)~guq!43i{MFRskT_uvTI3}T0~}fxZ5m(d3<&fg+xAaS5O<~fW$;AMa0=kp#Vxp zFL@1QHmajFT227#d~LW`PTlC3p$omi8T4P;u0*qvE|}F3#jQxJ^ZEjL5n9@=tMZ^T zd>c&?(X}`ksi5ahCTlyp%D#-+v%fD(R$t?t%RT-UccZ5Yk8*fqBQaJnnQ+C$?Q;{; z1WnK>xM(-i*rpvRF(%DB(%V@U>KXaf7;eXPOL%&j4;wU$LYLx2WTq_e>30j^Gp7E=Z+xaV2gR2zQ0R8-i%m1?_BM=IWsQVF>99 zqb!}0jkgRK3*jKQE`Z}4!pXyjvt}8QG|E)@LRb$k46R-33CKHneK6EzF4v5c46-Y- zu@?rkEH5 zvelJdS;lEiYr9)s-7CBMgPGfnF-p^>%$H?eK=&-9eiX=xGniLo28zs!e2uDMSomh* zZAl1Od2;)8>Q=~`?_9o6W3zrFRpzfw0;}@VCUILZ6cAn`fsS%!`SoZeRCQpsa!4;hL#GDIXC$^y7-ryT=WX{WOuV5+>@?BunI(bQ%70TY zE-pMb5lk&E>lVco5V9sD5LLAvVumgylrvB}w6BcWoULSc(!H-(=a_=z4z++>)Z)Y4 z-L2x*xOV+QNE72U8A;~XdxIXK^vV9p1RgZBDk#1bS*XAxBVEW_>uVBSYgoOET{e}P zvDJWJX|<2d%|IRGv%7#hS>v)GDW%3jZVj7FObWm%=BOv|c5npZ#-akr!{ zNj{XJS@P1`pvTG}&cwX9qYP&hRHb6ZP)4#q>YeUJI?Gh0F{`fdHpx5w+HvJ3@uZ3m zL{T1m0oyxEldlO~1d&e*6V*@cEK?38zT4UR6DxKp7}Xx{x@KJjvd$l|%QNJw4{lWV zuF8qbXoE|?ofV8jK#Ae~Wnx^LStx<72u`#!iWo-~E;ZfBrBsD+CPhh_1RDkMcP>zj z1=R24V(cwk)SXj7RKLV?_hRVva_FM9xztCpM?at=%W<1>%iVV`+!)ktb4H`fmEp34 zOj(j5uBn(!yt8M`DxZZD-WYsY@kxYhkRdL~@EDtaTIG|eal>hqP9e(G(pH{Zydnel z!xHz)3N|Fxu7_6D>7f$~t7o|yUP1=U7K(609fA>@KsW3Hh&8j=DM)V)V>?f$I#Or6 zjYf4HS?bFoOfuLj;og*peTEQE;3Ha)i9O=#}o4#!w5~4a~+G zvJIBc1kt981>Dq~h`Ii@1Tz`@E7Fhg9jA*Ak^P^7Nkgufh)pDmbvkRiacDk~D4Dtw z%5Uzs$Ft6}3zDQx#t~AbU()Ps^5SH^klEQfifzsnVTu?;eOs+7)>-KgNRC{_T`r%m zCFu2&{9Oo}#+_o`!J&*C>7}aI!Pqt|XAAb4G+{LD#_RE4OSjU}%`G|BPe!`R=1{AMx8a|myjhH8 z9(cv$-r&tT=)AOnVAXpwBD>tv3X|Z{#m*{s&@Bd7N7;;^d)=a?%WD_7oUY1%c!iXz zbyn3h)cW94l^bL%a4MUC2pyAtN!Qp$#^H#Vsd{S758bBM2ncuo)?ojX>~Y z%dIkF*9p+Dqf1*{Mn@PswaIQU8Hqi0jS*vES&j%`MIoptM?>aW5qGSAJV%g(cPQu) zYKO6$%^>Eo5z(7L9i_d8hjPRPgN-B)Ei3F==hB2Q(iVyeF(;wE3O-GC}B~+w;^ZX$%}2*LU)@-uZ?`O zDunT_(Jd__KITnAB_(@G(>e^-qmrtPt&oC^!c%N&Fk72TygoL^wko+0Y;DENgX2kU zQp?8_nygi(BH8YRSDY}03H6Qm*q03-Y(^#uI#Cv4ABRb>_b@GGqDiT;rN!5XWu-9x zwz^wLbT7c9Ks7^Ji7goGAJWymZ~+hGYAQjl-Kk;{Zp=gX~(%d^2>>27S+ZE-X0>X8!x z0^%=SyS5Gc6U4cQR%?SD&F718+P0<2D>mL*8%2HDSRVVY$!qwOMiW@&vPpUaHWqFB zk9AHgNS?}50ApkXJ?KRD2B9znvN{7?W7m2CZH3y_?y$*TwH-M2e4_Fz!%kCF)^l{;Zo#yarkmfTW9B+`49+nSt3&xwa}5 z_JWocn&0&81z+xfg32Fav1>W?dnRglvGPjR#LUfw_fjGF|GiiEnOs{)CK|ExvY@p| zj%_i9>{#6N@=luZ-~*AsEF6CKtsdg160w23F;~PWptLER2%i~q6G2-97{RxdZ#U*z zHJrCF4;Zml1k+#^`J7#O--4!gH^fep^h~F9tLVWbB(&H1 zvU>Fl%Vz4Vk`&8c%CPe-Sc@S~1|3nK?BCO0)2ClevrYD-q3X#O&l!OB^&YDqdF>-< ze1_T=<<@q)nlU;2Xd`B% zHlS?nV5tm|KN*!=w5`-h54AhnlG20ms4o7xfMGE@+gFZ z>321^gavY}uxd@qsMX0j_ZHZ#46zR>8fv;wd)bF`#8s@Vycn7rEoSKs*hX^cDsfug zZj1%XyyUiN$N6FfMK+72Yu4*d@a+ulu(_2Zw=Q|zgp_efZZat*2Lzf%k##zoWspKS z$X0@44MMiU7GXmj5biNT_qU^<7v2T?YylH0OA+l#Kr>I3Npg;i^NqEYF~?G6!t9ZO zbTeiocAQ%1q9uRJsU=h(ZA&`J)r656o29I*#^j(9c#r7gJ86*=YAYETJu);p;LX%* zOp}ue;NoV(%xcnYx3!s8`JAmyX7)OJ(5+2N{dD`0?%A2D02;|3@wPT0;=C=gBKBSm zLf?J@ks~MI zHq9Wiaf^qYoCA3f;2;2vPG0CIk2p)Cak&xvJaOYGakIPxQGM-d(YaEsGjNJ4$=s#_ zF6ic844e%Gt{lNy6;gZ1vvP$>8kCg(*f7GyExEN(C|;f>oV=z^^JeN8d#*9ee1;z9 zMIgAGkr2kg3}VS&o=APeW~f6qV@d%1H2y6ycc2>tb+OA34IW3>-vd=~lBK^r9TCvUO2Lr=}bxOX)!8A#0}WxYOQzfG)v0&nzglZIT%4PUw(gDJo9E z+N^s-iNzkb9IClkD5;k#eSU8a%a0|t2M}+-*x=v9pkPzB+?8lwQYS_vBWE(s$mg*h#Bk<*-y6`P#rx;;_%&mk8#I?HS`yOE=sY|28nm=a-Kw`T(AF9Eom zpN7fKem_L$M5a0z=$NGxb*WNib$KYa5M$QN63Cbq`=%op8@&Akx+kO*0hu?Wvid;U zZ_c~CxZmAmn)V!KSP?%!m#8z6PE>N2j4mLkMu(1V`Wcr+dYj->X7RO4J`I#&e8CbL zon7-s=+$k#|XDd$I2(z-VRjVFIKIDp6npOrG=fwr?Ako^8O|g3AaD=XN_XsD*5? zq=B0|zr>5V&vmvldo`{YYw^I4Cf1<@93so^IE5^f_7rZY&DMWMmXsMegj%-EFe(-m7j=JTbV{L2LSy_h;!=M}bIFJu5$`la1k z8ZHTUR)(Oi_%{-LbY;cPXtBf>^se>dXq*vNQ~3p@9@;frhKwX*32u7^P6hp2QM;tl zblaU(T~!I!0tDO3s^S_2+Hq5O4q1~G$h78sObW@P#8h7BD!?2nGQ?>%^(mEqb=6DP zk$kwyIfuHaQ{^V19O7vkm2EXa>&-Mq8k`PNQPz!qRUyh!q9G&?0MRp~WYF?T?z^TT ztGQnVyqVU4**L}4C1be<4m6;#xlA<9 z@n)d5?HNgFPNI`lgu6PQZ9ALUNkw7W6_<@HXl+E~5CYh4JFB7;f);sBqj`1?9+EW; z!JLiy!7?ASbCrcTS2sQoNLb0_D%Fl$wD>>2pvp$v)Zgj#+cJ=?E1$5@u4wjnaH>m; z7s1-;k2KgCitB?ue=@9gTN#bGAERbr{k$E~Lh$!#LqHgx--$E6GE#Yp-DG5*78$Je zUR!>4`PM-g8sgE8O<=yZ+Zb+WZ4-ytbL42-!`O28BWyMzwX3~xyRj|HkfW~|JlFno zyeuUxE$SS8JI2N31$GYgvZQdtvw9k}+dAFIf^xnP7HvJEDD?xPB_+C3CEFX*&)QH< z?QH&OOzsHDnpM$KMW|QFWT5OMFyGpR55W2ET&6<1tydAM##Z=b6Q8yw3j`4qeQ2?7 zw%Zzgh)!cLSTE&N!n7SB4flEgkjfijL{;QY%Pnt zQs2)IY|VDVQ^AqZ!X%|LXwR4~%2ERVC9jPvUS(zsGKNg{Dn{7g(Z1@$I;YWYH(vuEf|FCM8e7wl4$PcDvXaV9L_%XROt#du$x9FJ zGLpqX$FouWVgyc;H>j>)XWi7|TZ7FFil)ZK$rq&G$%^H3Ln{*QWD~{EDN~b3Tabd2 z8YApYIYjbzb_ddBlM=FwG}vgB_jTzvUT#3 z2!BUAZZXx{Vx(7N6I+l1sTZnLMJsBlq;i=`5-kwZbhGr_3#32d)@Z4V8ZD5iY>`K? z+r8`}1EAamg+GhCrDdb6ZIY2lMU7HsS737oSAxO0>|-7*=HYQ7dlMFn%7mhe7qp2G zGZ~P~)CEcPyMs$4UJDa|SITQlPZRcK1`O3j###&f2#!q60By|dMhfYtI#E!^9V{p8 zP7jUX3`;$N_Nk;iUyf(BO`tP{;_fXIBtxFd93tOb>DquXVB|PQ=C~A{O_Qp@D0R@W z(61Py=sY9$xia@fY_hCZ`C--wt#d6dJu|YNC$nA*jwQflYJa1~*^0ZS19S(Ek$0j^ zURZpMyywfjPE6y9#EXF_u{)7kl3C%Ea+5~sRGhC*d+`9=8%&lWh$c&)XVkQOX~U8# z$!Cex8+?|%=4`1j?OPjU4vTDsI*fx_>6(g#q5?^@AsSK3va({f+L9?`3_4S05&UF1 za+SgYQSqqm3_U;*H;lV-g$yH;MV12aXPKcuS5U=<)XuFD>X03zt{>uVb^~mm1}W$SAkMESQ$nELf{)OrkH(-*cIh{ zZUoHV{;@{$)@^cvSZmHNGu1ACnm8eUa_Ww=9RG+LFK zF>QxuEs>V;?YPA(BQhzfwmHI58fXlG8^jRk&MO?m4aO^3;y;mwPgkBHzfZ^Ci_c8V z^RKsmQQ%(`_)_VohB8 zk61I;z7}iY;qS!!Jp8#>kcU4X>)_#Cu}wVuN-WI7yJOpU_zSTeJp9F2Cl7bW9^hI* z>_Hy>O6(CH{?FJh9)2v=&BOl{i}LUbu}69MN3q9v_{CU155E+9f``8y+rz_qVo&q% zKx~kQe;nJ(!#|1b;w<*jh*D-AI2FEKNm0L;eGKU9{y^)n1{a>pTNUkk5A;< z#qr5J+#kP~YnQ~Q@bEX{Q+fFD_?0~T&G^+k{6xHzho6kkRFtYx9Sua_xqp zE4g;#(A8Y~@K7n&DuzJ6V?%SewqR&J*A@;{aIJEvifdIv)m(E9E#}&yp*pVJH00u1 z_0USL)eNoT+Ra0EaP1>QOW_4z4u}b@K3Eh92PIzYaaf!+#qB{f-aq;u;_7=Guy(DA!gFJ<7FPh92YEtwa4> z`{>XUTw67?hikVDJ5EYp@Up&8hV{; zo}oirTRRlv+Pa~`Tx%XW$~EuMF|Ms2I?lB_hfZ*%Z62=T+P%ZoT)S_0G1tPwbzIYiU0mBTypn5MhgWfJ+wdJ++dkaHwT}-s zbL|tuEnM3%?C09f;UL%kZMcJLk>O2T>l_Yqt!sE2*FHJCgKPH>cXI84;Rm?(so@8? z_UYkAxb~UhU0i!`xSMMa4M(~5@bIHt`|R*zTzh1=pNHQWeu8WNKD>u(pBsLfYo8w; zH}SL9Tsy_;s#*W%v-+qQfz+^$Z{8TJP{t zuJsKcSFDI@Uyl`Ytv@z_Yu|`X|(Ax5rben6`RVnCuw5&7ELT?(Zup? znphYn7HtnrEKgx#(FSN@$-~5=eTOENr)gsOE=?@o!^EO}9}|l6N{FQiA8%66N~l|CKl}snpj@O#G>uT#G?Ha6N~mU zOf1?fm{_y}m{_!*V`9;Mfr&-?B_=RZJ|}uQ0J_uVG@*#$sa8iZHQguVZ4- zevOGm`wb=*?YEd%v_qI!wBKQ3(axla<@YqP`~ee-7Q@7%#WAsHLzq~!VVYRppo!&= zG_f3|iRDi;vHTAvmhf9NvAm6mCH!}qSpGp1%SlWu+8EYd#|HBmBTap*T|X)H-oeh( zHxOy}v2D8CwjrhhB*u16V5A7-$4Y4Wzlw(sv+nEIAO;TAa!&sBiSY!F}?B1hLV`mdg^h#V{*Y-k<;yrxs7O*^MVoJEm}lLH6v z;V{(_c~<^8@jWb6SmgPsr*=3`IlgtJLcZcmk-{0%PmbiCp15U8oQjw2Z%rd+Ou!qi5)qz z`u2}T;Gd^~zk6q4_bzKd|6lI;qb~=wg=R9#vSHto0 z7xwH@*&+=mBV%P@*$3MPH(%4`JX)t0l%<#~wcjjtR4;X8T9@;1+RO}GB>^`a)-!iG zV|885Ln`~D`HqJMpDIPZfyG8<=b;2dn~j8qm;vQ=KsjhY$upolVq|t6On}mFBs9Ek zKsf*?`wb|C1mzI&4V*ACJNG9*nGCMhTXaB&0*coeU!6sDk;=&;$1O-`F%l9wag+Ld zq?r0ky&3$t2t)>A14UpE0jtY-O#gdQ{MGsMHJv|^O|d8ynZnvwkZoiw%)@SA@5dK2 zomhB!()&G@_w(?)mibsKAY6kdH`~CxfV7ca54cyb62QKK&BL<^^PGe-ZOH#I)J*wH zSQGQJj?>H^Ov+E=q!l;?fQuWr&_C5)N<&CD@!iWB6VfK&&1{QF6Ny=yUW*9}{lM0s zo)%zD|5P1`?YkRo^RjiU8MP)tQuzrE;oFLwRybr^3E^uKec$~7u>w0IdJWg!jm>cl zo)Nu)#Kj_d`Ur^-7rlOzUhkmSRvvyWwuN3l!Nd1QKSkf5vA!Pdw>U)C89f*9a94a9J(VI$&-Ik0hO*RCmX(xn8)dnR zp4ur(n6lhYS^k}}e1Q_aLRr2|Pft^p=a41-V|x7=W&RChUwnvOkI?I1(bV`mc(E*Bgjw*=&?WTrA;`$y8GOsw-s|aCg^-pHF-Xh80Q$tM(^FCTd{8q8N_GfNC7L!0 z9&zK{E2y8ocgCU|b8eWbZ!w`-TmU0Y}RDb&{CDnI^qK_9woKZ2HAJ6-ek!`2*!E3vm57rSDVKOOJB-Hz~0kz>l zlov$@PT*t2DaF=6=Xmo)eQY3TbuD}LAx#vyB_SpJ2sOKI!k$=4x`Eu?Vi9a#aUS1;->v;YgcW1_dbQ6Gz*6rdspx4jlD?}AdGK1V8wWQy~! z_zP~0Jw>C<0%Pmm!Y*fjo#R^#FL%}MQDliY_v~~&T|T(eIe27lq`~;UZ7_JT<#nfX zZ^XHGo^!B$4E`c>`3oZTiBk>zJDq#BZr;O!#R?|MKDq5R$F{H>acj5knE{oJ1t&&A z(Qo8O8lsVp&%}pQ53}7r&WnA3Yi~dY+)@-Pbq$V*R&wo+v734LchRMM3ya;3Z?T6d z;qxSk`+4|NB&^3kSZ_HqiY3OovHg_g7xe89e9O4_*?8(XkEH3Pe9KvJsF-7Vra?-^ z34G5S5~LM8{QLM~dR<1s^;UXqp;sZSKSr;gq}R`&j`(hR`ZAt+zRI;D@h9o^y9DnC z^zyMub0y+Ru8`)noh5?>Gei{ zAF83(Wt0z7$e3Q5x_X-_p^aYep@c2;^l$WZKRtbxvV56-{U?2UoW2dv)A#9#rnBCc zsiFh)`Z~QH#Eq1D_r}NF4qf7(YphFE00@5z!TQ*N<`>< zyM55nc~lC7rJkM6$F^=BWbM@klA@k@&c{Nt26iQs8LW`=PN&|6PKCz+%`nExprug5 z9tgryBL6J|hb##agGj~1u7({txE%%rHqitpGj=s>!#ug{%7}BD$i02=-V0SqSf(_D zjq;Nd%2S`cFGHVQEc$FRelI#z+wIu?zIRWZ8YoRDF1tx~{q+KjMQqHc@(<6!?}+nh z$elNSjOoiFrnU`+&P$X`Z+wk%2uAuVO~}E?ka%^bAUckZryf3*2nNz_pgIj!smyAlMW)u?~Fbd{zef=Xy;>f>qVX& zLPHTEI5OwdXP=gcTjV~N#Id~$!-ItDQ=%-53Dh*fvHd^9`(BKoPo9k|JDuGFVQUi_ zu!h%%z`lgOKu&gr$?1JY82&KB!|| zG${)jK@$ynG|;f}-jRg^S7w9ZH(}6YS{N9m7fVice5<0`5dC<$dWZ9?M=n5{9N!8r z!~hg&c&YFrA-wskqdql=$p9+5RllRjV3Aa}+HOpq6s9@%%6VaXi`i0oG+Jt%9njLN zWJ@!TR+FVRSqV#u;eJS`oz4SY4R{bMrzeD|4l@X(UOPL3t`^E<8bsxP#fjbt3{`#rtG0K--zG}yMK1c#} zCbGn--X5yA=i`vl@d&+sh7x+|>A&ddNqQ36GD*fBl8ilvNI?IUUP(PB3&y8%Y;jPxJ7nd%r+R@#B>6ZNBB~-XBu2AJOa2D9dk2 zSG@wSeb?f(?>Y$MJ|{HP;pO!0R{FM)hyO7AaeBp0$6xv$;adv&z5t`i@PE=bEHBO& zehy2Mz8Cox2TA0DzE?>z28Fiv4U=Yk99jDQu8h)SSg|xG{s2S7G+Q;<*AmI%8MklK>Ul6i}aVPv`uq28bcE*VRkkw(4g0Uq>E(Uj8K z&<--RH3=h_nI>WS0E^lsQ&qWfC^sFg_a)GuOv+~Y8ayZV&ov@9!SkVKu4iSSr8E;; zdohvIN;Bk1Dr#y1}m&7 zf>LA{wwf8I8gP_}+X`U{y9Yc-{Y8dN8W%0#8Tw6T=vL7t**a;KH}NY0Tq~q&0K;m! zR+LbEqw8oBBzGiI-^^>m|DX+gWdtX@67eu4h&2zu8MIBsJ+%jup;8?tPff9KRW5L? zm?UDG4A;zQ!XSn6&ul7bjmB!8QSQbhH2s7aa7Z%Dw2HcJQ( z4=`x~9GVjvMa$O-oJy1A)%)}|1X8fKN~%=csZuo#u_)D{n%_uYnJ=hLGTq#=ku!;z z4+@fyE7tjhWZD$SIWNWw<)@L?g1_`n)k*NDplxat-e4J9)_f#e67vylq5^rQ~57CTrWa9zq%NyAT z(JN~N$IjDR*d$tFwq5omwS}-sg{Wvu@?6p`9q#F5JW<)ce%jEKv?R>z^V8T;`E$o) z3QC!6mi&~ttsg+%_fkhT`sD$pl^0Cjl0Bm6OganIeXZcGMA@ihrzss}I_thLZx9L% zw(iPbScZS~JaUY7+uj5)t7}T+7i1?~89BK1AE)kJ4F8dND^EDKbs?AVB{@DY0ceT^ zV;43?bnlgh6L7j9%a(2>gKGu(xyYS2Jnme1;z+IdbW*~fAHiCba#+C~Gr(aF@wgS7 z^kcDtGuCSFpi}7A^Ev#Eq?9hEOw4-aBOTLenoHM z;aC)={0E|LzGYI>hu7GrDB)pB=%$2zO86cn{FD-~z!)3-9p7?6^bLCY3q8Fh78sZ0 z4WdURJLH1L??KH{G48qgov#Jr4-+hN5d{K0&Yp{W+UnHr&oQS-Ji0mhtZ+8ve82dD( zewJRpOsS7k>L7K&E0pjW5gz+U7etTH)8FXnZP5j9l6MSTvcj8BF|r;Do}Gh2vI`{t3wNF!&UeF`-!(H;CubsWogL11!kgbQ zGp#f-!G*>$p!98I7?`Y{HEOgK&0BalNMn-*P_qF-CKn9C`AKJxtQ~4V+qv+t4y(g} zB^`GnC$^uu*ZUoigE_ukb^E((x1JamI#(1wMhQh9g!284szv27gY)7vLj6qgMT|awkd@ z6S{cVuiT21d=P9JYBAUrAlWLf$;RS+jrlI}uWd8(6WONVN5Lbxea|Yy^Tc9G$?_t6 zTZI)Xt=dQdp8PO&u?P2 z^orpWgOeU!1N3?~z1~Z&pQYDb^!in}*+d^FVTSF;BJ!qrJ@zX~{Vk;qk;4tlOAqug z47;AQ@vY~4e2ae&S!kCw)-wZDk`K+9JuC4V{}?^pN4vTAQ;FU54dBlt+k7m(kDh)^ zPrsq3KhV=LdOCr@+lx9b=q;kB3G_6Xp0Fn@od}`W2=|i&9p0AkgLPCJ%t;yJ{a(s0tHr z|3EX^7SVlN$Q|Gb*km_Fbo(yt%l;IEg2PHD?aRK7Z6|Cd<~g76I-lrnSPt996N)ZpiG+treMf($#HpO)~_Z&7WhzqAus>MJo22KYw*zpeT4gcATdinKtl* z1qbXn@3p>;A2?un#pVmu-}R8#M-f}O+r=YV^=aV}w0wf7@b9wu*|@U0ZmIJod(~An_s%1_!ELTYQL~YUrjMdnkwK6Jyu*G3?qZ zh~};>wq&nHUNGy2EOg!J+-1-e`#Xt5K62^v&RsN~*zR$8cDRT{ui!I>c zcytjwPogWx5oI-f3-fRf8P9uT_qztKh(72VoD+KzX5i=m%)rqfkc#kgcu7S63@?eE ze7HrBo8UMSY%y5Nx8%nmH~V^S=UXPnJ(RG45+FPu==lW6v`^D3bdgu%-=uusr+m** zzMoLSYm^U~;G~{YcpbV13B9xM)O!OU_d@z^?}gw!IP?ulc#5(N(i2Sjv7wjg>6duw zV-SkNWANHXGI!WXPqmb=jT|_Bfo)1*yc)+yf;rUQ7wCo;+WYz(2n?--7(#ArKQ9Ii zHiz~_oV#{9U+QYWgAi`CMKoX?EvZaxI8kKfVS-2d=8$ZLihws`-+Y(YH-8|a@0$}l znfvBeaX8Pp%d!0d5=|!0!RZMu1oNDGf>%pd0wqrIR=s_2(WyPO9^K@5u(04Sctrdkb1 zUAI1uDoNAkhOX^m!?X6lK&iqxB0tffClW@MS4fl+20gm=!bKzqUwVf&+vJWOZNfc` z_oVSHr6w@2?#Nk3X5x;VIik02eoSbqvN^Q*Hi4j5Sg41+uz{5Z=FXFm38$7%L|-^M zsqf^}(WO4M!jN_a-{$td5HE^$(0WEOD3Sn$+5j%eja#Ql_Jz%QZEtbF-*4t z@oyW56DG$5RPhV*YuENVfH9jwZ)nP5`V()Do!e*P^||eH@EctA#50GB^zZY< z_X^|t1pRxJ_+CxlyB?y#Podx0o|u8$LsWrkAJv)EKDL|Xkr(y2=n}|>=qmER@X;#- zRxEZeJ$;-KJ_oOc=*!s3!)i(UbMz#nV0;2nA@;UhC{#IexEn_fcd?#Y(#atE&g{WP z&x;&A8BjqL04o%TgSNT)a2aWi2~rm}uFE=aw*Z1$X)rSiGC ztZZ)Sb(!+bvgDgFV`joybSlBy;0~;7xo$?OcnGM6|3lupz(;jl_v5pYKmr634+FNr z7FYsd%uC{FVOyXF$RLoV)#4Y%LRz7Xg;rke3Sk2l$k-?gc{xsfxauD}jXN@C(4jdAqQs(_&V83Vr$Tqc3NotTaVB#nw^ zjLe77c;Y4)28=)y8-Vl2q5KmYT!&_#sF%yj{UQvUV|fYJJ>|H!8?`ppg3z{HQpVZJ z6A$KE*Bz_B)c(v|Ls>+ZGAd=155T#J>WB8mGeJ zUl`mzJh=TUgWE3*Za+U*e~Auu@gtmz+!YuwdPy8O8NZ0Vc)I47*<2EzlvQNv|HKef zsiE_WS<50#%<}a}%`5%;UxB?iz3+@S(G~2A4Z$BCtAD|O8?1Q&QmC?EzaiKheg1OA z_Hb|Z;C5-Nj>kV5ZH7163j_OKkhbcoRp{!RD~RX<2l^qaOisO`eNtSsym7rF*@x|JwD)v9?zg4ezF{j72@$QNj12cm( z%2b^!yIlA8gqR;yTLPaj(wrN^Rz4f7c^=JB)jWi;{`!5mm~}B+EUzNIolhjQ}3$M7UR@D4$U?p7#Vv&2%Kv%(+E(X8<41l8wS~_eV zHfn5W&LGN`8T}ZMB=NKM8*TsP)(7L z$s^vvfQt@617eayAdX+>pzHDLAqY!(G$_lD)X@iH3s=HBxDwvQXT4nYKFn3`AQ!w( zW5FBx8Wy||1R(?d7V_DNTvz`GPF#((=?OTbWu4dzo`a~gW{J#=C}e;(oGNnSn$ zyQ~x6X1ZS@2}Qj_PooUnJ9hX|VbH zVDlS;%`Xo&|7Z|w4ikuU7#{?D#c=1S79t=cI*SrP={7=;~={#P^yrY3PI`XlypcnDUB7D&qKw*P5mU&** zd4AMzvND=yaKB}SAL$G)Hk|B?W)7MdwZGIsf4q)Y zaTzV|xEG@&cq-bd=J5HD#L0_M?gRuyh1!J4_s52CYsiG?BH@DcM2j`UrE(c8o|cM< zf^wXZj^CTG2TR*Fzt{-dxO%Y}RxkV*X($I%)+<5&)sV(w-=Ps2Vl;gop4C8(YqNj{ zSG|}rYf*9wtWs#@N85_{vbn#nV5Ls?`a5bLlsm+0=TvX4a^BTYQ?$`pzqzQPdHv?P zs-n4zmMqy;UbSRNwX<5iSiX2^5e{^P2+YukEuBl2)NGjx00yis!s_tv?!0wv2TmKi zb!~l(b6rt~-`~B+{b0b;drK80W1MTU$cYn~i*V>7f7~+HkJG)DFk3}YE23WZxc#>T z{OyZY&%Jf+5(Q_cw{7pOYv(LlBx8`*;uLb69_?SWD5mTp*^WhvYPxXhgZX~#5|veg zXHzj||3ol^!9)t4MUgG|7TLy&kI%4P{$S)0{yf6V(|q=MKKm>F{99iBna_U8XA%DV zB`zZqa5+(kXCpKD4L(@oPrzAX`p7MOyPlUizJNE@tdX7k=B0qTj~7Hum~rC$l0$qr zD~v9dI~?RydWS>#*B+^Vv-}^LJ1RgoZKKQD2ImacyixvE`77n4!Ed#XKJTBvI&<^6Xl6}`0ak^ zU9dloZTLRdp}8T<%|-P1oD}*qCix3~`%7quLSJ11cKX z=jtCk@dV7i;g65JpXVUMD4lfTBo>b+!c6xaq=PR0;G5WFJa_>*szY;5AV|$`>D@UA z-ksQ5#A0*?_ZP=J4W<8#rx)|tN`6}_N8}f?!f?)$b9RJt_CLrqRryQJ9iNZ`v8-)y zE~Yg~Yf9?nf@zO@#egs9m^eWLAw#eV!u+ig%Q+F05{X$yVQo`jK6$HhdQKpx{FQTA ze+3P41p3>#WDIo5jr^Z+6gM9$#)Hw172!&4@PFW~N@#fRZr2G1r1$O<+!i~=m8ve& zSfQEnEmuj8$YltNGdIRAVGV+fF%2wkV~pvh$ZA~+DDmm0v37q_h^wlVxl5IhF1kyF zwbP?2t+6F#wm@!4J)s;4A+cO|{U}?@<(NVzQ0G-y&Hbkf4M8)z+<$sPdJRfc{tjZH zbS;ccbcw5*CobZuAZ5vh%|uVdK$80MCkzi3l>f$P^J}Qk!2SzTUv5;V;L-cA!cc&2 zlz^s1t8)`k^8^Etngjm;2q#KPS_1u*`TA3mFT_%mPVx(5xF5eqM5Z~z1tI7D#X%kr;N+JR)u<9 z++0Tqpl7iY2hDhWx3VMnfkY=iV^uPKN0W8*rYVsnuKxE&ZpK9n{hgo(8Gc@I) zg@_<_XdWVn;nGG>V2w9LKJ7X<8u|ZFh8P+z!f-x(UDojUSP^m{rF@E&P~y`^1s$x2 z&fE-I9qoo|b^mT~L)Z((VCmh^*}ZDG?y6fy?jtMb9?<_}V`@np+j{ z`FQ%>@oOZKrbkNI9%$|gBTYotc1Z&7h%kIcqGc11sx$BR^MX$={;!M#vc$&$gCuYo z_uJWuC@J6|0Si))qd`o>aU@Zyv7`qon1O4jSB?@RSynvRR5Fc2I2?+Cj!5LpAO&4b z3YtL*n(-b`kTblH6f`5!$;&65;Tt1gBHF*kf}ZEwA3MWSBLBvpZ{V9GBB+)ViRc~? zhMi745n1Ca+L7srNU{WzDO^q3m?3FH4DA$QSpTxX$tBLsI6~2)YZkZod@Xyu?bKw+ zk3zT*8I-J<(KV4(WJcR0CGHU6YgM@gMRir)-8{6}ibLoU<8Lz)hm8&|-@_#uIp74@ z*VjfMqf8Y;Ul(CminH>0i^xIs2$N^F@0BMmTQpo`=Z$T0ceS~FxjK-2R!*JYy?d$5r|;exYjelPw8VgVw`a0@evMT((3hOlCzO!WBmL5Qh)D`h#3@ z&59i3vrqEbXYou7{Xm3gTaWNIUHBo~uWW=9P`3CiuHV7`m*w3dz9H{fcvGw$0EfY;4S zA1@HauZw)e8NNR9_s(#U9IMwQk5x<#*Qb11l>BtN2+vXEmzxo0-rdNa6S&r<*4BaVaQO|c!C_X736<6oO13Js!HPPz2VK4bSpZF)XJIi>N^X!P z#;j9y$P%S8Qzl~BN96D<;qaVCNxFn%w1i`{#E?SXoowa|w?wdt-5K#X!=;gjTnFEw zU0Ml^a7!XDIK!=xpR>sS#M=?9PTLSE_24MANs!UrFK|^8X~uZUbkISXA*sd5WD;9e zGTHSKL-QhwS@AN;1$VLHtz_G~oZoEPMBnG29eloB-#?}A zf2HpZw2JSy>U)pAe@@^3Ro@pt1I={z>HA;k`_J_KCO8H0yKc|0Rz&(rQVG89vN6bpc z80G=#Nz5Q=v9%^ussz~@<+8+a>!Q7%r4{NK@tW~bF^ofLzAQ3V9~pyA#V5rwuu{Gl zQ{(e_eMTa62f>Vk+f>wE1NUtNd!&k&UBajM5X(B8zm0 zG4j+;mZ;?!9wq~G7x!8TKF|0W1LgQj2%rtaTbg{#Cp#@AJDpEx$odIGKQCl zyj;Z#G@SECN_bhs%L-m@;RSXn^GE7=Y35}+FK%9T^Ag}?KQD)QImXMAyd3A{1TSCY z<*#}91~1>{<$JvR6EFYD%Lp&OZ zFtB^(s589g%%}K`jXr~dZa(u9{(KGVv@@f?+F5`qJv$G$I=c}8tIuLHV)9u4xCMdj z4^24>J3xGgT;RiD3>4nZzN+XrV&VDQ0|=PN8?gXM%zuNp(xo6N6gQ zgVvlOp^on@v|?GeoCd_6H!79H#gyO^mnLDb>Ym80>ybhvhpU#B8r{3GEHOYQWJ?<3 zn`Ldrt4y3Rjf@G1O2{a4frXmKMaNX28iz{xttuch#`fhV`DB91BsRCNYtdvP|Yx?Bbat0VX4AiR%=v-Hbf{hYb9*%SCAj>rBkbq2?$RR-Wkdl{4 z)XJvF-(^Ilf+H80>0#tSe-@9BJb93o6u+~u8O_g_T92>C99>D&rYUkW&RjPNi>49B zS4iew8kyt_-@u^y>mqBxE+V&6CDh^!uaCguqm*Y8yc2nXg5e2gxQhCtYP!spMt;T@ zzd~N};hK@_@oWUf9`B3*%GLBEs~rJfDjnJ53~v~DKfgWY3>S}l!5Q8-@^z;BCZByD zkVk&*4A+hPhR=S7XJ_*8?92?;Bjl5}pTWj!>6r$+JcB*gO=tG;=ll8d6TE!U8Qy&6 zZ{4fV%h?Tl)QqfW-MsWU!&}c{t|>kH zasK=~FJWi60Y`cu*N^yO#2LN=hj!rSMf?;)z1iXUzt7c5VBHZ_IB(6I{i8_MiL{7= z0}XK0U~*UJRML7=sTrb$4Z=B@GaVVxXF`Rj2qv2Sk^da;#Z`|-(xqD_8D zkm9sXtcdS0Tj@cX1$$n8QUJ;qIhO%QRx&R^h`mw59`E#dcJoBZ9+NndFm?G#=*!Y^ zYhr^!JeFh<@GL7EI~UZ*C%HJ`I5t-TdXsYDL98?&j?Z>P#yP`{+{!NH(zPj4!EaT( zIGtf9*00zod4NCnVz^R-Y>sePM<`N7zQku>I<5!;uDug^h0k8+vx}}r7`u2I4lw}> zf-bsrWF?-DY;=b29J!A_z0UAmBZv468!~q@%3A5jS0JH{e8(BSXXIbG$_JMy9eESk z&Nu+&%v5Li-ZS&?f)OiAaVQjC+|CzRXkU8<@V1{h!WSQh{CnoJP{32-D?RfKe*3O7 zyyMKzupmDJfvxlmxX#kEV*%hSDp|^Nl-@ZDpv%ry^JgP}w%{$+><+LerD90C;>uL5 zkR!78%1+4oQ+4}Wb$dnKAoPtX6$>x~Vix3h#ZSEAA+M^XyQS6RCp4;gsWM7PGDgn! ze4dl?eGb(?e9gT}{wI`RtnJ0|4N|vJ8kq~xIx>%wSv~MX0x6}{Lumxl2nhrvu-_S8 z6glV&FOK{ve}ZoA4!ivxJbk1OL}kk zwfpY5OWKzgi^w$~k>) z46M!cUqX5VplM^!$W~u`qNK8@-C(;AC)X=x(+gLw;fCAX*;! z0GE@WWQYDjVq<~`-z2fom;-c75@E2sU}cW%F6?w;8#M<$PhzNCgcrj$&lUVl z@a2l0y#3?gI;KJ_xQi|2cRH?x5T9V0Hxr(g`pRwhgMRDJbh|mUSJ= zx{ZgBQaL-_9MRr@u`ufU;_OU(0idG zSO1@cATTZ#LrX-s+(17nrE;D>kZ&Rsl|i>CkVYSlCy*J=a2YDq|0i%n6XE5uQq2R; zUObXtOrn5Q%@`1dHByO(VH63o;Az97oAQv@UwN$7(cnVAE|+IZPoYB2mc8N z?gW>PPRA?ymI8a##X4&G^Fe2EzIyG-EBGo(Cr*jTyYAku+I^h>9uGv&?2jQ>1aNsH zi2>?>tdI{EG4!AaW94|q;I!hCV5wrGKX0Qyf204}bIXIz7JOCQeTuh2I2k*>`D$MF zi@|T1Pc}Nf`SZN&AK+EiH(xDG{c2;@H-BCz(`0}1)tM@d7{W+{gdiKJ29^U;nHZWS z!jR6I+6Vfk2>--85IN{O!54#7(?**LK^8;%M0mAiP&{wz(U<&r1J7!24>-P%2{}=J z?Yz=_iQ637xVZjO`AeD@OlEQq-X@sRoe!Rdr)`0(R@(>1F#o`_`N7j*s`S}eEJ6jY zLrWsplE*A?hJ7K-uI|vy(8WZsT3F5F5S9|NV;Rt9nHc)42tyqSdop=GSX_mF4PTuE z{*Nj?CyS09|AQN59@_wdlrIJz$rpi{FzAYDTJt5mmv7CMaehc@*>kB{MtgVJwDsUk zY}$2b3!;A?5|QQ3a3HjXz6G_eLuH&5eIbY>?obep-Yf;HPgc zXLi*?md&+jUhp5y9A-=Vuc)OrC$uyIIms8QV$zUeJ&S6 zYeg8^G--kekFJZDg-5~MXtIC)bj<~H>v_7=*T)4Tc^y~~JjG2KPhpII9AmuyDIC~F z2EA8=Z_yCnXrk}f8wxJRO9wKl-bhi(vFM2q!TL91iBcA5c7j)S!W**L39~Zx|EELc z7=riR3YapXVs)(87F3KP)-9n{I!wUI69HdVi}0<)=^bRC!KS?Rde#Hi8_X#VHet>< zG+GrqR|Ip4@0J+F#0-hITnx<=;oB4|yCb-{I49VUCu?=L;xQAUmk>jRB3ub{eaCpI z-i?L2LcUv<@?DJ>dP;=XD?Wcm@V{c1`~l0EGuj5S_gh`$?^f)+bhKHx4CbzC9VBzl zBXh?~+;axk4l4v0Jl-pGy`GtJ*~&Y}b_|+%)+7F3IQruR!xPX<-fp z6$F2RvzQLQvM&$vX2qR()-8cJ+K?9vm)4y6^|rh%m^F)P2Ll{TH5ZTNUlR;m3ijp%R}ZfJ z!1{r3&S?Gl!0Yg{tb}OjSWoZ$eb-iETm-jYga84d^AaXWSKh!Wneq*=HjIgHVM;p= z{9F(G%p-ml5;tyTb}daLjojVvM-1;nnQP(gM_BErzxcto}13%5dB~# z1d)G)q@g%BeWPC1(gvwD)=+AVhV6s#^rFt_Ixo~mpBB!He~P`tAku;qvr^1TDa9^2sn z?0t_RSI%H9gqnOUx`D63JQt){EW#UEbudP?&XDe{F^d6+DBxogL?{=kj1YM4?J^T| zRvBh;=}fThyNy*-G%{=>`;6l|GQ$kTjv;j8trjK0mClpta$$0AHZ!Z&YJvk( z#r!yqFqSuJaf5gyuLaJT#C$IhMe4r_&D8?1oKDY0&AOW* zv15f6Sr49t`SN27^YJ7+zcFdP&l!FgVJfkvKLEm@gM1eJEpYiaUf#ilz7-BSA#EDj zgv2;p1&;47IRMbN*zGC*2U%gWZ|lX-ViAU#H-=KsRXOEvotunVH@N%|4g`DApu1Q? zbA^f|X@EMhL&jy=>x$&OxS-yc0cO~cnGBTzZc=HG8q<17r% zEqL@e@Q}%RDd!}`0JUT~S@C}l%sIBffqp%`@VOMLB}_Nx=Ewn5&C34`{X9}3H&`5k;P=u<2DN+D>+z!<_4horFb+i z6+;CgdNjUX6j$PA+^Jfg6mamo%o$FM43-L3I$CESIgocauzvDoRKgV}S{= zBGV~@8|bwvL?bM%usT8?Q@S$L6^Pt(JZy+i6TmveP^G|?o!QJ4A!fG&d0P*U?km~= z#g$}=K`b(B^C^?$ZQ!Ri3=3PqCLRGPt$%2VC;6m2~xKN{QmsB4^^f{7_w}|k4x)GAnYJp6& zBGm+-h;Sx}&{$Ssa0aIg=6nLgcg}1TEl#FT-8C_U1uO>EGy%PW2tFSxe#n`UhhLS1 z#={m*3{4i{`(+j5m3^2?0RgIt7K2ku9UxQ{uzSPfor#az#Lz|ofb5tQt9@gAKe#97 zxea*?M-a4Jm7|VIVYMW54aFL)<*>rOL4?~_(VSpo9+Y;EWS56ejaKHF4+{P~3`Scb z!fw-?wpQC0PeY%s$$jKmZPWpK42OvaCe?LnplR802TOe>szu%2TTJS|Bu3d;- z$^rto#y&1(cSEs82w+aTBpC&33XkO+3fAWzoA&--eO~rIWUGm1`?vBTl5-%#K~D_L z7vYZB^vyFrzU^uz5{tdFu0fYU0d;k138D^dSvRcS<^RF{L3Q~ zFjwW`J5-HU(#*i`9S0@{w-+`wX8&f33#%1L`Erb~=~s)$Qp_T&IE!Gr=SiFkgjr;p zGwc;Z&j<_|C1suGFjN((0jEVpzNsrRZS3t8KhPDKHumsKmK8bt7MO1Hw85;?HE)Y^ zckl{V-Sb-p0~ac`UwEV<7`U+T%^&?O@Hc!I_AD`8Ke}WfnUK8sc&^>}EFXHa4sp4HHARiSTZVA#rR&p%D)| zlaYXE3uPgAMPMXg72ib~uPl@uN%`U>0Sw0AuKog~Q!H??^-zh9e)MS$hMK&E#Ad+< zp5m|V`Rr%;><6!9*W@G4whSY49_s)|epmto>(3iFK_C3$oC8s|@q9S$`6Iumcyr%% z6>sw7LI8M*V5WUJ2=&dhkK_Ab&X=pURJ^(Wixq)&ML?;58dmZ26A?p9lM1#{u3L2naJkGIRhEdgy3mAu4$GW0j;ruF(%G2|9u ztOs8h2*U>wCgYFi6rUOZSH2*n)KuSI;HJe4{LjI^L;vFz_qhqb(4Odr+EfhDRbJpQ!Y5cNecJ3mMxPCsW;AMH1Nq>WoJtEaTzyqT_w6>+YLht@`5iE}9eJ*sw9 zyq{1yc+f~t>ZOhR?%t<71o)p9e_K1Cum=0m*=$}M5 z5O4J^5zPxen4{(A5U#op^pkgU%NCzUHmu6USLNLyzA5i?Rw{MU1u51v-GVbrKo%e; zGl!)Bd%u^xA7JkXXuUhzs`pFn995m+T^zw3wBGfQ1Og<103DVBkuUP?NxnUWx02uW z>Q2nKZPCt5=P|?f$M$D0EkgoyYJf$?2y z7MLa6N?u|==_(TMy0a&;Lb6>j`aa2a&zp?a`r8ZRP}o%b&&IzM%Pqw_7(hfn$IC?s zQt$h?i?u3*08pzbtgm73_L2Gak@@zK`SwLN^Kz#XSQ+}f2xHfVNM0REjK@$=t~;) zOz7F4&Y5EI&C86L)0k|gx;#FAprzC4bHiC5Hi$5y(>2j7@Zrc-@ZZQT$%HpK!u#S` zAg5pggohD*qt!CsmCn5gmx(!M9~~(6k!<&qZ1<6D_mOP( zagh3~Sg`E`>+ z*>HN^uSq_0j7Et)<4JZu!&vN#lw-Q(G(QHoy-wm63r2*WK;#KL9h>;HP<(n4CPNS( z1S`D6e^8DenN?l-uMsoj2wZtT_bl#i=@CajqMwa{pBL-dQOAfoOLPf0IZP=KzLfMN%#imjpEUX}DV_Bo12qq}Gzr)!|n<2h}KecsMgjUdLJK07rN@QKx#^uXwOEj_~=f@GtYPncsUtDunYfOQgWPxR>z;fFH zqnU-HN(`5ao9qjO-yR~K=mtKC@>Ysvs=PAW^31%#v0e;Uh~@Uhfqkf4?zU)8Hlo;D z#Tr@cilw&2CczMn+r{u=G2g!UDsT5*_T*-ixD4o#)Vj*HL}n9?Tg31pF)xEc0)Cdk z&RYr+lV#Y`XH~#29Bak!5>aYj1o*h$%|hx?NErxA7P8v5Au^wE+?wI=Rz6KEXA8A(OZi6hUtFurH-yv?#xH>9t+S(j^9GvoUnyB%U7U(!J91z_ZS5x}V>mV``X=x}d z`PEj$t8ZbzQQ=r5hVK>IGA2HUuA{^ zXPX$_D5@?OcuJz3FNW(x_2q+F?{4?{-0=gTMhxF28ZQ@CCPZ>D2+EryW~GL`W7*2( zI2pjP-7d{C;kZ`}-;fqg85ruH*_Ku=98SB^mJ3I%7~UotQr9|`ZyM4_rUYb)8N+@at#1_d_O&)ejLHm{$Jh*~kSNeh2m^fkK5>_QRg=V$OtFF4c#EjBFF*<} znbf#Q4A+Z|832T!k|DgUnXq{!m1GDpBPOo}kPJcIWF-RXfL4+%>!~Z35~IZm$AcB1 zr=`>LP;7=gadeuPT!}SsC3<>yPi0dpxG+>jIDN4HZLwS`b&lRAR>vf9sx+14>;Yeo zR|+g;WqJy3hMyS1NQsrouvBxb8DRuFaFR;lEr zqD0Aw@glZdf1?sycOYQ3-))q#T4kFrid88#^N}uWjmotElCl&^|Obj3?qE?;o(_x(71q=N-8EK(RCN(xFV~%x6~#o;rWQlJlsl}` z87-<+En1tsE?w%(Bt@*ctqY7@EtJTFhN2EUv8!!ecXvH}yuses$bcu{@rVtTnuOJY zgX~eo2{1JVjR@8+d51!^!Td~A^S#@LHH{r0wLCI#t?*oCn+sICF5ve{%4Hs}sLdn~ zJcGF^V2-}Br3R1h^0Z1$=<{^#g1Bv0W0bw6Ww*qZ6_BNw0FjkJdmwjYfE4E1yvG=!cE4oI482! zWyW&ZBrcZ3x0pp*92s@kMw}p*uGcd{zsu9(0sd8oWkjQ~WF6=M)5!phQAvXZc|=nt znK%>!J!-V7cCJn=DmDp(E-4onn2io|$$E`yD=&C8Yu~NdRhOf}wmwpU)9USNV~h*U zM0;4PUU9!|W{_W#J5}bs?($5kTWMQDT~BfWO^6ez23D_VPlRA7$uyP}ep7Of?!NA2 zt2E-B^=^NQB-uVss;8!mrPG_#9TSw-m@9TDC$pAyDZ%huFG9~(DYnNUFF#X=HZd{XeS;f84$-{(W)~*s}O`a}_ zk5JI~J#I-A-DS%XQBdpY#N;tAsp?Tco4uG{B%kSpdN(nDG+k9o*Vdhg3f8OmwMV$q zB-SZ?>)JHzK2P5YV`@tw2paTgG_=kDPy)5Kb)7IijD&hvnDeaxDXKozYi>n@KB*1$ zTCi4snb?`8SVTJWP;c1i?$Xm&I<>8J8SB%OvCZd!h|L8IX{6g+W|{4h34CHz8W?v3 z+`he0dHcZ!dwhK>c5G;_tM2Z_zKAtmm99!gP=pzrp66jwa`bo6S>3Eilth|Z+TB$x zot=zzV~Ph!gpIQ6(v+=)tSAHSE;IOmSz^q%OHrSwY^tSLg}`(&tg%Q-#7NBaZ7`5- z#SXYgBjZmur7Cv+pj`%ou^T1$>{t}1U>RxL$kC&6oDywGc#T0-<$I+G-jq~Ilmu$A zBD|b(#6*e4RqMT8gyJ&w551~IGsRW%b&{T6^X5G&*=mQ$xb#LqWzB;yol#6NDRYrg ze;dul9p_F;gX^k^$v<{4HHrLrYm;PGSo70>UrGB$=l0Sgt{#q7>1^ zk@3{_^!F<~Zgk8tAgS#qwR}^xeW(?C#X5Us$_8V~Hu$)1khs~*QP(4fFJUF8OF64Y z^RBz>yygsC3HH}6qr9x~YOtqhJ`=Bx zE4(H#3#VB`+t!`$!;ZC? zt`WQF*q%`d%}!6QNJ=W`jgm=n4vUH9M(fHX5&LR0twLR8#VsSDx!&X6-?biuLu~0D9XcD`@12$zRM+4G4!>pFqF1YBQL53m5tbW++ma;KgV!? zR(<^=y2_3b;&W)yn09U_YICzM;6Z_WKpjUl!l1Sq@y3VctF2wJ+tRS#CsI!0Keru{ zGZdA4M28BPA}YaTj2J?q+r<>|ukt=sT#dqRhm(T~_f9cM9L4>8;wmvuOfl4|tdf#w zZjrDiDsG~;vSHeU4_p)CtsCVpL?u<5!L)5uju!c`dq)}P)dk1xJ!G7DrL18_!MMk+ z+qwtm53#!QR4!Frqbx^!BY~^qEi#~~gLal#w6k>3&eB0UO9#w@Yhh*yQ)t)T$a2?1 zk=3rgtL-(LkzpBBVTcCikx(9j{L2wJ`#3qjXdjWh&=DQBl3c)G4i6TDe@ziGxD;l zIr3AN3n%Vvi$q*^MqYE>#nXDOj{KKv&B%CHFZ?KWj8wXA9&ypLq7@Atd4QK5ULNLU zfR|6B3L{@d^1lOSMqY638G%p6ECyT6La^06XRbxX&aC3E8vKHJ`1CWo`RifV{byiR zdhMA{x_Zxi22aG$hAdpAIdEc~QNW5O_V>pO9pn$f1eZPZDFdcCp_1 zB6|(ez$N7#bptH^qR$;}&#T+7)NL|A;NenrW8?_@>QlE9>h_$xfq*btKOjw`v(y-! z_;)>=aalAy% z9oaP0&*lV@!wDpt#F@_bf6QMI7rch{i7-Y42Su)R4;o{rF!eN`h%F4J z!+%YB{Q%itAPN#l4X0jI;*EA}q?0uUbq=EyW%B^&EKJ}#BABdl#E{<+hDouKUH% zpNTNMSLCQjiCcpxr=%VbnxATV)P&m4pM@en)@YWG8Zc(~ACs(qvdG)orQ~DGxfMu- z+fI3tu#ev)$IVy<)EdKRkU5bG%&oWa*u^SmcxGfLM$r*=;0JLLLk*5Fd?Jimvs8ZA zK7Kfy`1w8l|G0*dL(nf=4U*}xgCuIq_il&DMcD(n942`jA-NnOxf~&5{$L~I#{d2i zasiE^i}#HqH=&^;H*<6#cuJlg9TrG6D9vJgkk}L*E1&JW2&6cPK+x(Z9AUW48W5P2 zpF~<6i++7pz4{}$W{Fv7)9vsVzEj>QR9zKIv!-RwTG`_N2g`kgu9K6)I8yz1QvEnm z{kT7@FqL{azL3lDo8A*)>gouneq7`dE+gQr3r7&`as0>zEVJ)M5hK0)^(cj>4I&Iz zQ#nnf?kR(cE!NNM$JlW&o-vEZQ5eGVWt$_6XaEKTgUA!d0eSWx*En#{$9w zvOK;r);)nloKGU2Kq8*-hb7`g)~x%z(kX$6Cy;&@jlfSge`Fn5_f`?k*A$x`w+7v1 zNV)kG-X}!nOUk{&5r%5ifbhSGatln(T^LKbG->Pv1OhPaja9KpWgHl6s*(9tQM3y0$PGX$viIkf2rBGb(zmrTS znPgfJN2US)dqSlJq|!oCX#uIU;C~F2E_v^$v|t2nTtcC^5JEAibe#wnYAQ`c>SYpX zVFZfJ0>+jFkv2LIOyPeeFD*2QbaE__O7ENZgiI%sOs9}cCzDJk|BoTlrSBb?P98zH z@}(mPp)_S=Ey=V>gr{gSO-Acw66%x)7KW24vw~2Y9AQK{G9dhKn)JC(qIBSKJ7~(O z>ph{=sif3tq|~XT)T#etD0SI;N2yasrs52=5yX|52K_ZCwOWLyX-Z9jSA)GHzzxAd5ypZ zo}G0{4CiFwVDZsWhI4aa&(bISA|P5|2H?bZH~#QBzDPuHp2ZlRh?UFE9T%BH=>+>& z1q`h-ndf&-jcjp-uR>Ju`^1n(gr_q?XI#wM*i%gq9{;h=0rIEr$7S%bleqHHu>AEE zTu(pt1za7L=jWWlTlqnd&=7*o9z^ul4UuAOAct;u^&gV)JP|_jt^Thgm80NEhOtE? z*_jOS`7)}Ne=x{Gp_~@?oI3a!ejok)=qST}&K2RSk*s~-PkUx?t$F9ru#tI;P| zLnfLb!q*ViPY{$HU8GUUAh_d2B-gbzl<(>v5JO7^0!=dfElvWGNJrtX_^)aoIM68k zlXwEn$=d)@y*ec$JdZ=neb+`DyHO{GjtZOx$Lt85-2XSki0~KV#E58#I3!?d+=IaZ z;x#C*hr|A9F-OVB;Izvf=d#Mf$TnBU&Xy2l@`01xv~hSOr#yUa?66G7@V-2sqEl9F zmcS8UYk30LK%^L77P$H!MNHOPcoy0W8JKxCiY^@e$VUMNNGcOy1Y2UP+f*aLuYjVmCA?iJx87H>&~#~ynDStKM