diff --git a/Source/VirtualTrees.AncestorLcl.pas b/Source/VirtualTrees.AncestorLcl.pas index cab88612..ea048b12 100644 --- a/Source/VirtualTrees.AncestorLcl.pas +++ b/Source/VirtualTrees.AncestorLcl.pas @@ -32,7 +32,7 @@ TVTAncestorLcl = class abstract(TBaseVirtualTree) protected function GetHintWindowClass: THintWindowClass; override; - function GetTreeFromDataObject(const DataObject: TVTDragDataObject): TBaseVirtualTree; override; + class function GetTreeFromDataObject(const DataObject: TVTDragDataObject): TBaseVirtualTree; deprecated 'Use class TVTDragManager.GetTreeFromDataObject() instead'; function DoRenderOLEData(const FormatEtcIn: TFormatEtc; out Medium: TStgMedium; ForClipboard: Boolean): HRESULT; override; property OnRenderOLEData: TVTRenderOLEDataEvent read FOnRenderOLEData write FOnRenderOLEData; public //methods @@ -89,7 +89,7 @@ function TVTAncestorLcl.GetHintWindowClass: THintWindowClass; //---------------------------------------------------------------------------------------------------------------------- -function TVTAncestorLcl.GetTreeFromDataObject(const DataObject: TVTDragDataObject): TBaseVirtualTree; +class function TVTAncestorLcl.GetTreeFromDataObject(const DataObject: TVTDragDataObject): 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. @@ -142,7 +142,7 @@ function TVTAncestorLcl.PasteFromClipboard: Boolean; else begin // Try to get the source tree of the operation to optimize the operation. - Source := GetTreeFromDataObject(Data); + Source := TVTDragManager.GetTreeFromDataObject(Data); Result := ProcessOLEData(Source, Data, FocusedNode, DefaultPasteMode, Assigned(Source) and (tsCutPending in Source.TreeStates)); if Assigned(Source) then diff --git a/Source/VirtualTrees.BaseTree.pas b/Source/VirtualTrees.BaseTree.pas index a24c4f9e..b9192da4 100644 --- a/Source/VirtualTrees.BaseTree.pas +++ b/Source/VirtualTrees.BaseTree.pas @@ -1163,7 +1163,6 @@ TBaseVirtualTree = class abstract(TVTBaseAncestor) function GetOperationCanceled: Boolean; function GetOptionsClass: TTreeOptionsClass; virtual; function GetSelectedCount(): Integer; override; - function GetTreeFromDataObject(const DataObject: TVTDragDataObject): TBaseVirtualTree; virtual; procedure HandleHotTrack(X, Y: TDimension); virtual; procedure HandleIncrementalSearch(CharCode: Word); virtual; procedure HandleMouseDblClick(var Message: TLMMouse; const HitInfo: THitInfo); virtual; @@ -1237,8 +1236,6 @@ TBaseVirtualTree = class abstract(TVTBaseAncestor) {$ifdef DelphiStyleServices} procedure UpdateStyleElements; override; {$ifend} - procedure UpdateWindowAndDragImage(const Tree: TBaseVirtualTree; TreeRect: TRect; UpdateNCArea, - ReshowDragImage: Boolean); virtual; procedure ValidateCache; virtual; procedure ValidateNodeDataSize(var Size: Integer); virtual; procedure WndProc(var Message: TLMessage); override; @@ -2220,8 +2217,9 @@ procedure InitializeGlobalStructures(); TheInstance := HINSTANCE; - // Register the tree reference clipboard format. Others will be handled in InternalClipboarFormats. + // Register the tree reference clipboard format. CF_VTREFERENCE := RegisterClipboardFormat(CFSTR_VTREFERENCE); + CF_VTHEADERREFERENCE := RegisterClipboardFormat(CFSTR_VTHEADERREFERENCE); UtilityImages := CreateBitmapFromResourceName(TheInstance, BuildResourceName('vt_utilities')); UtilityImageSize := UtilityImages.Height; @@ -12896,13 +12894,6 @@ function TBaseVirtualTree.GetOptionsClass: TTreeOptionsClass; //---------------------------------------------------------------------------------------------------------------------- -function TBaseVirtualTree.GetTreeFromDataObject(const DataObject: TVTDragDataObject): TBaseVirtualTree; -begin - Result:= nil; -end; - -//---------------------------------------------------------------------------------------------------------------------- - function TBaseVirtualTree.GetRealImagesWidth: Integer; begin Result := GetRealImageListWidth(FImages); @@ -16596,94 +16587,7 @@ procedure TBaseVirtualTree.UpdateEditBounds; } SYSRGN = 4; -procedure TBaseVirtualTree.UpdateWindowAndDragImage(const Tree: TBaseVirtualTree; TreeRect: TRect; UpdateNCArea, - ReshowDragImage: Boolean); - -// 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). -{$ifndef INCOMPLETE_WINAPI} -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; - - DC: HDC; -{$endif} - - //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 - //todo: reimplement - {$ifndef INCOMPLETE_WINAPI} - 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); - - //Take proper drag image depending on whether the drag is being done in the tree - //or in the header. - if (Tree.FHeader.DragImage <> nil) and (Tree.FHeader.DragImage.Visible) then - begin - useDragImage := Tree.FHeader.DragImage; - - // 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); - - // Start with non-client area if requested. - if UpdateNCArea then - begin - // Compute the part of the non-client area which must be updated. - - // 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(nil, NCRegion, RDW_FRAME or RDW_NOERASE or RDW_NOCHILDREN or RDW_INVALIDATE or RDW_VALIDATE or - RDW_UPDATENOW); - DeleteObject(NCRegion); - DeleteObject(UpdateRegion); - end; - - 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(nil, UpdateRegion, RedrawFlags); - DeleteObject(UpdateRegion); - DeleteObject(DragRegion); - DeleteObject(VisibleTreeRegion); - end; - end; - {$endif} -end; - -//---------------------------------------------------------------------------------------------------------------------- +function GetRandomRgn(DC: HDC; Rgn: HRGN; iNum: Integer): Integer; stdcall; external 'GDI32.DLL'; procedure TBaseVirtualTree.ValidateCache(); @@ -22457,7 +22361,8 @@ procedure TBaseVirtualTree.PrepareDragImage(HotSpot: TPoint; const DataObject: T // 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); + HotSpot.X := Width div 2; + HotSpot.Y := Height div 2; lDragImage.ColorKey := FColors.BackGroundColor; lDragImage.PrepareDrag(Image, ImagePos, HotSpot, DataObject); @@ -22643,7 +22548,7 @@ function TBaseVirtualTree.ProcessDrop(const DataObject: TVTDragDataObject; Targe begin BeginUpdate; // try to get the source tree of the operation - Source := GetTreeFromDataObject(DataObject); + Source := TVTDragManager.GetTreeFromDataObject(DataObject); if Assigned(Source) then Source.BeginUpdate; try diff --git a/Source/VirtualTrees.ClipBoard.pas b/Source/VirtualTrees.ClipBoard.pas index ebec4473..1e0dbede 100644 --- a/Source/VirtualTrees.ClipBoard.pas +++ b/Source/VirtualTrees.ClipBoard.pas @@ -108,7 +108,8 @@ TClipboardFormatList = class var // Clipboard format IDs used in OLE drag'n drop and clipboard transfers. CF_VIRTUALTREE, - CF_VTREFERENCE, + CF_VTREFERENCE, // Reference to a virtual tree + CF_VTHEADERREFERENCE, // A drag and drop of the column header took place CF_VRTF, CF_VRTFNOOBJS, // Unfortunately CF_RTF* is already defined as being // registration strings so I have to use different identifiers. diff --git a/Source/VirtualTrees.DataObject.pas b/Source/VirtualTrees.DataObject.pas index 72fe8b5b..0561b7e3 100644 --- a/Source/VirtualTrees.DataObject.pas +++ b/Source/VirtualTrees.DataObject.pas @@ -9,7 +9,6 @@ interface uses Classes, Controls, Graphics, LCLType, SysUtils, Types, WSReferences, {$ifdef Windows} - Windows, ActiveX, JwaWinBase, {$endif} @@ -30,6 +29,7 @@ interface TVTDataObject = class(TInterfacedObject, IDataObject) private FOwner : TCustomControl; // The tree which provides clipboard or drag data. + FHeader : TPersistent; // 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 @@ -48,7 +48,8 @@ TVTDataObject = class(TInterfacedObject, IDataObject) property InternalStgMediumArray : TInternalStgMediumArray read FInternalStgMediumArray write FInternalStgMediumArray; property Owner : TCustomControl read FOwner; public - constructor Create(AOwner : TCustomControl; ForClipboard : Boolean); virtual; + constructor Create(AOwner : TCustomControl; ForClipboard : Boolean); overload; + constructor Create(AHeader : TPersistent; AOwner : TCustomControl); overload; destructor Destroy; override; function DAdvise(const FormatEtc : TFormatEtc; advf : DWord; const advSink : IAdviseSink; out dwConnection : DWord) : HResult; virtual; stdcall; @@ -84,12 +85,20 @@ constructor TVTDataObject.Create(AOwner : TCustomControl; ForClipboard : Boolean FOwner := AOwner; FForClipboard := ForClipboard; - TVTCracker(FOwner).GetNativeClipboardFormats(FFormatEtcArray); + if Assigned(FOWner) then + TVTCracker(FOwner).GetNativeClipboardFormats(FFormatEtcArray); {$ENDIF} end; //---------------------------------------------------------------------------------------------------------------------- +constructor TVTDataObject.Create(AHeader: TPersistent; AOwner : TCustomControl); +begin + FHeader := AHeader; +end; + +//---------------------------------------------------------------------------------------------------------------------- + destructor TVTDataObject.Destroy; var I : Integer; @@ -389,8 +398,21 @@ function TVTDataObject.GetData(const FormatEtcIn : TFormatEtc; out Medium : TStg {$ENDIF} begin {$IFDEF EnableWinDataObject} + if (FormatEtcIn.cfFormat = CF_VTHEADERREFERENCE) and Assigned(FHeader) then + 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.PunkForRelease := nil; + Exit(S_OK); + end; // if CF_VTHEADERREFERENCE + + // The tree reference format is always supported and returned from here. - if FormatEtcIn.cfFormat = CF_VTREFERENCE then + if (FormatEtcIn.cfFormat = CF_VTREFERENCE) and Assigned(FOWner) 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. @@ -405,31 +427,30 @@ function TVTDataObject.GetData(const FormatEtcIn : TFormatEtc; out Medium : TStg GlobalUnLock(Medium.HGlobal); Medium.tymed := TYMED_HGLOBAL; Medium.PunkForRelease := nil; - Result := S_OK; + Exit(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 + end; // if CF_VTREFERENCE + + 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 - for I := 0 to High(FormatEtcArray) do + if EqualFormatEtc(FormatEtcIn, FormatEtcArray[I]) then begin - if EqualFormatEtc(FormatEtcIn, FormatEtcArray[I]) then - begin - if not RenderInternalOLEData(FormatEtcIn, Medium, Result) then - Result := TVTCracker(FOwner).RenderOLEData(FormatEtcIn, Medium, FForClipboard); - Break; - end; + if not RenderInternalOLEData(FormatEtcIn, Medium, Result) then + Result := TVTCracker(FOwner).RenderOLEData(FormatEtcIn, Medium, FForClipboard); + Break; end; end; - except - FillChar(Medium, SizeOf(Medium), #0); - Result := E_FAIL; + ZeroMemory(@Medium, SizeOf(Medium)); end; - end; + except + ZeroMemory(@Medium, SizeOf(Medium)); + Result := E_FAIL; + end; // try..except {$ENDIF} end; diff --git a/Source/VirtualTrees.DragImage.pas b/Source/VirtualTrees.DragImage.pas index 67601d2e..98bae842 100644 --- a/Source/VirtualTrees.DragImage.pas +++ b/Source/VirtualTrees.DragImage.pas @@ -411,8 +411,7 @@ procedure TVTDragImage.PrepareDrag(DragImage : TBitmap; ImagePosition, HotSpot : // 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.ptOffset := HotSpot; //lcl //todo: replace CopyImage. Alternatively reimplement Drag support {$ifndef INCOMPLETE_WINAPI} diff --git a/Source/VirtualTrees.DragnDrop.pas b/Source/VirtualTrees.DragnDrop.pas index 1a159a16..6fefbdb6 100644 --- a/Source/VirtualTrees.DragnDrop.pas +++ b/Source/VirtualTrees.DragnDrop.pas @@ -15,7 +15,8 @@ interface DelphiCompat , VirtualTrees.Types , VirtualTrees.BaseTree - , virtualdragmanager; + , virtualdragmanager + , VirtualTrees.Header; type TEnumFormatEtc = class(TInterfacedObject, IEnumFormatEtc) @@ -61,6 +62,7 @@ TVTDragManager = class(TInterfacedObject, IVTDragManager, IDropSource, IDropTa 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. + FHeader : TVTHeader; 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 @@ -86,6 +88,7 @@ TVTDragManager = class(TInterfacedObject, IVTDragManager, IDropSource, IDropTa function GiveFeedback(Effect: LongWord): HResult; stdcall; function QueryContinueDrag(EscapePressed: BOOL; KeyState: LongWord): HResult; stdcall; {$ENDIF} + class function GetTreeFromDataObject(const DataObject: TVTDragDataObject): TBaseVirtualTree; end; var @@ -105,6 +108,7 @@ TVTDragManager = class(TInterfacedObject, IVTDragManager, IDropSource, IDropTa implementation uses + VirtualTrees.Clipboard, VirtualTrees.DataObject; type @@ -281,11 +285,43 @@ function TVTDragManager.GetIsDropTarget : Boolean; {$ENDIF} end; +class function TVTDragManager.GetTreeFromDataObject(const DataObject: TVTDragDataObject): 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 + 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; +end; + //---------------------------------------------------------------------------------------------------------------------- function TVTDragManager.DragEnter(const DataObject : IDataObject; KeyState : LongWord; Pt : TPoint; var Effect : LongWord) : HResult; +var + Medium: TStgMedium; begin {$IFDEF EnableWinDataObject} + if not Assigned(FDropTargetHelper) then + CoCreateInstance(CLSID_DragDropHelper, nil, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, FDropTargetHelper); + FDataObject := DataObject; FIsDropTarget := True; @@ -301,8 +337,13 @@ function TVTDragManager.DragEnter(const DataObject : IDataObject; KeyState : Lon else FDropTargetHelper.DragEnter(0, DataObject, Pt, Effect); // Do not pass handle, otherwise the IDropTargetHelper will perform autoscroll. Issue #486 end; - FDragSource := TreeView.GetTreeFromDataObject(DataObject); + FDragSource := GetTreeFromDataObject(DataObject); Result := TreeView.DragEnter(KeyState, Pt, Effect); + if (DataObject.GetData(StandardOLEFormat, Medium) = S_OK) and (FDragSource = FOWner) then + begin + FHeader := FDragSource.Header; + FDRagSource := nil; + end; {$ENDIF} end; @@ -330,7 +371,13 @@ function TVTDragManager.DragOver(KeyState : LongWord; Pt : TPoint; var Effect : if Assigned(FDropTargetHelper) and FFullDragging then FDropTargetHelper.DragOver(Pt, Effect); - Result := TreeView.DragOver(FDragSource, KeyState, dsDragMove, Pt, Effect); + if Assigned(fHeader) then + begin + TreeView.Header.DragTo(Pt); + Result := NOERROR; + end + else + Result := TreeView.DragOver(FDragSource, KeyState, dsDragMove, Pt, Effect); {$ENDIF} end; @@ -342,7 +389,13 @@ function TVTDragManager.Drop(const DataObject : IDataObject; KeyState : LongWord if Assigned(FDropTargetHelper) and FFullDragging then FDropTargetHelper.Drop(DataObject, Pt, Effect); - Result := TreeView.DragDrop(DataObject, KeyState, Pt, Effect); + if Assigned(fHeader) then + begin + FHeader.ColumnDropped(Pt); + Result := NO_ERROR; + end + else + Result := TreeView.DragDrop(DataObject, KeyState, Pt, Effect); FIsDropTarget := False; FDataObject := nil; {$ENDIF} diff --git a/Source/VirtualTrees.Header.pas b/Source/VirtualTrees.Header.pas index a240e776..8beb316a 100644 --- a/Source/VirtualTrees.Header.pas +++ b/Source/VirtualTrees.Header.pas @@ -214,7 +214,6 @@ TVirtualTreeColumns = class(TCollection) 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 : Integer; function GetItem(Index : TColumnIndex) : TVirtualTreeColumn; function GetNewIndex(P : TPoint; var OldIndex : TColumnIndex) : Boolean; @@ -408,7 +407,6 @@ 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); virtual; procedure FixedAreaConstraintsChanged(Sender : TObject); function GetColumnsClass : TVirtualTreeColumnsClass; virtual; function GetOwner : TPersistent; override; @@ -440,6 +438,8 @@ TVTHeader = class(TPersistent) {$IF LCL_FullVersion >= 2010000} procedure FixDesignFontsPPI(const ADesignTimePPI: Integer); virtual; {$IFEND} + procedure ColumnDropped(const P: TPoint); + procedure DragTo(P : TPoint); function InHeader(P : TPoint) : Boolean; virtual; function InHeaderSplitterArea(P : TPoint) : Boolean; virtual; procedure Invalidate(Column : TVirtualTreeColumn; ExpandToBorder : Boolean = False; UpdateNowFlag : Boolean = False); @@ -487,7 +487,8 @@ implementation uses Generics.Defaults, Generics.Collections, - VirtualTrees, VirtualTrees.BaseTree, Math, Forms, VirtualTrees.Utils, VirtualTrees.HeaderPopup, GraphUtil; + VirtualTrees, VirtualTrees.BaseTree, Math, Forms, VirtualTrees.Utils, VirtualTrees.HeaderPopup, GraphUtil, + VirtualTrees.DataObject; type TVirtualTreeColumnsCracker = class(TVirtualTreeColumns); @@ -508,6 +509,9 @@ TVirtualTreeColumnsHelper = class helper for TVirtualTreeColumns function TreeViewControl : TBaseVirtualTreeCracker; end; +const + cMargin = 2; // the margin between text and the header rectangle + cDownOffset = 1; // the offset of the column header text whit mouse button down //----------------- TVTFixedAreaConstraints ---------------------------------------------------------------------------- @@ -1358,27 +1362,9 @@ procedure TVTHeader.DragTo(P : TPoint); {$ifndef INCOMPLETE_WINAPI} //Fix for various problems mentioned in issue 248. if NeedRepaint then - begin TBaseVirtualTreeCracker(FOwner).UpdateWindow(); - //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; {$endif} - FDragImage.DragTo(P, NeedRepaint); end; @@ -1564,9 +1550,7 @@ function TVTHeader.HandleMessage(var Message: TLMessage): Boolean; var P : TPoint; - R : TRect; I : TColumnIndex; - OldPosition : Integer; HitIndex : TColumnIndex; NewCursor : TCursor; Button : TMouseButton; @@ -1816,59 +1800,12 @@ function TVTHeader.HandleMessage(var Message: TLMessage): Boolean; //successfull dragging moves columns with TLMLButtonUp(Message) do P := Tree.ClientToScreen(Point(XPos, YPos)); - GetWindowRect(Tree.Handle, R); + ColumnDropped(P); {$ifdef DEBUG_VTV}Logger.Send([lcDrag],'Header - EndDrag / R',R);{$endif} - with FColumns do - begin {$ifdef DEBUG_VTV}Logger.Send([lcDrag],'Header - EndDrag / FDropTarget: %d FDragIndex : %d FDragIndexPosition : %d', [FDropTarget, FDragIndex, FColumns[FDragIndex].Position]);{$endif} {$ifdef DEBUG_VTV}Logger.Send([lcDrag],'Header - EndDrag / FDropBefore', FColumns.FDropBefore);{$endif} - 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 {$ifdef DEBUG_VTV}Logger.Send([lcDrag],'Header - EndDrag / FDropTargetPosition', FColumns[FDropTarget].Position);{$endif} - 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; @@ -2041,6 +1978,61 @@ function TVTHeader.HandleMessage(var Message: TLMessage): Boolean; end; end; +procedure TVTHeader.ColumnDropped(const P: TPoint); +var + R: TRect; + OldPosition: Integer; +begin + 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; + //---------------------------------------------------------------------------------------------------------------------- procedure TVTHeader.ImageListChange(Sender : TObject); @@ -2058,9 +2050,12 @@ procedure TVTHeader.PrepareDrag(P, Start : TPoint); var Image : TBitmap; - ImagePos : TPoint; + HotSpot : TPoint; + ImagePos : TPoint deprecated; DragColumn : TVirtualTreeColumn; RTLOffset : TDimension; + lDataObject: IDataObject; + lDragEffect: DWord; // The last executed drag effect, not needed here begin //Determine initial position of drag image (screen coordinates). @@ -2101,12 +2096,15 @@ procedure TVTHeader.PrepareDrag(P, Start : TPoint); { Dec(ImagePos.Y, FHeight); } + HotSpot.X := HotSpot.X - DragColumn.Left - cMargin; + HotSpot.Y := HotSpot.Y + Height - cMargin; // header is in the non-client area and so the coordinates are negative if hoRestrictDrag in FOptions then FDragImage.MoveRestriction := dmrHorizontalOnly else FDragImage.MoveRestriction := dmrNone; - FDragImage.PrepareDrag(Image, ImagePos, P, nil); + + FDragImage.PrepareDrag(Image, ImagePos, HotSpot, nil); FDragImage.ShowDragImage; finally Image.Free; @@ -5921,7 +5919,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas : TCanvas; R : TRect; con PaintRectangle := ATargetRect; // calculate text and glyph position - InflateRect(PaintRectangle, - 2, - 2); + InflateRect(PaintRectangle, - cMargin, - cMargin); DrawFormat := DT_TOP or DT_NOPREFIX; case CaptionAlignment of taLeftJustify : @@ -5938,7 +5936,7 @@ procedure TVirtualTreeColumns.PaintHeader(TargetCanvas : TCanvas; R : TRect; con // Move glyph and text one pixel to the right and down to simulate a pressed button. if IsDownIndex then begin - OffsetRect(TextRectangle, 1, 1); + OffsetRect(TextRectangle, cDownOffset, cDownOffset); Inc(GlyphPos.X); Inc(GlyphPos.Y); Inc(SortGlyphPos.X); diff --git a/Source/VirtualTrees.Types.pas b/Source/VirtualTrees.Types.pas index ccacf867..99a65d67 100644 --- a/Source/VirtualTrees.Types.pas +++ b/Source/VirtualTrees.Types.pas @@ -97,6 +97,7 @@ interface // to implement optimized moves and other back references. CFSTR_VIRTUALTREE = 'Virtual Tree Data'; CFSTR_VTREFERENCE = 'Virtual Tree Reference'; + CFSTR_VTHEADERREFERENCE = 'Virtual Tree Header Reference'; CFSTR_HTML = 'HTML Format'; CFSTR_RTF = 'Rich Text Format'; CFSTR_RTFNOOBJS = 'Rich Text Format Without Objects';