1{%MainUnit ../comctrls.pp} 2 3{****************************************************************************** 4 TTreeView 5 ****************************************************************************** 6 7 Author: Mattias Gaertner 8 9 ***************************************************************************** 10 This file is part of the Lazarus Component Library (LCL) 11 12 See the file COPYING.modifiedLGPL.txt, included in this distribution, 13 for details about the license. 14 ***************************************************************************** 15 16 Abstract: 17 TTreeView for LCL 18} 19 20 21{ $DEFINE TREEVIEW_DEBUG} 22 23const 24 TTreeNodeWithPointerStreamVersion : word = 1; 25 TTreeNodeStreamVersion : word = 2; 26 TVAutoHeightString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789jgpq|\()^'; 27 MinNodeCapacity = 10; 28 29function CompareExpandedNodes(Data1, Data2: Pointer): integer; 30var 31 Node1: TTreeNodeExpandedState; 32 Node2: TTreeNodeExpandedState; 33begin 34 Node1:=TTreeNodeExpandedState(Data1); 35 Node2:=TTreeNodeExpandedState(Data2); 36 Result:=AnsiCompareText(Node1.NodeText,Node2.NodeText); 37end; 38 39function CompareTextWithExpandedNode(Key, Data: Pointer): integer; 40var 41 NodeText: String; 42 Node: TTreeNodeExpandedState; 43begin 44 NodeText:=String(Key); 45 Node:=TTreeNodeExpandedState(Data); 46 Result:=AnsiCompareText(NodeText,Node.NodeText); 47end; 48 49procedure TreeViewError(const Msg: string); 50begin 51 raise ETreeViewError.Create(Msg); 52end; 53 54procedure TreeNodeError(const Msg: string); 55begin 56 raise ETreeNodeError.Create(Msg); 57end; 58 59procedure TreeNodeErrorFmt(const Msg: string; Format: array of const); 60begin 61 raise ETreeNodeError.CreateFmt(Msg, Format); 62end; 63 64function IndexOfNodeAtTop(NodeArray: TTreeNodeArray; Count, y: integer): integer; 65// NodeArray must be sorted via Top 66// returns index of Node with Node.Top <= y < Node[+1].Top 67var 68 l, m, r, VisibleCount: integer; 69 VisibleNodesAlloc: Boolean; 70 VisibleNodes: TTreeNodeArray; 71begin 72 if (Count = 0) or (NodeArray = nil) then 73 Exit(-1); 74 // Count the visible nodes 75 VisibleCount := 0; 76 VisibleNodesAlloc := False; 77 for l := 0 to Count-1 do 78 if NodeArray[l].Visible then 79 Inc(VisibleCount); 80 try 81 // Make a temporary array of visible nodes if there are hidden nodes 82 if VisibleCount < Count then begin 83 GetMem(VisibleNodes,SizeOf(Pointer)*VisibleCount); 84 m := 0; 85 for l := 0 to Count-1 do 86 if NodeArray[l].Visible then begin 87 VisibleNodes[m] := NodeArray[l]; 88 Inc(m); 89 end; 90 Count := VisibleCount; 91 VisibleNodesAlloc := True; 92 end 93 else 94 VisibleNodes := NodeArray; 95 // Binary search for the Y coordinate 96 l := 0; 97 r := Count - 1; 98 while (l <= r) do 99 begin 100 m := (l + r) shr 1; 101 //DebugLn(':0 [IndexOfNodeAtTop] m=',m,' y=',y,' ',NodeArray[m].Text,' NodeArray[m].Top=',NodeArray[m].Top,' NodeArray[m].BottomExpanded=',NodeArray[m].BottomExpanded); 102 if VisibleNodes[m].Top > y then 103 r := m - 1 104 else if VisibleNodes[m].BottomExpanded <= y then 105 l := m + 1 106 else 107 Exit(VisibleNodes[m].Index); 108 end; 109 Result := -1; 110 finally 111 if VisibleNodesAlloc then 112 Freemem(VisibleNodes); 113 end; 114end; 115 116// procedure for sorting a TTreeNodeArray 117procedure Sort(Nodes: TTreeNodeArray; Count: integer; 118 SortProc: TTreeNodeCompare; UpdateIndex: Boolean); 119// Sorts the nodes using merge sort and updates the sibling links 120var 121 Buffer: TTreeNodeArray; 122 i: Integer; 123 124 procedure MergeNodeArrays(Pos1, Pos2, Pos3: integer); 125 // merge two sorted arrays (result is in Src) 126 // the first array ranges Pos1..Pos2-1, the second ranges Pos2..Pos3 127 var Src1Pos,Src2Pos,DestPos,cmp,a:integer; 128 begin 129 if (Pos1>=Pos2) or (Pos2>Pos3) then exit; 130 Src1Pos:=Pos2-1; 131 Src2Pos:=Pos3; 132 DestPos:=Pos3; 133 while (Src2Pos>=Pos2) and (Src1Pos>=Pos1) do begin 134 cmp:=SortProc(Nodes[Src1Pos],Nodes[Src2Pos]); 135 if cmp>0 then begin 136 Buffer[DestPos]:=Nodes[Src1Pos]; 137 dec(Src1Pos); 138 end else begin 139 Buffer[DestPos]:=Nodes[Src2Pos]; 140 dec(Src2Pos); 141 end; 142 dec(DestPos); 143 end; 144 while Src2Pos>=Pos2 do begin 145 Buffer[DestPos]:=Nodes[Src2Pos]; 146 dec(Src2Pos); 147 dec(DestPos); 148 end; 149 for a:=DestPos+1 to Pos3 do 150 Nodes[a]:=Buffer[a]; 151 end; 152 153 procedure MergeSort(StartPos, EndPos: integer); 154 // sort Src from Position StartPos to EndPos (both included) 155 var cmp,mid:integer; 156 begin 157 if StartPos>=EndPos then begin 158 // sort one element -> very easy :) 159 end else if StartPos+1=EndPos then begin 160 // sort two elements -> quite easy :) 161 cmp:=SortProc(Nodes[StartPos],Nodes[EndPos]); 162 if cmp>0 then begin 163 Buffer[StartPos]:=Nodes[StartPos]; 164 Nodes[StartPos]:=Nodes[EndPos]; 165 Nodes[EndPos]:=Buffer[StartPos]; 166 end; 167 end else begin 168 // sort more than two elements -> Mergesort 169 mid:=(StartPos+EndPos) shr 1; 170 MergeSort(StartPos,mid); 171 MergeSort(mid+1,EndPos); 172 MergeNodeArrays(StartPos,mid+1,EndPos); 173 end; 174 end; 175 176begin 177 if Count>0 then begin 178 Buffer := GetMem(SizeOf(Pointer)*Count); 179 MergeSort(0,Count-1); 180 FreeMem(Buffer); 181 // update sibling links 182 Nodes[0].FPrevBrother := nil; 183 Nodes[Count-1].FNextBrother := nil; 184 if UpdateIndex then Nodes[0].FIndex:=0; 185 for i:= 1 to Count-1 do begin 186 Nodes[i].FPrevBrother := Nodes[i-1]; 187 Nodes[i-1].FNextBrother := Nodes[i]; 188 if UpdateIndex then Nodes[i].FIndex:=i; 189 end; 190 end; 191end; 192 193{ TTreeNodeExpandedState } 194 195function TTreeNodeExpandedState.DefaultGetNodeText(Node: TTreeNode): string; 196begin 197 Result:=Node.Text; 198end; 199 200constructor TTreeNodeExpandedState.Create(FirstTreeNode: TTreeNode; 201 const GetNodeTextEvent: TTVGetNodeText); 202begin 203 if GetNodeTextEvent<>nil then 204 FOnGetNodeText:=GetNodeTextEvent 205 else 206 FOnGetNodeText:=@DefaultGetNodeText; 207 CreateChildNodes(FirstTreeNode); 208end; 209 210constructor TTreeNodeExpandedState.Create(TreeView: TCustomTreeView; 211 const GetNodeTextEvent: TTVGetNodeText); 212begin 213 if GetNodeTextEvent<>nil then 214 FOnGetNodeText:=GetNodeTextEvent 215 else 216 FOnGetNodeText:=@DefaultGetNodeText; 217 CreateChildNodes(TreeView.Items.GetFirstNode); 218end; 219 220destructor TTreeNodeExpandedState.Destroy; 221begin 222 Clear; 223 inherited Destroy; 224end; 225 226procedure TTreeNodeExpandedState.Clear; 227begin 228 if Children<>nil then begin 229 Children.FreeAndClear; 230 FreeThenNil(Children); 231 end; 232end; 233 234procedure TTreeNodeExpandedState.CreateChildNodes(FirstTreeNode: TTreeNode); 235var 236 ChildNode: TTreeNode; 237 NewExpandedNode: TTreeNodeExpandedState; 238begin 239 Clear; 240 if (FirstTreeNode=nil) then exit; 241 if (FirstTreeNode.Parent<>nil) then 242 NodeText:=OnGetNodeText(FirstTreeNode.Parent) 243 else 244 NodeText:=''; 245 ChildNode:=FirstTreeNode; 246 while ChildNode<>nil do begin 247 if ChildNode.Expanded then begin 248 if Children=nil then Children:=TAvlTree.Create(@CompareExpandedNodes); 249 NewExpandedNode:=TTreeNodeExpandedState.Create(ChildNode.GetFirstChild,OnGetNodeText); 250 if ChildNode.GetFirstChild=nil then 251 NewExpandedNode.NodeText:=OnGetNodeText(ChildNode); 252 Children.Add(NewExpandedNode); 253 end; 254 ChildNode:=ChildNode.GetNextSibling; 255 end; 256end; 257 258procedure TTreeNodeExpandedState.Apply(FirstTreeNode: TTreeNode; CollapseToo: boolean); 259var 260 ChildNode: TTreeNode; 261 ANode: TAvlTreeNode; 262 ChildNodeText: String; 263begin 264 if Children=nil then exit; 265 ChildNode:=FirstTreeNode; 266 while ChildNode<>nil do begin 267 ChildNodeText:=OnGetNodeText(ChildNode); 268 ANode:=Children.FindKey(PChar(ChildNodeText),@CompareTextWithExpandedNode); 269 if ChildNodeText='' then 270 debugln(['TTreeNodeExpandedState.Apply ',ChildNode.GetTextPath,' ChildNodeText="',ChildNodeText,'"']); 271 if ANode<>nil then 272 ChildNode.Expanded:=true 273 else if CollapseToo then 274 ChildNode.Expanded:=false; 275 if ANode<>nil then 276 TTreeNodeExpandedState(ANode.Data).Apply(ChildNode.GetFirstChild,CollapseToo); 277 ChildNode:=ChildNode.GetNextSibling; 278 end; 279end; 280 281procedure TTreeNodeExpandedState.Apply(TreeView: TCustomTreeView; CollapseToo: boolean); 282begin 283 Apply(TreeView.Items.GetFirstNode,CollapseToo); 284end; 285 286{ TTreeNode } 287 288function TTreeNode.DefaultTreeViewSort(Node1, Node2: TTreeNode): Integer; 289begin 290 if (Node1.TreeView<>nil) and Assigned(Node1.TreeView.OnCompare) then begin 291 Result:=0; 292 Node1.TreeView.OnCompare(Node1.TreeView,Node1, Node2, Result); 293 end else 294 {$IFDEF UNIX} 295 Result := CompareStr(Node1.Text, Node2.Text); 296 {$ELSE} 297 Result := AnsiCompareStr(Node1.Text,Node2.Text); 298 {$ENDIF} 299end; 300 301constructor TTreeNode.Create(AnOwner: TTreeNodes); 302begin 303 inherited Create; 304 FNodeEffect := gdeNormal; 305 FImageIndex := -1; 306 FOverlayIndex := -1; 307 FSelectedIndex := -1; 308 FStateIndex := -1; 309 FStates := [nsVisible]; 310 FOwner := AnOwner; 311 FSubTreeCount := 1; 312 FIndex := -1; 313end; 314 315destructor TTreeNode.Destroy; 316var 317 lOwnerAccessibleObject, lAccessibleObject: TLazAccessibleObject; 318begin 319 {$IFDEF TREEVIEW_DEBUG} 320 DebugLn('[TTreeNode.Destroy] Self=',DbgS(Self),' Self.Text=',Text); 321 {$ENDIF} 322 Include(FStates,nsDeleting); 323 324 // we must trigger TCustomTreeView.OnDeletion event before 325 // unbinding. See issue #17832. 326 if Assigned(Owner) and Assigned(Owner.Owner) then 327 begin 328 Owner.Owner.Delete(Self); 329 Include(Owner.Owner.FStates, tvsScrollbarChanged);; 330 Owner.Owner.UpdateScrollbars; 331 end; 332 333 // Remove the accessibility object too 334 if Assigned(Owner) and Assigned(Owner.Owner) then 335 begin 336 lOwnerAccessibleObject := Owner.Owner.GetAccessibleObject(); 337 if lOwnerAccessibleObject<>nil then 338 begin 339 lAccessibleObject := lOwnerAccessibleObject.GetChildAccessibleObjectWithDataObject(Self); 340 if lAccessibleObject <> nil then 341 lOwnerAccessibleObject.RemoveChildAccessibleObject(lAccessibleObject); 342 end; 343 end; 344 345 // delete children 346 HasChildren := false; 347 // unbind all references 348 Unbind; 349 if Assigned(Owner) then begin 350 if FStates * [nsSelected, nsMultiSelected] <> [] then 351 Owner.FSelection.Remove(Self); 352 end; 353 354 Data := nil; 355 // free data 356 if Assigned(FItems) then 357 begin 358 FreeMem(FItems); 359 FItems := nil; 360 end; 361 inherited Destroy; 362end; 363 364function TTreeNode.GetHandle: THandle; 365begin 366 if TreeView<>nil then 367 Result := TreeView.Handle 368 else 369 Result := 0; 370end; 371 372function TTreeNode.GetParentNodeOfAbsoluteLevel( 373 TheAbsoluteLevel: integer): TTreeNode; 374var 375 i: integer; 376 l: LongInt; 377begin 378 l:=Level; 379 if (TheAbsoluteLevel > l) or (TheAbsoluteLevel < 0) then 380 Result := nil 381 else 382 begin 383 Result := Self; 384 for i := TheAbsoluteLevel to l-1 do 385 Result := Result.Parent; 386 end; 387end; 388 389function TTreeNode.GetTreeNodes: TTreeNodes; 390begin 391 if Owner is TTreeNodes then 392 Result:=TTreeNodes(Owner) 393 else 394 Result:=nil; 395end; 396 397function TTreeNode.GetTreeView: TCustomTreeView; 398begin 399 if Owner <> nil then 400 Result := Owner.Owner 401 else 402 Result := nil; 403end; 404 405function TTreeNode.GetTop: integer; 406begin 407 if TreeView <> nil then 408 TreeView.UpdateAllTops; 409 Result := FTop; 410end; 411 412function TTreeNode.GetVisible: Boolean; 413begin 414 Result:=nsVisible in FStates; 415end; 416 417function TTreeNode.HasAsParent(AValue: TTreeNode): Boolean; 418begin 419 if AValue<>nil then begin 420 if Parent=nil then Result := False 421 else if Parent=AValue then Result := True 422 else Result := Parent.HasAsParent(AValue); 423 end 424 else Result := True; 425end; 426 427procedure TTreeNode.SetText(const S: string); 428var 429 lSelfAX: TLazAccessibleObject; 430begin 431 if S=FText then exit; 432 FText := S; 433 if TreeView=nil then exit; 434 Include(TreeView.FStates,tvsMaxRightNeedsUpdate); 435 if (TreeView.SortType in [stText, stBoth]) 436 and (nsBound in FStates) then begin 437 if (Parent <> nil) then Parent.AlphaSort 438 else TreeView.AlphaSort; 439 end; 440 Update; 441 Changed(ncTextChanged); 442 // Update accessibility information 443 lSelfAX := TreeView.GetAccessibleObject.GetChildAccessibleObjectWithDataObject(Self); 444 if lSelfAX <> nil then 445 lSelfAX.AccessibleValue := S; 446end; 447 448procedure TTreeNode.SetData(AValue: Pointer); 449begin 450 if FData=AValue then exit; 451 FData := AValue; 452 if (TreeView<>nil) 453 and (TreeView.SortType in [stData, stBoth]) and Assigned(TreeView.OnCompare) 454 and (not Deleting) 455 and (nsBound in FStates) then 456 begin 457 if Parent <> nil then 458 Parent.AlphaSort 459 else 460 TreeView.AlphaSort; 461 end; 462 Changed(ncDataChanged); 463end; 464 465function TTreeNode.GetState(NodeState: TNodeState): Boolean; 466begin 467 Result:=NodeState in FStates; 468end; 469 470procedure TTreeNode.SetHeight(AValue: integer); 471begin 472 if AValue<0 then AValue:=0; 473 if AValue=FHeight then exit; 474 FHeight:=AValue; 475 if TreeView<>nil then 476 TreeView.FStates:=TreeView.FStates+[tvsScrollbarChanged,tvsTopsNeedsUpdate]; 477 Update; 478 Changed(ncHeightChanged); 479end; 480 481procedure TTreeNode.SetImageEffect(AValue: TGraphicsDrawEffect); 482begin 483 if FNodeEffect=AValue then exit; 484 FNodeEffect := AValue; 485 Update; 486 Changed(ncImageEffect); 487end; 488 489procedure TTreeNode.SetImageIndex(AValue: TImageIndex); 490begin 491 if FImageIndex=AValue then exit; 492 FImageIndex := AValue; 493 Update; 494 Changed(ncImageIndex); 495end; 496 497procedure TTreeNode.SetIndex(const AValue: Integer); 498 499 procedure RaiseMissingTreeNodes; 500 begin 501 TreeViewError('TTreeNode.SetIndex missing Owner'); 502 end; 503 504var 505 OldIndex: LongInt; 506begin 507 OldIndex:=Index; 508 if OldIndex=AValue then exit; 509 if Parent=nil then begin 510 // moving a top level node 511 if Owner=nil then RaiseMissingTreeNodes; 512 if AValue=0 then 513 MoveTo(Owner.GetFirstNode,naInsert) 514 else if AValue<OldIndex then 515 MoveTo(Owner.TopLvlItems[AValue-1],naInsertBehind) 516 else 517 MoveTo(Owner.TopLvlItems[AValue],naInsertBehind); 518 end else begin 519 // moving a normal node 520 if AValue=0 then 521 MoveTo(Parent.GetFirstChild,naInsert) 522 else if AValue<OldIndex then 523 MoveTo(Parent.Items[AValue-1],naInsertBehind) 524 else 525 MoveTo(Parent.Items[AValue],naInsertBehind); 526 end; 527end; 528 529procedure TTreeNode.SetSelectedIndex(AValue: Integer); 530begin 531 if FSelectedIndex = AValue then exit; 532 FSelectedIndex := AValue; 533 Update; 534 Changed(ncSelectedIndex); 535end; 536 537procedure TTreeNode.SetVisible(const AValue: Boolean); 538begin 539 if Visible = AValue then exit; 540 if AValue then 541 Include(FStates,nsVisible) 542 else 543 Exclude(FStates,nsVisible); 544 Selected := False; 545 if TreeView<>nil then 546 TreeView.FStates:=TreeView.FStates+[tvsScrollbarChanged,tvsTopsNeedsUpdate, 547 tvsTopItemNeedsUpdate,tvsBottomItemNeedsUpdate, 548 tvsMaxLvlNeedsUpdate,tvsMaxRightNeedsUpdate]; 549 Update; 550 Changed(ncVisibility); 551end; 552 553procedure TTreeNode.SetOverlayIndex(AValue: Integer); 554begin 555 if FOverlayIndex = AValue then exit; 556 FOverlayIndex := AValue; 557 Update; 558 Changed(ncOverlayIndex) 559end; 560 561procedure TTreeNode.SetStateIndex(AValue: Integer); 562begin 563 if FStateIndex = AValue then exit; 564 FStateIndex := AValue; 565 Update; 566 Changed(ncStateIndex); 567end; 568 569function TTreeNode.AreParentsExpandedAndVisible: Boolean; 570var 571 ANode: TTreeNode; 572begin 573 Result:=false; 574 ANode:=Parent; 575 while ANode<>nil do begin 576 if not (ANode.Expanded and ANode.Visible) then exit; 577 ANode:=ANode.Parent; 578 end; 579 Result:=true; 580end; 581 582procedure TTreeNode.BindToMultiSelected; 583var 584 TheTreeNodes: TTreeNodes; 585 CurNode: TTreeNode; 586begin 587 TheTreeNodes:=TreeNodes; 588 if TheTreeNodes=nil then exit; 589 590 // Get the first selected node of the tree 591 CurNode := TheTreeNodes.FFirstMultiSelected; 592 593 // Initialize self unbinded 594 Self.FPrevMultiSelected := nil; 595 Self.FNextMultiSelected := nil; 596 597 // If there isn't any selected node, set self as first 598 if CurNode = nil then 599 begin 600 TheTreeNodes.FFirstMultiSelected := Self; 601 TheTreeNodes.FStartMultiSelected := Self; 602 end 603 else 604 begin 605 606 // if last selected node was the previous one 607 if (TheTreeNodes.FLastMultiSelected.AbsoluteIndex+1=Self.AbsoluteIndex) and (TheTreeNodes.FLastMultiSelected.FNextMultiSelected=nil) then 608 begin 609 TheTreeNodes.FLastMultiSelected.FNextMultiSelected := Self; 610 Self.FPrevMultiSelected := TheTreeNodes.FLastMultiSelected; 611 end 612 else 613 begin 614 // if last selected node was the next one 615 if (TheTreeNodes.FLastMultiSelected.AbsoluteIndex=Self.AbsoluteIndex+1) and (TheTreeNodes.FLastMultiSelected.FPrevMultiSelected=nil) then 616 begin 617 TheTreeNodes.FLastMultiSelected.FPrevMultiSelected := Self; 618 Self.FNextMultiSelected := TheTreeNodes.FLastMultiSelected; 619 TheTreeNodes.FFirstMultiSelected := Self 620 end 621 else 622 begin 623 624 // Scan linked list of selected nodes until one has a lower absolute index or we reach the end 625 While (CurNode.GetNextMultiSelected<>Nil) and (CurNode.AbsoluteIndex<Self.AbsoluteIndex) do 626 CurNode := CurNode.GetNextMultiSelected; 627 628 // last of the list 629 if CurNode.AbsoluteIndex < Self.AbsoluteIndex then 630 begin 631 CurNode.FNextMultiSelected := Self; 632 Self.FPrevMultiSelected := CurNode; 633 end 634 else 635 // insert between two nodes 636 begin 637 Self.FPrevMultiSelected := CurNode.FPrevMultiSelected; 638 Self.FNextMultiSelected := CurNode; 639 if CurNode.FPrevMultiSelected <> nil then 640 CurNode.FPrevMultiSelected.FNextMultiSelected := Self; 641 CurNode.FPrevMultiSelected := Self; 642 end; 643 // Set self as head of the list if needed 644 if Self.FPrevMultiSelected = nil then 645 TheTreeNodes.FFirstMultiSelected := Self; 646 end; 647 648 end; 649 650 end; 651 652 // Set self as last selected node 653 TheTreeNodes.FLastMultiSelected := Self; 654end; 655 656function TTreeNode.CompareCount(CompareMe: Integer): Boolean; 657Begin 658 Result:=(CompareMe=Count); 659end; 660 661function TTreeNode.DoCanExpand(ExpandIt: Boolean): Boolean; 662begin 663 Result := False; 664 if (TreeView<>nil) and HasChildren then begin 665 if ExpandIt then 666 Result := TreeView.CanExpand(Self) 667 else 668 Result := TreeView.CanCollapse(Self); 669 end; 670end; 671 672procedure TTreeNode.DoExpand(ExpandIt: Boolean); 673begin 674//DebugLn('[TTreeNode.DoExpand] Self=',DbgS(Self),' Text=',Text, 675//' HasChildren=',HasChildren,' ExpandIt=',ExpandIt,' Expanded=',Expanded); 676 if HasChildren and (Expanded<>ExpandIt) then begin 677 if ExpandIt then 678 Include(FStates,nsExpanded) 679 else begin 680 Exclude(FStates,nsExpanded); 681 if (not Owner.KeepCollapsedNodes) then begin 682 while GetLastChild<>nil do 683 GetLastChild.Free; 684 end; 685 end; 686 if TreeView<>nil then begin 687 TreeView.FStates:=(TreeView.FStates+[tvsTopsNeedsUpdate, 688 tvsTopItemNeedsUpdate,tvsBottomItemNeedsUpdate, 689 tvsScrollbarChanged,tvsMaxRightNeedsUpdate]); 690 Update; 691 if ExpandIt then 692 TreeView.Expand(Self) 693 else 694 TreeView.Collapse(Self); 695 end; 696 end; 697end; 698 699procedure TTreeNode.ExpandItem(ExpandIt: Boolean; Recurse: Boolean); 700var 701 ANode: TTreeNode; 702begin 703 if Recurse then begin 704 ExpandItem(ExpandIt, False); 705 ANode := GetFirstChild; 706 while ANode<>nil do begin 707 ANode.ExpandItem(ExpandIt, true); 708 ANode := ANode.FNextBrother; 709 end; 710 end 711 else begin 712 if TreeView<>nil then 713 Include(TreeView.FStates,tvsManualNotify); 714 try 715 if DoCanExpand(ExpandIt) then 716 DoExpand(ExpandIt); 717 finally 718 if TreeView<>nil then 719 Exclude(TreeView.FStates,tvsManualNotify); 720 end; 721 end; 722end; 723 724procedure TTreeNode.Expand(Recurse: Boolean); 725begin 726 ExpandItem(True, Recurse); 727end; 728 729procedure TTreeNode.ExpandParents; 730var ANode: TTreeNode; 731begin 732 ANode:=Parent; 733 while ANode<>nil do begin 734 ANode.Expanded:=true; 735 ANode:=ANode.Parent; 736 end; 737end; 738 739procedure TTreeNode.FreeAllNodeData; 740var 741 i: Integer; 742begin 743 FreeAndNil(FData); 744 for i:=0 to Count-1 do 745 Items[i].FreeAllNodeData; 746end; 747 748procedure TTreeNode.Collapse(Recurse: Boolean); 749begin 750 ExpandItem(False, Recurse); 751end; 752 753function TTreeNode.GetExpanded: Boolean; 754begin 755 Result := GetState(nsExpanded); 756end; 757 758procedure TTreeNode.SetExpanded(AValue: Boolean); 759begin 760 if AValue=Expanded then exit; 761 if AValue then 762 Expand(False) 763 else 764 Collapse(False); 765end; 766 767function TTreeNode.GetSelected: Boolean; 768begin 769 Result := GetState(nsSelected); 770 if (not Result) and (tvoAllowMultiSelect in TreeView.Options) then 771 Result := GetState(nsMultiSelected); 772end; 773 774procedure TTreeNode.SetSelected(AValue: Boolean); 775var 776 MultiSelect: Boolean; 777 TV: TCustomTreeView; 778begin 779 if AValue=GetSelected then exit; 780 TV:=TreeView; 781 if AValue then 782 begin 783 Include(FStates,nsSelected); 784 if (TV<>nil) then 785 begin 786 TV.EndEditing(true); 787 MultiSelect:=tvoAllowMultiselect in TV.Options; 788 789 if not MultiSelect and Assigned(FOwner) then 790 FOwner.SelectionsChanged(Self, True); 791 792 if MultiSelect then TV.LockSelectionChangeEvent; 793 try 794 TV.Selected:=Self; 795 if TV.Selected<>Self then 796 Exclude(FStates,nsSelected); 797 if (nsSelected in FStates) and MultiSelect then 798 MultiSelected:=true; 799 finally 800 if MultiSelect then TV.UnlockSelectionChangeEvent; 801 end; 802 end; 803 end else 804 begin 805 if not MultiSelected and Assigned(FOwner) then 806 FOwner.SelectionsChanged(Self, False); 807 Exclude(FStates,nsSelected); 808 if (TV<>nil) and (TV.Selected=Self) then 809 begin 810 {$IFDEF TREEVIEW_DEBUG} 811 DebugLn('TTreeNode.SetSelected: Removing selection from Node (but it does not work): ', Text); 812 {$ENDIF} 813// TV.EndEditing(true); // Done in TV.SetSelection 814 TV.Selected:=nil; 815 Assert(TV.Selected<>Self, 'Should not happen'); 816 //if TV.Selected=Self then 817 // Include(FStates,nsSelected); 818 end; 819 end; 820 Update; 821end; 822 823function TTreeNode.GetCut: boolean; 824begin 825 Result := GetState(nsCut); 826end; 827 828procedure TTreeNode.SetCut(AValue: Boolean); 829begin 830 if AValue=Cut then exit; 831 if AValue then 832 Include(FStates,nsCut) 833 else 834 Exclude(FStates,nsCut); 835end; 836 837function TTreeNode.GetDropTarget: Boolean; 838begin 839 Result := GetState(nsDropHilited); 840end; 841 842procedure TTreeNode.SetDropTarget(AValue: Boolean); 843begin 844 if AValue then begin 845 Include(FStates,nsDropHilited); 846 if TreeView<>nil then 847 TreeView.FLastDropTarget:=Self; 848 end else begin 849 Exclude(FStates,nsDropHilited); 850 if TreeView<>nil then 851 TreeView.FLastDropTarget:=nil; 852 end; 853end; 854 855function TTreeNode.GetHasChildren: Boolean; 856begin 857 if not GetState(nsValidHasChildren) then 858 begin 859 if Owner.Owner.NodeHasChildren(Self) then 860 Include(FStates, nsHasChildren) 861 else 862 Exclude(FStates, nsHasChildren); 863 Include(FStates, nsValidHasChildren); 864 end; 865 Result := GetState(nsHasChildren); 866end; 867 868procedure TTreeNode.SetFocused(AValue: Boolean); 869begin 870 if AValue=GetFocused then exit; 871 if AValue then 872 Include(FStates,nsFocused) 873 else 874 Exclude(FStates,nsFocused); 875 Update; 876end; 877 878function TTreeNode.Bottom: integer; 879begin 880 Result := Top + Height; 881end; 882 883function TTreeNode.BottomExpanded: integer; 884var 885 Node: TTreeNode; 886begin 887 Node := GetNextVisibleSibling; 888 if Node <> nil then 889 Result := Node.Top 890 else begin 891 Node := GetLastVisibleChild; 892 if Expanded and (Node <> nil) then 893 Result := Node.BottomExpanded 894 else 895 Result := Bottom; 896 end; 897end; 898 899function TTreeNode.GetFocused: Boolean; 900begin 901 Result := GetState(nsFocused); 902end; 903 904procedure TTreeNode.SetHasChildren(AValue: Boolean); 905begin 906 if GetState(nsValidHasChildren) and (AValue=HasChildren) then exit; 907 //DebugLn('[TTreeNode.SetHasChildren] Self=',DbgS(Self),' Self.Text=',Text,' AValue=',AValue); 908 if AValue then 909 Include(FStates,nsHasChildren) 910 else begin 911 while GetLastChild<>nil do 912 GetLastChild.Free; 913 Exclude(FStates,nsHasChildren) 914 end; 915 Include(FStates, nsValidHasChildren); 916 Update; 917end; 918 919function TTreeNode.GetNextSibling: TTreeNode; 920begin 921 Result := FNextBrother; 922end; 923 924function TTreeNode.GetPrevSibling: TTreeNode; 925begin 926 Result := FPrevBrother; 927end; 928 929function TTreeNode.GetNextVisible: TTreeNode; 930begin 931 if Visible and Expanded and (GetFirstVisibleChild<>nil) then 932 Result:=GetFirstVisibleChild 933 else begin 934 Result:=Self; 935 while (Result<>nil) and (Result.GetNextVisibleSibling=nil) do 936 Result:=Result.Parent; 937 if Result<>nil then 938 Result:=Result.GetNextVisibleSibling; 939 end; 940 if (Result<>nil) and ( (not Result.Visible) or (not AreParentsExpandedAndVisible) ) then 941 Result:=nil; 942end; 943 944function TTreeNode.GetNextVisibleSibling: TTreeNode; 945begin 946 Result := Self; 947 repeat 948 Result := Result.GetNextSibling; 949 until ((Result=nil) or (Result.Visible)); 950 if (Result<>nil) and (not Result.Visible) then // Result := nil ... will be removed 951 Assert(False,'TTreeNode.GetNextVisibleSibling: (Result<>nil) and (not Result.Visible)'); 952end; 953 954function TTreeNode.GetPrevVisible: TTreeNode; 955var 956 ANode: TTreeNode; 957begin 958 Result:=GetPrevVisibleSibling; 959 if Result <> nil then begin 960 while Result.Visible and Result.Expanded do begin 961 ANode:=Result.GetLastVisibleChild; 962 if ANode=nil then break; 963 Result:=ANode; 964 end; 965 end 966 else 967 Result := Parent; 968 if (Result<>nil) and ( (not Result.Visible) or (not AreParentsExpandedAndVisible) ) then 969 Result:=nil; 970end; 971 972function TTreeNode.GetPrevVisibleSibling: TTreeNode; 973begin 974 Result := Self; 975 repeat 976 Result := Result.GetPrevSibling; 977 until ((Result=nil) or (Result.Visible)); 978 if (Result<>nil) and (not Result.Visible) then // Result := nil ... will be removed 979 Assert(False,'TTreeNode.GetPrevVisibleSibling: (Result<>nil) and (not Result.Visible)'); 980end; 981 982function TTreeNode.GetPrevExpanded: TTreeNode; 983var 984 ANode: TTreeNode; 985begin 986 Result:=GetPrevVisibleSibling; 987 if Result <> nil then begin 988 while Result.Visible and Result.Expanded do begin 989 ANode:=Result.GetLastVisibleChild; 990 if ANode=nil then break; 991 Result:=ANode; 992 end; 993 end 994 else 995 Result:=Parent; 996end; 997 998function TTreeNode.GetNextChild(AValue: TTreeNode): TTreeNode; 999begin 1000 if AValue <> nil then 1001 Result := AValue.GetNextSibling 1002 else 1003 Result := nil; 1004end; 1005 1006function TTreeNode.GetNextExpanded: TTreeNode; 1007var 1008 ANode: TTreeNode; 1009begin 1010 ANode := GetFirstVisibleChild; 1011 if Expanded and (ANode<>nil) then 1012 Result:=ANode 1013 else begin 1014 Result:=Self; 1015 while (Result<>nil) and (Result.GetNextVisibleSibling=nil) do 1016 Result:=Result.Parent; 1017 if Result<>nil then 1018 Result:=Result.GetNextVisibleSibling; 1019 end; 1020end; 1021 1022function TTreeNode.GetNextMultiSelected: TTreeNode; 1023begin 1024 Result:=FNextMultiSelected; 1025end; 1026 1027function TTreeNode.GetPrevChild(AValue: TTreeNode): TTreeNode; 1028begin 1029 if AValue <> nil then 1030 Result := AValue.GetPrevSibling 1031 else 1032 Result := nil; 1033end; 1034 1035function TTreeNode.GetPrevMultiSelected: TTreeNode; 1036begin 1037 Result:=FPrevMultiSelected; 1038end; 1039 1040function TTreeNode.GetFirstChild: TTreeNode; 1041begin 1042 if Count>0 then 1043 Result:=FItems[0] 1044 else 1045 Result:=nil; 1046end; 1047 1048function TTreeNode.GetFirstSibling: TTreeNode; 1049begin 1050 if Parent<>nil then 1051 Result:=Parent.GetFirstChild 1052 else begin 1053 Result:=Self; 1054 while Result.FPrevBrother<>nil do 1055 Result:=Result.FPrevBrother; 1056 end; 1057end; 1058 1059function TTreeNode.GetFirstVisibleChild: TTreeNode; 1060begin 1061 Result := GetFirstChild; 1062 if (Result<>nil) and (not Result.Visible) then 1063 Result := Result.GetNextVisibleSibling; 1064end; 1065 1066function TTreeNode.GetLastSibling: TTreeNode; 1067begin 1068 if Parent<>nil then 1069 Result:=Parent.GetLastChild 1070 else begin 1071 Result:=Self; 1072 while Result.FNextBrother<>nil do 1073 Result:=Result.FNextBrother; 1074 end; 1075end; 1076 1077function TTreeNode.GetLastChild: TTreeNode; 1078begin 1079 if Count > 0 then 1080 Result := FItems[Count - 1] 1081 else 1082 Result := nil; 1083end; 1084 1085function TTreeNode.GetLastVisibleChild: TTreeNode; 1086begin 1087 Result := GetLastChild; 1088 if Assigned(Result) and not Result.Visible then begin 1089 Result := Result.GetPrevVisible; 1090 if Result = Self then begin // No visible nodes found. 1091 Assert(Visible, 'TTreeNode.GetLastVisibleChild: Node is not Visible'); 1092 Result := Nil; 1093 end; 1094 end; 1095end; 1096 1097function TTreeNode.GetLastSubChild: TTreeNode; 1098var 1099 Node: TTreeNode; 1100begin 1101 Result:=GetLastChild; 1102 if Result<>nil then begin 1103 Node:=Result.GetLastSubChild; 1104 if Node<>nil then 1105 Result:=Node; 1106 end; 1107end; 1108 1109function TTreeNode.GetNext: TTreeNode; 1110begin 1111 Result:=GetFirstChild; 1112 if Result=nil then 1113 Result:=GetNextSkipChildren; 1114end; 1115 1116function TTreeNode.GetNextSkipChildren: TTreeNode; 1117begin 1118 Result:=Self; 1119 while (Result<>nil) and (Result.FNextBrother=nil) do 1120 Result:=Result.Parent; 1121 if Result<>nil then 1122 Result:=Result.FNextBrother; 1123end; 1124 1125function TTreeNode.GetPrev: TTreeNode; 1126var 1127 ANode: TTreeNode; 1128begin 1129 Result := GetPrevSibling; 1130 if Result <> nil then begin 1131 ANode := Result; 1132 repeat 1133 Result := ANode; 1134 ANode := Result.GetLastChild; 1135 until ANode = nil; 1136 end else 1137 Result := Parent; 1138end; 1139 1140function TTreeNode.GetAbsoluteIndex: Integer; 1141// - first node has index 0 1142// - the first child of a node has an index one bigger than its parent 1143// - a node without children has an index one bigger than its previous brother 1144var 1145 ANode: TTreeNode; 1146begin 1147 Result:=-1; 1148 ANode:=Self; 1149 repeat 1150 inc(Result); 1151 while ANode.FPrevBrother<>nil do begin 1152 ANode:=ANode.FPrevBrother; 1153 inc(Result,ANode.FSubTreeCount); 1154 end; 1155 ANode:=ANode.Parent; 1156 until ANode=nil; 1157end; 1158 1159function TTreeNode.GetDeleting: Boolean; 1160begin 1161 Result := nsDeleting in FStates; 1162end; 1163 1164function TTreeNode.GetHeight: integer; 1165begin 1166 if FHeight <= 0 then 1167 begin 1168 if TreeView <> nil then 1169 Result := TreeView.FDefItemHeight 1170 else 1171 Result := DefaultTreeNodeHeight; 1172 end 1173 else 1174 Result := FHeight; 1175end; 1176 1177function TTreeNode.GetIndex: Integer; 1178// returns number of previous siblings (nodes on same lvl with same parent) 1179var 1180 ANode: TTreeNode; 1181begin 1182 if self = nil then 1183 exit(-1); 1184 if FIndex>=0 then 1185 exit(FIndex); 1186 // many algorithms uses the last sibling, so we check that first for speed 1187 if (Parent<>nil) and (Parent[Parent.Count-1]=Self) then begin 1188 Result:=Parent.Count-1; 1189 FIndex:=Result; 1190 exit; 1191 end; 1192 // count previous siblings 1193 Result := -1; 1194 ANode := Self; 1195 while ANode <> nil do begin 1196 Inc(Result); 1197 if ANode.FIndex>=0 then begin 1198 inc(Result,ANode.FIndex); 1199 break; 1200 end; 1201 ANode := ANode.GetPrevSibling; 1202 end; 1203 FIndex:=Result; 1204end; 1205 1206function TTreeNode.GetItems(AnIndex: Integer): TTreeNode; 1207begin 1208 if (AnIndex<0) or (AnIndex>=Count) then 1209 TreeNodeErrorFmt(rsIndexOutOfBounds,[ClassName, AnIndex, Count-1]); 1210 Result:=FItems[AnIndex]; 1211end; 1212 1213procedure TTreeNode.SetItems(AnIndex: Integer; AValue: TTreeNode); 1214begin 1215 if (AnIndex<0) or (AnIndex>=Count) then 1216 TreeNodeErrorFmt(rsIndexOutOfBounds, [ClassName, AnIndex, Count-1]); 1217 Items[AnIndex].Assign(AValue); 1218end; 1219 1220procedure TTreeNode.SetMultiSelected(const AValue: Boolean); 1221begin 1222 if AValue=GetMultiSelected then exit; 1223 if AValue then begin 1224 if (Treeview<>nil) and (not (tvoAllowMultiselect in TreeView.Options)) then 1225 exit; 1226 if Assigned(FOwner) then 1227 FOwner.SelectionsChanged(Self, True); 1228 Include(FStates,nsMultiSelected); 1229 if TreeNodes<>nil then BindToMultiSelected; 1230 end else begin 1231 if Assigned(FOwner) then 1232 FOwner.SelectionsChanged(Self, False); 1233 Exclude(FStates,nsMultiSelected); 1234 if TreeNodes<>nil then UnbindFromMultiSelected; 1235 end; 1236 if TreeView<>nil then TreeView.InternalSelectionChanged; 1237 Update; 1238end; 1239 1240function TTreeNode.IndexOf(AValue: TTreeNode): Integer; 1241begin 1242 if (AValue = nil) or (AValue.FParent <> Self) then 1243 begin 1244 Result:=-1; 1245 exit; 1246 end; 1247 Result := AValue.GetIndex; 1248end; 1249 1250function TTreeNode.IndexOfText(const NodeText: string): Integer; 1251begin 1252 Result := Count - 1; 1253 while Result >= 0 do 1254 begin 1255 if FItems[Result].Text = NodeText then exit; 1256 dec(Result); 1257 end; 1258end; 1259 1260function TTreeNode.FindNode(const NodeText: string): TTreeNode; 1261begin 1262 Result:=GetFirstChild; 1263 while (Result<>nil) and (Result.Text<>NodeText) do 1264 Result:=Result.GetNextSibling; 1265end; 1266 1267function TTreeNode.GetTextPath: string; 1268var 1269 Node: TTreeNode; 1270begin 1271 Result := ''; 1272 Node := Self; 1273 while Assigned(Node) do 1274 begin 1275 if Result <> '' then 1276 Result := '/' + Result; 1277 Result := Node.Text + Result; 1278 Node := Node.Parent; 1279 end; 1280end; 1281 1282function TTreeNode.GetCount: Integer; 1283begin 1284 Result := FCount; 1285end; 1286 1287procedure TTreeNode.EndEdit(Cancel: Boolean); 1288var 1289 TV: TCustomTreeView; 1290begin 1291 TV:=TreeView; 1292 if TV=nil then exit; 1293 TV.EndEditing(Cancel); 1294end; 1295 1296procedure TTreeNode.Unbind; 1297// unbind from parent and neighbor siblings, but not from owner 1298var 1299 OldIndex, i: integer; 1300 HigherNode: TTreeNode; 1301 TheTreeView: TCustomTreeView; 1302begin 1303 {$IFDEF TREEVIEW_DEBUG} 1304 DebugLn('[TTreeNode.Unbind] Self=',DbgS(Self),' Self.Text=',Text); 1305 {$ENDIF} 1306 if not (nsBound in FStates) then exit; 1307 Exclude(FStates,nsBound); 1308 if Owner<>nil then dec(Owner.FCount); 1309 1310 // remove single select 1311 Selected:=false; 1312 TheTreeView:=Nil; 1313 // invalidate caches of TreeView and if root item, remove from TreeView.Items 1314 if Owner<>nil then begin 1315 Owner.ClearCache; 1316 if FParent=nil then 1317 Owner.MoveTopLvlNode(Owner.IndexOfTopLvlItem(Self),-1,Self); 1318 TheTreeView:=Owner.Owner; 1319 if TheTreeView<>nil then begin 1320 TheTreeView.FStates:=TheTreeView.FStates+[tvsMaxRightNeedsUpdate, 1321 tvsTopsNeedsUpdate,tvsTopItemNeedsUpdate,tvsBottomItemNeedsUpdate]; 1322 if TheTreeView.FLastDropTarget=Self then 1323 TheTreeView.FLastDropTarget:=nil; 1324 if TheTreeView.FInsertMarkNode=Self then 1325 TheTreeView.FInsertMarkNode:=nil; 1326 end; 1327 end; 1328 // unmultiselect (keeping MultiSelected, but leaving multiselection list) 1329 UnbindFromMultiSelected; 1330 // remove from sibling list 1331 if FPrevBrother<>nil then FPrevBrother.FNextBrother:=FNextBrother; 1332 if FNextBrother<>nil then FNextBrother.FPrevBrother:=FPrevBrother; 1333 FPrevBrother:=nil; 1334 FNextBrother:=nil; 1335 // remove from parent 1336 if FParent<>nil then begin 1337 // update all FSubTreeCount 1338 HigherNode:=FParent; 1339 while HigherNode<>nil do begin 1340 dec(HigherNode.FSubTreeCount,FSubTreeCount); 1341 HigherNode:=HigherNode.Parent; 1342 end; 1343 //if TreeNodes<>nil then Dec(TreeNodes.FCount,FSubTreeCount); 1344 // remove from parents list 1345 OldIndex:=FIndex; 1346 if OldIndex<0 then 1347 RaiseGDBException(''); 1348 for i:=OldIndex to FParent.FCount-2 do begin 1349 FParent.FItems[i]:=FParent.FItems[i+1]; 1350 FParent.FItems[i].FIndex:=i; 1351 end; 1352 dec(FParent.FCount); 1353 if (FParent.FCapacity>15) and (FParent.FCount<(FParent.FCapacity shr 2)) 1354 then begin 1355 // shrink FParent.FItems 1356 FParent.FCapacity:=FParent.FCapacity shr 1; 1357 ReAllocMem(FParent.FItems,SizeOf(Pointer)*FParent.FCapacity); 1358 end; 1359 if FParent.Count=0 then begin 1360 Assert(Assigned(TheTreeView), 'TTreeNode.Unbind: TheTreeView=Nil.'); 1361 TheTreeView.BeginUpdate; 1362 try 1363 FParent.Expanded:=false; 1364 FParent.HasChildren:=false; 1365 finally 1366 TheTreeView.EndUpdate; 1367 end; 1368 end; 1369 FParent:=nil; 1370 end; 1371end; 1372 1373procedure TTreeNode.UnbindFromMultiSelected; 1374var 1375 TheTreeNodes: TTreeNodes; 1376begin 1377 TheTreeNodes:=TreeNodes; 1378 if TheTreeNodes=nil then exit; 1379 if TheTreeNodes.FFirstMultiSelected=Self then 1380 TheTreeNodes.FFirstMultiSelected:=FNextMultiSelected; 1381 // Reset last multiselected node 1382 if TheTreeNodes.FLastMultiSelected=Self then 1383 begin 1384 if Self.FNextMultiSelected <> nil then 1385 TheTreeNodes.FLastMultiSelected := Self.FNextMultiSelected 1386 else 1387 if Self.FPrevMultiSelected <> nil then 1388 TheTreeNodes.FLastMultiSelected := Self.FPrevMultiSelected 1389 else 1390 TheTreeNodes.FLastMultiSelected := nil; 1391 end; 1392 if FNextMultiSelected<>nil then 1393 FNextMultiSelected.FPrevMultiSelected:=FPrevMultiSelected; 1394 if FPrevMultiSelected<>nil then 1395 FPrevMultiSelected.FNextMultiSelected:=FNextMultiSelected; 1396 FNextMultiSelected:=nil; 1397 FPrevMultiSelected:=nil; 1398end; 1399 1400function AddModeStr(AddMode: TAddMode): string; 1401begin 1402 WriteStr(Result, AddMode); 1403end; 1404 1405procedure TTreeNode.InternalMove(ANode: TTreeNode; AddMode: TAddMode); 1406{ 1407 TAddMode = (taAddFirst, taAdd, taInsert); 1408 1409 taAdd: add Self as last child of ANode 1410 taAddFirst: add Self as first child of ANode 1411 taInsert: add Self in front of ANode 1412} 1413var 1414 HigherNode: TTreeNode; 1415 NewIndex, NewParentItemSize, i: integer; 1416 WasSelected: Boolean; 1417begin 1418 {$IFDEF TREEVIEW_DEBUG} 1419 DbgOut('[TTreeNode.InternalMove] Self=',DbgS(Self),' Self.Text=',Text 1420 ,' ANode=',ANode<>nil,' AddMode=', AddModeStr(AddMode)); 1421 if ANode<>nil then 1422 DbgOut(' ANode.Text=',ANode.Text); 1423 DebugLn(''); 1424 {$ENDIF} 1425 if TreeView<>nil then 1426 TreeView.BeginUpdate; 1427 try 1428 WasSelected:=Selected; 1429 Unbind; 1430 if Owner<>nil then 1431 Owner.ClearCache; 1432 Include(FStates,nsBound); 1433 if Owner<>nil then inc(Owner.FCount); 1434 // set parent 1435 if AddMode in [taAdd, taAddFirst] then 1436 FParent:=ANode 1437 else begin // taInsert 1438 if (ANode=nil) then 1439 TreeNodeError('TTreeNode.InternalMove AddMode=taInsert but ANode=nil'); 1440 FParent:=ANode.Parent; 1441 FPrevBrother:=ANode.FPrevBrother; 1442 FNextBrother:=ANode; 1443 end; 1444 if FParent<>nil then begin 1445 FParent.HasChildren:=true; 1446 if (FParent.FCount=FParent.FCapacity) then begin 1447 // grow FParent.FItems 1448 if FParent.FCapacity=0 then 1449 FParent.FCapacity:=5 1450 else 1451 FParent.FCapacity:=FParent.FCapacity shl 1; 1452 NewParentItemSize:=SizeOf(Pointer)*FParent.FCapacity; 1453 if FParent.FItems=nil then 1454 GetMem(FParent.FItems,NewParentItemSize) 1455 else 1456 ReAllocMem(FParent.FItems,NewParentItemSize); 1457 end; 1458 inc(FParent.FCount); 1459 // calculate new Index 1460 case AddMode of 1461 taAdd: NewIndex:=FParent.Count-1; 1462 taAddFirst: NewIndex:=0; 1463 else 1464 // taInsert 1465 NewIndex:=ANode.Index; 1466 end; 1467 // move next siblings 1468 for i:=FParent.FCount-1 downto NewIndex+1 do begin 1469 FParent.FItems[i]:=FParent.FItems[i-1]; 1470 FParent.FItems[i].FIndex:=i; 1471 end; 1472 // insert this node to parent's items 1473 FParent.FItems[NewIndex]:=Self; 1474 FIndex:=NewIndex; 1475 // set Next and Prev sibling 1476 if NewIndex>0 then 1477 FPrevBrother:=FParent.FItems[NewIndex-1] 1478 else 1479 FPrevBrother:=nil; 1480 if NewIndex<FParent.Count-1 then 1481 FNextBrother:=FParent.FItems[NewIndex+1] 1482 else 1483 FNextBrother:=nil; 1484 // update total node count of all parents 1485 HigherNode:=FParent; 1486 while HigherNode<>nil do begin 1487 inc(HigherNode.FSubTreeCount,FSubTreeCount); 1488 HigherNode:=HigherNode.Parent; 1489 end; 1490 //if TreeNodes<>nil then inc(TreeNodes.FCount,FSubTreeCount); 1491 end else begin 1492 // add as top level node 1493 case AddMode of 1494 taAdd: 1495 begin 1496 // add as last top level node 1497 if Owner<>nil then begin 1498 FPrevBrother:=Owner.GetLastNode; 1499 Owner.MoveTopLvlNode(-1,Owner.FTopLvlCount,Self); 1500 end; 1501 end; 1502 taAddFirst: 1503 begin 1504 // add as first top level node = root node 1505 if Owner<>nil then begin 1506 FNextBrother:=Owner.GetFirstNode; 1507 Owner.MoveTopLvlNode(-1,0,Self); 1508 end; 1509 end; 1510 taInsert: 1511 begin 1512 // insert node in front of ANode 1513 //DebugLn('[TTreeNode.InternalMove] ANode.Index=',ANode.Index,' ANode=',DbgS(ANode)); 1514 FNextBrother:=ANode; 1515 FPrevBrother:=ANode.GetPrevSibling; 1516 if Owner<>nil then begin 1517 Owner.MoveTopLvlNode(-1,ANode.Index,Self); 1518 end; 1519 end; 1520 end; 1521 end; 1522 // connect Next and Prev sibling 1523 if FPrevBrother<>nil then FPrevBrother.FNextBrother:=Self; 1524 if FNextBrother<>nil then FNextBrother.FPrevBrother:=Self; 1525 if Owner.Owner<>nil then 1526 Owner.Owner.FStates:=Owner.Owner.FStates+[tvsMaxRightNeedsUpdate, 1527 tvsTopsNeedsUpdate,tvsTopItemNeedsUpdate,tvsBottomItemNeedsUpdate]; 1528 // re-add to multiselection list 1529 if MultiSelected then 1530 BindToMultiSelected; 1531 if WasSelected then 1532 Selected:=true; 1533 finally 1534 if TreeView<>nil then TreeView.EndUpdate; 1535 end; 1536 1537 {$IFDEF TREEVIEW_DEBUG} 1538 DbgOut('[TTreeNode.InternalMove] END Self=',DbgS(Self),' Self.Text=',Text 1539 ,' ANode=',DbgS(ANode<>nil),' AddMode=',AddModeStr(AddMode)); 1540 if ANode<>nil then 1541 DbgOut(' ANode.Text=',ANode.Text); 1542 DebugLn(''); 1543 {$ENDIF} 1544end; 1545 1546procedure TTreeNode.MoveTo(Destination: TTreeNode; Mode: TNodeAttachMode); 1547{ 1548 TNodeAttachMode = (naAdd, naAddFirst, naAddChild, naAddChildFirst, naInsert); 1549 1550 naAdd: add as last sibling of Destination 1551 naAddFirst: add as first sibling of Destnation 1552 naAddChild: add as last child of Destination 1553 naAddChildFirst: add as first child of Destination 1554 naInsert: insert in front of Destination 1555 naInsertBehind: insert behind Destination 1556} 1557var 1558 AddMode: TAddMode; 1559 //ANode: TTreeNode; 1560 //HItem: HTreeItem; 1561 OldOnChanging: TTVChangingEvent; 1562 OldOnChange: TTVChangedEvent; 1563begin 1564 if (Destination=nil) and not(Mode in [naAdd,naAddFirst]) then 1565 TreeNodeError('TTreeNode.MoveTo Destination=nil'); 1566 if Mode=naInsertBehind then begin // convert naInsertBehind 1567 if Destination.GetNextSibling=nil then 1568 Mode:=naAdd 1569 else begin 1570 Mode:=naInsert; 1571 Destination:=Destination.GetNextSibling; 1572 end; 1573 end; 1574 if (Destination = nil) or not Destination.HasAsParent(Self) then begin 1575 OldOnChanging := TreeView.OnChanging; 1576 OldOnChange := TreeView.OnChange; 1577 TreeView.OnChanging := nil; 1578 TreeView.OnChange := nil; 1579 try 1580 if (Destination <> nil) and (Mode in [naAdd, naAddFirst]) then 1581 Destination := Destination.Parent; 1582 case Mode of 1583 naAddFirst, 1584 naAddChildFirst: AddMode := taAddFirst; 1585 naInsert: AddMode := taInsert; 1586 else 1587 AddMode:=taAdd; 1588 end; 1589 if (Destination <> Self) then 1590 InternalMove(Destination, AddMode); 1591 finally 1592 TreeView.OnChanging := OldOnChanging; 1593 TreeView.OnChange := OldOnChange; 1594 end; 1595 end; 1596 Changed(ncParentChanged) 1597end; 1598 1599procedure TTreeNode.MultiSelectGroup; 1600var 1601 FirstNode, LastNode, ANode: TTreeNode; 1602begin 1603 if Assigned(TreeView) and not (tvoAllowMultiselect in TreeView.Options) then 1604 Exit; 1605 if Assigned(TreeView) then TreeView.LockSelectionChangeEvent; 1606 try 1607 // We need to select the nodes between the selected node and the current node 1608 FirstNode := GetPrevSibling; 1609 while Assigned(FirstNode) and not FirstNode.Selected do 1610 FirstNode := FirstNode.GetPrevSibling; 1611 if not Assigned(FirstNode) then FirstNode := Self; 1612 LastNode := GetNextSibling; 1613 while Assigned(LastNode) and not LastNode.Selected do 1614 LastNode := LastNode.GetNextSibling; 1615 if not Assigned(LastNode) then LastNode := Self; 1616 ANode := FirstNode; 1617 while Assigned(ANode) do 1618 begin 1619 ANode.MultiSelected := True; 1620 if ANode = LastNode then Break; 1621 ANode := ANode.GetNextSibling; 1622 end; 1623 finally 1624 if Assigned(TreeView) then TreeView.UnlockSelectionChangeEvent; 1625 end; 1626end; 1627 1628procedure TTreeNode.MakeVisible; 1629begin 1630 if Assigned(TreeView) then 1631 TreeView.EnsureNodeIsVisible(Self) 1632 else 1633 ExpandParents; 1634end; 1635 1636function TTreeNode.GetLevel: Integer; 1637// root is on level 0 1638var 1639 ANode: TTreeNode; 1640begin 1641 Result := 0; 1642 ANode := Parent; 1643 while Assigned(ANode) do 1644 begin 1645 Inc(Result); 1646 ANode := ANode.Parent; 1647 end; 1648end; 1649 1650function TTreeNode.GetMultiSelected: Boolean; 1651begin 1652 Result := GetState(nsMultiSelected); 1653end; 1654 1655function TTreeNode.IsNodeVisible: Boolean; 1656begin 1657 if Assigned(TreeView) then 1658 Result := TreeView.IsNodeVisible(Self) 1659 else 1660 Result := AreParentsExpandedAndVisible; 1661end; 1662 1663function TTreeNode.IsNodeHeightFullVisible: Boolean; 1664begin 1665 if Assigned(TreeView) then 1666 Result := TreeView.IsNodeHeightFullVisible(Self) 1667 else 1668 Result := AreParentsExpandedAndVisible; 1669end; 1670 1671procedure TTreeNode.Update; 1672var 1673 TV: TCustomTreeView; 1674begin 1675 TV := TreeView; 1676 if Assigned(TV) and (Owner.FUpdateCount = 0) and (not (csLoading in TV.ComponentState)) then 1677 TV.Invalidate; 1678end; 1679 1680function TTreeNode.EditText: Boolean; 1681var 1682 TV: TCustomTreeView; 1683begin 1684 TV := TreeView; 1685 Result := Assigned(TV) and (tvsIsEditing in TreeView.FStates); 1686 TV.BeginEditing(Self); 1687end; 1688 1689function TTreeNode.DisplayRect(TextOnly: Boolean): TRect; 1690begin 1691 FillChar(Result, SizeOf(Result), 0); 1692 if TreeView <> nil then 1693 begin 1694 Result.Left := TreeView.BorderWidth; 1695 Result.Top := Top - TreeView.ScrolledTop + TreeView.BorderWidth; 1696 Result.Right := TreeView.ClientWidth - TreeView.BorderWidth; 1697 Result.Bottom := Result.Top + Height; 1698 if TextOnly then 1699 begin 1700 Result.Left := DisplayTextLeft; 1701 if Result.Left > Result.Right then 1702 Result.Left := Result.Right; 1703 Result.Right := DisplayTextRight; 1704 if Result.Right < Result.Left then 1705 Result.Right := Result.Left; 1706 end; 1707 end; 1708end; 1709 1710function TTreeNode.DisplayExpandSignLeft: integer; 1711var 1712 TV: TCustomTreeView; 1713 l: LongInt; 1714 RealIndent: Integer; 1715begin 1716 Result := 0; 1717 TV := TreeView; 1718 RealIndent := TV.Indent; 1719 if TV = nil then Exit; 1720 l := Level; 1721 if not (tvoShowRoot in TV.Options) then 1722 inc(Result, RealIndent * (l - 1) + (RealIndent shr 2) + TV.BorderWidth - TV.FScrolledLeft) 1723 else 1724 inc(Result, RealIndent * l + TV.BorderWidth - TV.FScrolledLeft); 1725end; 1726 1727function TTreeNode.DisplayExpandSignRect: TRect; 1728begin 1729 FillChar(Result, SizeOf(Result), 0); 1730 if TreeView <> nil then 1731 begin 1732 Result.Left := DisplayExpandSignLeft; 1733 Result.Top := Top; 1734 Result.Right := Result.Left + TreeView.Indent; 1735 Result.Bottom := Top + Height; 1736 end; 1737end; 1738 1739function TTreeNode.DisplayExpandSignRight: integer; 1740begin 1741 Result := DisplayExpandSignLeft; 1742 if TreeView <> nil then 1743 inc(Result, TreeView.Indent); 1744end; 1745 1746function TTreeNode.DisplayIconLeft: integer; 1747var 1748 TV: TCustomTreeView; 1749begin 1750 Result := DisplayStateIconLeft; 1751 TV := TreeView; 1752 if (TV = nil) or (TV.StateImages = nil) then Exit; 1753 if (StateIndex < 0) or (StateIndex >= TV.StateImages.Count) then Exit; 1754 Inc(Result, TV.StateImages.WidthForPPI[TV.StateImagesWidth, TV.Font.PixelsPerInch] + TV.FDefItemSpace); 1755end; 1756 1757function TTreeNode.DisplayStateIconLeft: integer; 1758begin 1759 Result := DisplayExpandSignRight; 1760end; 1761 1762function TTreeNode.DisplayTextLeft: integer; 1763var 1764 TV: TCustomTreeView; 1765 ImgIndex: TImageIndex; 1766 sz: TSize; 1767begin 1768 Result := DisplayIconLeft; 1769 TV := TreeView; 1770 if TV = nil then 1771 exit; 1772 sz := TV.GetImageSize; 1773 if (TV.Images = nil) then 1774 begin 1775 inc(Result, sz.CX); 1776 exit; 1777 end; 1778 if (TV.Selected = Self) then 1779 ImgIndex:=SelectedIndex 1780 else 1781 ImgIndex:=ImageIndex; 1782 if (ImgIndex<0) or (ImgIndex>=TV.Images.Count) then Exit; 1783 Inc(Result, sz.CX + TV.FDefItemSpace); 1784end; 1785 1786function TTreeNode.DisplayTextRight: integer; 1787var 1788 TV: TCustomTreeView; 1789begin 1790 Result := DisplayTextLeft; 1791 TV := TreeView; 1792 if TV <> nil then 1793 Inc(Result, TV.Canvas.TextWidth(Text) + TV.Indent div 2); 1794end; 1795 1796function TTreeNode.AlphaSort: Boolean; 1797begin 1798 Result := CustomSort(nil); 1799end; 1800 1801function TTreeNode.CustomSort(SortProc: TTreeNodeCompare): Boolean; 1802begin 1803 if FCount>0 then begin 1804 if Owner<>nil then Owner.ClearCache; 1805 if not Assigned(SortProc) then SortProc:=@DefaultTreeViewSort; 1806 Sort(FItems, FCount, SortProc, true); 1807 end; 1808 if (TreeView <> nil) then 1809 Include(TreeView.FStates, tvsTopsNeedsUpdate); 1810 Result:=true; 1811end; 1812 1813procedure TTreeNode.Delete; 1814begin 1815 if not Deleting then Free; 1816end; 1817 1818procedure TTreeNode.DeleteChildren; 1819begin 1820 if Owner<>nil then Owner.ClearCache; 1821 Collapse(true); 1822 HasChildren := False; 1823end; 1824 1825procedure TTreeNode.Assign(Source: TPersistent); 1826var 1827 ANode: TTreeNode; 1828begin 1829 if Owner<>nil then Owner.ClearCache; 1830 if Source is TTreeNode then 1831 begin 1832 ANode := TTreeNode(Source); 1833 Text := ANode.Text; 1834 Data := ANode.Data; 1835 ImageIndex := ANode.ImageIndex; 1836 SelectedIndex := ANode.SelectedIndex; 1837 StateIndex := ANode.StateIndex; 1838 OverlayIndex := ANode.OverlayIndex; 1839 Height := ANode.Height; 1840 Focused := ANode.Focused; 1841 //DropTarget := ANode.DropTarget; 1842 Cut := ANode.Cut; 1843 HasChildren := ANode.HasChildren; 1844 end 1845 else inherited Assign(Source); 1846end; 1847 1848function TTreeNode.IsEqual(Node: TTreeNode): Boolean; 1849begin 1850 Result := (Text = Node.Text) and (Data = Node.Data); 1851end; 1852 1853procedure TTreeNode.ReadData(Stream: TStream; StreamVersion: integer); 1854var 1855 I, ItemCount: Integer; 1856 NewExpanded: boolean; 1857 OldInfo: TOldTreeNodeInfo; 1858 Info: TTreeNodeInfo; 1859 Node: TTreeNode; 1860 lSelfAX: TLazAccessibleObject; 1861begin 1862 if Owner<>nil then Owner.ClearCache; 1863 if StreamVersion=TTreeNodeWithPointerStreamVersion then 1864 begin 1865 Stream.ReadBuffer(OldInfo, SizeOf(TOldTreeNodeInfo)); 1866 ImageIndex := OldInfo.ImageIndex; 1867 SelectedIndex := OldInfo.SelectedIndex; 1868 StateIndex := OldInfo.StateIndex; 1869 OverlayIndex := OldInfo.OverlayIndex; 1870 Data := Pointer(OldInfo.Data); 1871 Height := OldInfo.Height; 1872 NewExpanded := OldInfo.Expanded; 1873 ItemCount := OldInfo.Count; 1874 SetLength(FText,OldInfo.TextLen) 1875 end 1876 else 1877 begin 1878 Stream.ReadBuffer(Info, SizeOf(TTreeNodeInfo)); 1879 ImageIndex := Info.ImageIndex; 1880 SelectedIndex := Info.SelectedIndex; 1881 StateIndex := Info.StateIndex; 1882 OverlayIndex := Info.OverlayIndex; 1883 Height := Info.Height; 1884 NewExpanded := Info.Expanded; 1885 ItemCount := Info.Count; 1886 SetLength(FText,Info.TextLen); 1887 end; 1888 if FText<>'' then 1889 begin 1890 Stream.Read(FText[1],length(FText)); 1891 // Update accessibility information 1892 if TreeView<>nil then 1893 begin 1894 lSelfAX := TreeView.GetAccessibleObject.GetChildAccessibleObjectWithDataObject(Self); 1895 if lSelfAX <> nil then 1896 lSelfAX.AccessibleValue := FText; 1897 end; 1898 end; 1899 if Owner<>nil then begin 1900 for I := 0 to ItemCount - 1 do begin 1901 Node:=Owner.AddChild(Self, ''); 1902 Node.ReadData(Stream, StreamVersion); 1903 Owner.Owner.Added(Node); 1904 end; 1905 end; 1906 Expanded := NewExpanded; 1907end; 1908 1909procedure TTreeNode.ReadDelphiData(Stream: TStream; Info: PDelphiNodeInfo); 1910var 1911 I, Size, ItemCount: Integer; 1912begin 1913 if Owner<>nil then Owner.ClearCache; 1914 Stream.ReadBuffer(Size, SizeOf(Size)); 1915 Stream.ReadBuffer(Info^, Size); 1916 Text := Info^.Text; 1917 ImageIndex := Info^.ImageIndex; 1918 SelectedIndex := Info^.SelectedIndex; 1919 StateIndex := Info^.StateIndex; 1920 OverlayIndex := Info^.OverlayIndex; 1921 Data := {%H-}Pointer(Info^.Data); 1922 if Owner<>nil then begin 1923 ItemCount := Info^.Count; 1924 for I := 0 to ItemCount - 1 do 1925 Owner.AddChild(Self, '').ReadDelphiData(Stream, Info); 1926 end; 1927end; 1928 1929procedure TTreeNode.WriteData(Stream: TStream; StreamVersion: integer); 1930var 1931 i: integer; 1932 OldInfo: TOldTreeNodeInfo; 1933 Info: TTreeNodeInfo; 1934begin 1935 if StreamVersion=TTreeNodeWithPointerStreamVersion then 1936 begin 1937 OldInfo.ImageIndex := ImageIndex; 1938 OldInfo.SelectedIndex := SelectedIndex; 1939 OldInfo.OverlayIndex := OverlayIndex; 1940 OldInfo.StateIndex := StateIndex; 1941 OldInfo.Height := FHeight; 1942 OldInfo.Data := PtrUInt(Data); 1943 OldInfo.Count := Count; 1944 OldInfo.Expanded := Expanded; 1945 OldInfo.TextLen := Length(Text); 1946 Stream.WriteBuffer(OldInfo, SizeOf(TOldTreeNodeInfo)); 1947 end 1948 else 1949 begin 1950 Info.ImageIndex := ImageIndex; 1951 Info.SelectedIndex := SelectedIndex; 1952 Info.OverlayIndex := OverlayIndex; 1953 Info.StateIndex := StateIndex; 1954 Info.Height := FHeight; 1955 Info.Count := Count; 1956 Info.Expanded := Expanded; 1957 Info.TextLen := Length(Text); 1958 Stream.WriteBuffer(Info, SizeOf(TTreeNodeInfo)); 1959 end; 1960 if Text<>'' then 1961 Stream.Write(FText[1],length(Text)); 1962 for i := 0 to Count - 1 do 1963 Items[i].WriteData(Stream, StreamVersion); 1964end; 1965 1966procedure TTreeNode.WriteDelphiData(Stream: TStream; Info: PDelphiNodeInfo); 1967var 1968 I, Size, L, ItemCount: Integer; 1969begin 1970 L := Length(Text); 1971 if L > 255 then L := 255; 1972 Size := SizeOf(TDelphiNodeInfo) + L - 255; 1973 Info^.Text := Text; 1974 Info^.ImageIndex := ImageIndex; 1975 Info^.SelectedIndex := SelectedIndex; 1976 Info^.OverlayIndex := OverlayIndex; 1977 Info^.StateIndex := StateIndex; 1978 Info^.Data := {%H-}Cardinal(Data); 1979 ItemCount := Count; 1980 Info^.Count := ItemCount; 1981 Stream.WriteBuffer(Size, SizeOf(Size)); 1982 Stream.WriteBuffer(Info^, Size); 1983 for I := 0 to ItemCount - 1 do 1984 Items[I].WriteDelphiData(Stream, Info); 1985end; 1986 1987procedure TTreeNode.Changed(ChangeReason: TTreeNodeChangeReason); 1988begin 1989 TreeView.NodeChanged(self,ChangeReason); 1990end; 1991 1992function TTreeNode.GetOwner: TPersistent; 1993begin 1994 Result := FOwner; 1995end; 1996 1997procedure TTreeNode.ConsistencyCheck; 1998var 1999 RealSubTreeCount: integer; 2000 i: integer; 2001 Node1: TTreeNode; 2002begin 2003 if FCapacity<0 then 2004 RaiseGDBException(''); 2005 if FCapacity<FCount then 2006 RaiseGDBException(''); 2007 if FCount<0 then 2008 RaiseGDBException(''); 2009 if FHeight<0 then 2010 RaiseGDBException(''); 2011 if (FItems<>nil) and (FCapacity<=0) then 2012 RaiseGDBException(''); 2013 if (FCapacity>0) and (FItems=nil) then 2014 RaiseGDBException(''); 2015 if (FNextBrother<>nil) and (FNextBrother.FPrevBrother<>Self) then 2016 RaiseGDBException(''); 2017 if (FPrevBrother<>nil) and (FPrevBrother.FNextBrother<>Self) then 2018 RaiseGDBException(''); 2019 if (FNextMultiSelected<>nil) and (FNextMultiSelected.FPrevMultiSelected<>Self) then 2020 RaiseGDBException(''); 2021 if (FPrevMultiSelected<>nil) and (FPrevMultiSelected.FNextMultiSelected<>Self) then 2022 RaiseGDBException(''); 2023 if MultiSelected then begin 2024 Node1:=TreeView.GetFirstMultiSelected; 2025 while (Node1<>nil) and (Node1<>Self) do Node1:=Node1.FNextMultiSelected; 2026 if Node1=nil then 2027 RaiseGDBException(''); 2028 end; 2029 if Selected and (TreeView<>nil) and (tvoAllowMultiselect in TreeView.Options) 2030 and (not MultiSelected) then 2031 RaiseGDBException('');// selected, but not multiselected 2032 2033 // check children 2034 RealSubTreeCount:=1; 2035 for i:=0 to FCount-1 do begin 2036 if (Items[i]=nil) then RaiseGDBException(''); 2037 Node1:=Items[i]; 2038 if Node1.FParent<>Self then RaiseGDBException(''); 2039 if (i=0) and (Node1.FPrevBrother<>nil) then 2040 RaiseGDBException(''); 2041 if (i>0) and (Node1.FPrevBrother=nil) then 2042 RaiseGDBException(''); 2043 if (i>0) and (Node1.FPrevBrother<>Items[i-1]) then 2044 RaiseGDBException(''); 2045 if (i<FCount-1) and (Node1.FNextBrother=nil) then 2046 RaiseGDBException(''); 2047 if (i<FCount-1) and (Node1.FNextBrother<>Items[i+1]) then 2048 RaiseGDBException(''); 2049 if (i=FCount-1) and (Node1.FNextBrother<>nil) then 2050 RaiseGDBException(''); 2051 if Node1.FIndex<>i then 2052 RaiseGDBException(''); 2053 Node1.ConsistencyCheck; 2054 inc(RealSubTreeCount,Node1.SubTreeCount); 2055 end; 2056 if FParent<>nil then begin 2057 if FParent.IndexOf(Self)<0 then RaiseGDBException(''); 2058 end; 2059 if RealSubTreeCount<>SubTreeCount then RaiseGDBException(''); 2060 if FTop<0 then RaiseGDBException(''); 2061 // check for circles 2062 if FNextBrother=Self then RaiseGDBException(''); 2063 if FPrevBrother=Self then RaiseGDBException(''); 2064 if FParent=Self then RaiseGDBException(''); 2065 Node1:=FParent; 2066 while Node1<>nil do begin 2067 if (Node1=Self) then RaiseGDBException(''); 2068 Node1:=Node1.FParent; 2069 end; 2070end; 2071 2072procedure TTreeNode.WriteDebugReport(const Prefix: string; Recurse: boolean); 2073var i: integer; 2074begin 2075 DbgOut('%s%s.WriteDebugReport Self=%p',[Prefix, ClassName, Pointer(Self)]); 2076 ConsistencyCheck; 2077 DebugLn(' Text=',Text); 2078 if Recurse then begin 2079 for i:=0 to FCount-1 do 2080 Items[i].WriteDebugReport(Prefix+' ',true); 2081 end; 2082end; 2083 2084 2085{ TTreeNodes } 2086 2087constructor TTreeNodes.Create(AnOwner: TCustomTreeView); 2088begin 2089 inherited Create; 2090 FSelection := TFPList.Create; 2091 FOwner := AnOwner; 2092end; 2093 2094destructor TTreeNodes.Destroy; 2095begin 2096 Clear; 2097 FreeThenNil(FSelection); 2098 inherited Destroy; 2099end; 2100 2101function TTreeNodes.GetCount: Integer; 2102begin 2103 Result:=FCount; 2104end; 2105 2106function TTreeNodes.GetOwner: TPersistent; 2107begin 2108 Result := FOwner; 2109end; 2110 2111function TTreeNodes.GetHandle: THandle; 2112begin 2113 if Owner<>nil then 2114 Result:=Owner.Handle 2115 else 2116 Result:=0; 2117end; 2118 2119procedure TTreeNodes.Delete(Node: TTreeNode); 2120begin 2121 Node.Delete; 2122 if (FUpdateCount=0) and (Owner<>nil) then 2123 Owner.Invalidate; 2124end; 2125 2126procedure TTreeNodes.Clear; 2127var 2128 Node: TTreeNode; 2129begin 2130 BeginUpdate; 2131 ClearCache; 2132 Node := GetLastNode; 2133 if Assigned(Node) then 2134 begin 2135 while Assigned(Node) do 2136 begin 2137 Node.Delete; 2138 Node := GetLastNode; 2139 end; 2140 end; 2141 FSelection.Clear; 2142 if (FOwner <> nil) then 2143 FOwner.GetAccessibleObject().ClearChildAccessibleObjects(); 2144 EndUpdate; 2145end; 2146 2147procedure TTreeNodes.ClearMultiSelection(ClearSelected: boolean = false); 2148var 2149 ANode, OldNode: TTreeNode; 2150begin 2151 if Assigned(Owner) then Owner.LockSelectionChangeEvent; 2152 try 2153 ANode := FFirstMultiSelected; 2154 while Assigned(ANode) do 2155 begin 2156 OldNode := ANode; 2157 ANode := ANode.GetNextMultiSelected; 2158 OldNode.MultiSelected := false; 2159 end; 2160 if ClearSelected then 2161 Owner.Selected := nil; 2162 finally 2163 if Assigned(Owner) then Owner.UnlockSelectionChangeEvent; 2164 end; 2165end; 2166 2167procedure TTreeNodes.SelectOnlyThis(Node: TTreeNode); 2168begin 2169 if Owner<>nil then Owner.LockSelectionChangeEvent; 2170 try 2171 ClearMultiSelection(true); 2172 Node.Selected:=true; 2173 finally 2174 if Owner<>nil then Owner.UnlockSelectionChangeEvent; 2175 end; 2176end; 2177 2178function TTreeNodes.IsMultiSelection: boolean; 2179begin 2180 Result:=(FFirstMultiSelected<>nil) 2181 and (FFirstMultiSelected.GetNextMultiSelected<>nil); 2182end; 2183 2184function TTreeNodes.AddChildFirst(ParentNode: TTreeNode; const S: string): TTreeNode; 2185begin 2186 Result := AddChildObjectFirst(ParentNode, S, nil); 2187end; 2188 2189function TTreeNodes.AddChildObjectFirst(ParentNode: TTreeNode; const S: string; 2190 Data: Pointer): TTreeNode; 2191begin 2192 Result := InternalAddObject(ParentNode, S, Data, taAddFirst); 2193end; 2194 2195function TTreeNodes.AddChild(ParentNode: TTreeNode; const S: string): TTreeNode; 2196begin 2197 Result := AddChildObject(ParentNode, S, nil); 2198end; 2199 2200function TTreeNodes.AddChildObject(ParentNode: TTreeNode; const S: string; 2201 Data: Pointer): TTreeNode; 2202begin 2203 Result := InternalAddObject(ParentNode, S, Data, taAdd); 2204end; 2205 2206function TTreeNodes.AddFirst(SiblingNode: TTreeNode; const S: string): TTreeNode; 2207begin 2208 Result := AddObjectFirst(SiblingNode, S, nil); 2209end; 2210 2211function TTreeNodes.AddObjectFirst(SiblingNode: TTreeNode; const S: string; 2212 Data: Pointer): TTreeNode; 2213var ParentNode: TTreeNode; 2214begin 2215 if SiblingNode <> nil then 2216 ParentNode := SiblingNode.Parent 2217 else 2218 ParentNode := nil; 2219 Result := InternalAddObject(ParentNode, S, Data, taAddFirst); 2220end; 2221 2222function TTreeNodes.AddNode(Node: TTreeNode; Relative: TTreeNode; 2223 const S: string; Ptr: Pointer; Method: TNodeAttachMode): TTreeNode; 2224var 2225 AddMode: TAddMode; 2226begin 2227 if (Relative=nil) and not (Method in [naAdd,naAddFirst]) then 2228 TreeNodeError('TTreeNode.AddNode Relative=nil'); 2229 if Method=naInsertBehind then begin // convert naInsertBehind 2230 if Relative.GetNextSibling=nil then 2231 Method:=naAdd 2232 else begin 2233 Method:=naInsert; 2234 Relative:=Relative.GetNextSibling; 2235 end; 2236 end; 2237 if (Relative <> nil) and (Method in [naAdd, naAddFirst]) then 2238 Relative := Relative.Parent; 2239 // Convert TNodeAttachMode to TAddMode 2240 case Method of 2241 naAddFirst,naAddChildFirst: AddMode := taAddFirst; 2242 naInsert: AddMode := taInsert; 2243 else 2244 AddMode:=taAdd; 2245 end; 2246 fNewNodeToBeAdded := Node; 2247 Result := InternalAddObject(Relative, S, Ptr, AddMode); 2248end; 2249 2250procedure TTreeNodes.SelectionsChanged(ANode: TTreeNode; const AIsSelected: Boolean); 2251begin 2252 if ANode <> nil then 2253 begin 2254 if AIsSelected then 2255 FSelection.Add(ANode) 2256 else 2257 FSelection.Remove(ANode); 2258 end; 2259end; 2260 2261function TTreeNodes.GetSelections(const AIndex: Integer): TTreeNode; 2262 2263 procedure RaiseOutOfBounds; 2264 begin 2265 TreeNodeError('TTreeNodes.GetSelections Index '+IntToStr(AIndex) 2266 +' out of bounds (Count='+IntToStr(FSelection.Count)+')'); 2267 end; 2268 2269begin 2270 if (AIndex < 0) or (AIndex > FSelection.Count - 1) then 2271 RaiseOutOfBounds; 2272 Result := TTreeNode(FSelection.Items[AIndex]); 2273end; 2274 2275function TTreeNodes.Add(SiblingNode: TTreeNode; const S: string): TTreeNode; 2276begin 2277 Result := AddObject(SiblingNode, S, nil); 2278end; 2279 2280procedure TTreeNodes.Repaint(ANode: TTreeNode); 2281var 2282 R: TRect; 2283begin 2284 if (FUpdateCount < 1) and (Owner<>nil) then begin 2285 while (ANode <> nil) and not ANode.IsVisible do ANode := ANode.Parent; 2286 if ANode <> nil then begin 2287 R := ANode.DisplayRect(False); 2288 InvalidateRect(Owner.Handle, @R, True); 2289 end; 2290 end; 2291end; 2292 2293function TTreeNodes.AddObject(SiblingNode: TTreeNode; const S: string; 2294 Data: Pointer): TTreeNode; 2295var ParentNode: TTreeNode; 2296begin 2297 if SiblingNode <> nil then 2298 ParentNode := SiblingNode.Parent 2299 else 2300 ParentNode := nil; 2301 Result := InternalAddObject(ParentNode, S, Data, taAdd); 2302end; 2303 2304function TTreeNodes.Insert(NextNode: TTreeNode; const S: string): TTreeNode; 2305begin 2306 Result := InsertObject(NextNode, S, nil); 2307end; 2308 2309function TTreeNodes.InsertObject(NextNode: TTreeNode; const S: string; 2310 Data: Pointer): TTreeNode; 2311// create a new node with Text=S and Data=Data and insert in front of 2312// NextNode (as sibling with same parent). 2313begin 2314 Result:=InternalAddObject(NextNode,S,Data,taInsert); 2315end; 2316 2317function TTreeNodes.InsertBehind(PrevNode: TTreeNode; const S: string): TTreeNode; 2318begin 2319 Result := InsertObjectBehind(PrevNode, S, nil); 2320end; 2321 2322function TTreeNodes.InsertObjectBehind(PrevNode: TTreeNode; const S: string; 2323 Data: Pointer): TTreeNode; 2324// create a new node with Text=S and Data=Data and insert in front of 2325// NextNode (as sibling with same parent). 2326begin 2327 if (PrevNode<>nil) and (PrevNode.GetNextSibling<>nil) then 2328 Result:=InternalAddObject(PrevNode.GetNextSibling,S,Data,taInsert) 2329 else 2330 Result:=AddObject(PrevNode,S,Data); 2331end; 2332 2333procedure TTreeNodes.SortTopLevelNodes(SortProc: TTreeNodeCompare); 2334begin 2335 Sort(FTopLvlItems, FTopLvlCount, SortProc, true); 2336end; 2337 2338function TTreeNodes.InternalAddObject(Node: TTreeNode; const S: string; 2339 Data: Pointer; AddMode: TAddMode): TTreeNode; 2340{ 2341 TAddMode = (taAddFirst, taAdd, taInsert); 2342 2343 taAdd: add Result as last child of Node 2344 taAddFirst: add Result as first child of Node 2345 taInsert: add Result in front of Node 2346} 2347var 2348 ok: boolean; 2349 // Item: HTreeItem; 2350 lAccessibleObject: TLazAccessibleObject; 2351begin 2352 if Owner=nil then 2353 TreeNodeError('TTreeNodes.InternalAddObject Owner=nil'); 2354 {$IFDEF TREEVIEW_DEBUG} 2355 write('[TTreeNodes.InternalAddObject] Node=',Node<>nil,' S=',S, 2356 ' AddMode=',AddModeStr(AddMode)); 2357 if Node<>nil then 2358 DbgOut(' Node.Text=',Node.Text); 2359 DebugLn(''); 2360 {$ENDIF} 2361 Result := fNewNodeToBeAdded; // Used by AddNode to pass an existing node. 2362 if Result = Nil then 2363 Result := Owner.CreateNode; 2364 fNewNodeToBeAdded := nil; 2365 ok:=false; 2366 try 2367 Result.Data := Data; 2368 Result.Text := S; 2369 // move node in tree (tree of TTreeNode) 2370 Result.InternalMove(Node,AddMode); 2371 if (Owner<>nil) and Owner.AutoExpand and (Result.Parent<>nil) then 2372 Result.Parent.Expanded:=true; 2373 if (Owner<>nil) and (not (csReading in Owner.ComponentState)) then 2374 Owner.Added(Result); 2375 ok:=true; 2376 if ok and (Owner<>nil) and (Owner.AccessibilityOn) then 2377 begin 2378 lAccessibleObject := FOwner.GetAccessibleObject().AddChildAccessibleObject(Result); 2379 lAccessibleObject.AccessibleDescription := 'Item'; 2380 lAccessibleObject.AccessibleValue := S; 2381 lAccessibleObject.AccessibleRole := larTreeItem; 2382 end; 2383 finally 2384 Include(FOwner.FStates, tvsScrollbarChanged); 2385 FOwner.UpdateScrollbars; 2386 // this construction creates nicer exception output 2387 if not ok then 2388 Result.Free; 2389 end; 2390end; 2391 2392function TTreeNodes.GetFirstNode: TTreeNode; 2393begin 2394 if Assigned(FTopLvlItems) then 2395 Result := FTopLvlItems[0] 2396 else 2397 Result := nil; 2398end; 2399 2400function TTreeNodes.GetFirstVisibleNode: TTreeNode; 2401var 2402 Node: TTreeNode; 2403 i: Integer; 2404begin 2405 Result := nil; 2406 if Assigned(FTopLvlItems) then 2407 for i := 0 to FTopLvlCount-1 do begin 2408 Node := FTopLvlItems[i]; 2409 if Node.Visible then begin 2410 Result := Node; 2411 Break; 2412 end; 2413 end; 2414end; 2415 2416function TTreeNodes.GetLastNode: TTreeNode; 2417begin 2418 if Assigned(FTopLvlItems) then 2419 Result := FTopLvlItems[FTopLvlCount - 1] 2420 else 2421 Result := nil; 2422end; 2423 2424function TTreeNodes.GetLastVisibleNode: TTreeNode; 2425var 2426 Node: TTreeNode; 2427 i: Integer; 2428begin 2429 Result := nil; 2430 if Assigned(FTopLvlItems) then 2431 for i := FTopLvlCount-1 downto 0 do begin 2432 Node := FTopLvlItems[i]; 2433 if Node.Visible then begin 2434 Result := Node; 2435 Break; 2436 end; 2437 end; 2438end; 2439 2440function TTreeNodes.GetLastSubNode: TTreeNode; 2441// absolute last node 2442var 2443 Node: TTreeNode; 2444begin 2445 Result := GetLastNode; 2446 if Assigned(Result) then 2447 begin 2448 Node := Result.GetLastSubChild; 2449 if Assigned(Node) then Result := Node; 2450 end; 2451end; 2452 2453function TTreeNodes.GetLastExpandedSubNode: TTreeNode; 2454// absolute last expanded node 2455var 2456 Node: TTreeNode; 2457begin 2458 Result := GetLastVisibleNode; 2459 while Assigned(Result) and (Result.Expanded) do 2460 begin 2461 Node := Result.GetLastVisibleChild; 2462 if Assigned(Node) then 2463 Result := Node 2464 else 2465 exit; 2466 end; 2467end; 2468 2469function TTreeNodes.FindTopLvlNode(const NodeText: string): TTreeNode; 2470begin 2471 Result := GetFirstNode; 2472 while Assigned(Result) and (Result.Text <> NodeText) do 2473 Result := Result.GetNextSibling; 2474end; 2475 2476function TTreeNodes.FindNodeWithText(const NodeText: string): TTreeNode; 2477begin 2478 Result := GetFirstNode; 2479 while Assigned(Result) and (Result.Text <> NodeText) do 2480 Result := Result.GetNext; 2481end; 2482 2483function TTreeNodes.FindNodeWithTextPath(TextPath: string): TTreeNode; 2484var 2485 p: SizeInt; 2486 CurText: String; 2487begin 2488 Result:=nil; 2489 repeat 2490 p:=System.Pos('/',TextPath); 2491 if p>0 then begin 2492 CurText:=LeftStr(TextPath,p-1); 2493 System.Delete(TextPath,1,p); 2494 end else begin 2495 CurText:=TextPath; 2496 TextPath:=''; 2497 end; 2498 //debugln(['TTreeNodes.FindNodeWithTextPath CurText=',CurText,' Rest=',TextPath]); 2499 if Result=nil then 2500 Result:=FindTopLvlNode(CurText) 2501 else 2502 Result:=Result.FindNode(CurText); 2503 until (Result=nil) or (TextPath=''); 2504end; 2505 2506function TTreeNodes.FindNodeWithData(const NodeData: Pointer): TTreeNode; 2507begin 2508 Result := GetFirstNode; 2509 while Assigned(Result) and (Result.Data <> NodeData) do 2510 Result := Result.GetNext; 2511end; 2512 2513function TTreeNodes.GetNodeFromIndex(Index: Integer): TTreeNode; 2514// find node with absolute index in ALL nodes (even collapsed) 2515 2516 procedure RaiseIndexOutOfBounds; 2517 begin 2518 TreeNodeError('TTreeNodes.GetNodeFromIndex Index '+IntToStr(Index) 2519 +' out of bounds (Count='+IntToStr(FCount)+')'); 2520 end; 2521 2522 procedure RaiseSubTreeCount0; 2523 begin 2524 TreeNodeError( 2525 'TTreeNodes.GetNodeFromIndex: Consistency error - SubTreeCount=0'); 2526 end; 2527 2528 procedure RaiseSubTreeCountTooBig; 2529 begin 2530 TreeNodeError( 2531 'TTreeNodes.GetNodeFromIndex: Consistency error - invalid SubTreeCount'); 2532 end; 2533 2534 procedure RaiseCountTooBig; 2535 begin 2536 TreeNodeError( 2537 'TTreeNodes.GetNodeFromIndex: Consistency Error - Count too big'); 2538 end; 2539 2540var 2541 I, J: Integer; 2542begin 2543 if (Index < 0) or (Index >= FCount) then 2544 RaiseIndexOutOfBounds; 2545 if (FNodeCache.CacheNode <> nil) and (Abs(FNodeCache.CacheIndex - Index) <= 1) 2546 then begin 2547 with FNodeCache do 2548 begin 2549 if Index = CacheIndex then Result := CacheNode 2550 else if Index < CacheIndex then Result := CacheNode.GetPrev 2551 else Result := CacheNode.GetNext; 2552 end; 2553 end 2554 else if Index>Count-5 then begin 2555 // optimization for the last nodes 2556 Result:=GetLastSubNode; 2557 i:=Count-1; 2558 while (Index<i) do begin 2559 Result:=Result.GetPrev; 2560 dec(i); 2561 end; 2562 end 2563 else begin 2564 Result := GetFirstNode; 2565 I:=0; 2566 while (Result<>nil) and (Index>I) do begin 2567 Repeat 2568 // calculate the absolute index of the next sibling 2569 J:=I+Result.FSubTreeCount; 2570 if J=I then RaiseSubTreeCount0; 2571 if J<=Index then begin 2572 // Index > absolute index of next sibling -> search in next sibling 2573 Result:=Result.GetNextSibling; 2574 I:=J; 2575 end else 2576 break; 2577 until false; 2578 if (Result<>nil) and (Index>I) then begin 2579 // Index is somewhere in subtree of Result 2580 Result:=Result.GetFirstChild; 2581 if Result=nil then RaiseSubTreeCountTooBig; 2582 inc(I); 2583 end; 2584 end; 2585 end; 2586 if Result = nil then RaiseCountTooBig; 2587 FNodeCache.CacheNode := Result; 2588 FNodeCache.CacheIndex := Index; 2589end; 2590 2591function TTreeNodes.GetSelectionCount: Cardinal; 2592begin 2593 Result := Cardinal(FSelection.Count); 2594end; 2595 2596procedure TTreeNodes.SetItem(Index: Integer; AValue: TTreeNode); 2597begin 2598 GetNodeFromIndex(Index).Assign(AValue); 2599end; 2600 2601procedure TTreeNodes.SetTopLvlItems(Index: integer; AValue: TTreeNode); 2602begin 2603 GetTopLvlItems(Index).Assign(AValue); 2604end; 2605 2606procedure TTreeNodes.BeginUpdate; 2607begin 2608 Inc(FUpdateCount); 2609end; 2610 2611procedure TTreeNodes.EndUpdate; 2612begin 2613 Dec(FUpdateCount); 2614 if FUpdateCount = 0 then 2615 begin 2616 Include(Owner.FStates,tvsScrollbarChanged); 2617 Owner.UpdateScrollbars; 2618 Owner.Invalidate; 2619 end; 2620end; 2621 2622procedure TTreeNodes.FreeAllNodeData; 2623var 2624 i: Integer; 2625begin 2626 BeginUpdate; 2627 for i:=0 to TopLvlCount-1 do 2628 TopLvlItems[i].FreeAllNodeData; 2629 EndUpdate; 2630end; 2631 2632function TTreeNodes.GetEnumerator: TTreeNodesEnumerator; 2633begin 2634 Result := TTreeNodesEnumerator.Create(Self); 2635end; 2636 2637procedure TTreeNodes.GrowTopLvlItems; 2638begin 2639 if FTopLvlItems<>nil then begin 2640 FTopLvlCapacity:=FTopLvlCapacity shl 1; 2641 ReAllocMem(FTopLvlItems,SizeOf(Pointer)*FTopLvlCapacity); 2642 end else begin 2643 FTopLvlCapacity:=MinNodeCapacity; 2644 GetMem(FTopLvlItems,SizeOf(Pointer)*FTopLvlCapacity); 2645 end; 2646 //debugln('TTreeNodes.GrowTopLvlItems END FTopLvlCapacity=',FTopLvlCapacity,' FTopLvlCount=',FTopLvlCount,' ',FTopLvlItems<>nil); 2647end; 2648 2649function TTreeNodes.GetTopLvlItems(Index: integer): TTreeNode; 2650begin 2651 Result:=FTopLvlItems[Index]; 2652end; 2653 2654procedure TTreeNodes.ShrinkTopLvlItems; 2655var 2656 OldCapacity: LongInt; 2657begin 2658 if FTopLvlItems<>nil then begin 2659 OldCapacity:=FTopLvlCapacity; 2660 FTopLvlCapacity:=FTopLvlCapacity shr 1; 2661 if FTopLvlCapacity<FTopLvlCount then FTopLvlCapacity:=FTopLvlCount; 2662 if (FTopLvlCapacity<MinNodeCapacity) then begin 2663 if (FTopLvlCount>0) then 2664 FTopLvlCapacity:=MinNodeCapacity 2665 else 2666 FTopLvlCapacity:=0; 2667 end; 2668 if OldCapacity=FTopLvlCapacity then exit; 2669 //debugln('TTreeNodes.ShrinkTopLvlItems A FTopLvlCapacity=',FTopLvlCapacity,' FTopLvlCount=',FTopLvlCount,' ',FTopLvlItems<>nil); 2670 ReAllocMem(FTopLvlItems,SizeOf(Pointer)*FTopLvlCapacity); 2671 //debugln('TTreeNodes.ShrinkTopLvlItems B FTopLvlCapacity=',FTopLvlCapacity,' FTopLvlCount=',FTopLvlCount,' ',FTopLvlItems<>nil); 2672 end else begin 2673 if (FTopLvlCapacity>0) then 2674 TreeNodeError('TTreeNodes.ShrinkTopLvlItems FTopLvlCapacity>0'); 2675 end; 2676end; 2677 2678function TTreeNodes.IndexOfTopLvlItem(Node: TTreeNode): integer; 2679begin 2680 if (Node<>nil) and (Node.Owner=Self) then 2681 Result:=Node.FIndex 2682 else 2683 Result:=-1; 2684end; 2685 2686procedure TTreeNodes.MoveTopLvlNode(TopLvlFromIndex, TopLvlToIndex: integer; 2687 Node: TTreeNode); 2688// TopLvlFromIndex = -1 and is insert 2689// TopLvlToIndex = -1 is remove 2690var i: integer; 2691begin 2692 {$IFDEF TREEVIEW_DEBUG} 2693 DebugLn('[TTreeNodes.MoveTopLvlNode] TopLvlFromIndex=',TopLvlFromIndex, 2694 ' TopLvlToIndex=',TopLvlToIndex,' Node.Text=',Node.Text); 2695 {$ENDIF} 2696 if TopLvlFromIndex=TopLvlToIndex then exit; 2697 if (TopLvlFromIndex>=FTopLvlCount) then 2698 TreeNodeError('TTreeNodes.MoveTopLvlNode TopLvlFromIndex>FTopLvlCount'); 2699 if (TopLvlToIndex>FTopLvlCount) then 2700 TreeNodeError('TTreeNodes.MoveTopLvlNode TopLvlFromIndex>FTopLvlCount'); 2701 if (TopLvlFromIndex>=0) then begin 2702 Node:=FTopLvlItems[TopLvlFromIndex]; 2703 if (TopLvlToIndex>=0) then begin 2704 // move node 2705 if TopLvlFromIndex<TopLvlToIndex then begin 2706 // move forward 2707 for i:=TopLvlFromIndex to TopLvlToIndex-1 do begin 2708 FTopLvlItems[i]:=FTopLvlItems[i+1]; 2709 FTopLvlItems[i].FIndex:=i; 2710 end; 2711 end else begin 2712 // move backward 2713 for i:=TopLvlToIndex downto TopLvlFromIndex+1 do begin 2714 FTopLvlItems[i]:=FTopLvlItems[i-1]; 2715 FTopLvlItems[i].FIndex:=i; 2716 end; 2717 end; 2718 FTopLvlItems[TopLvlToIndex]:=Node; 2719 FTopLvlItems[TopLvlToIndex].FIndex:=TopLvlToIndex; 2720 end else begin 2721 // remove node 2722 if FTopLvlItems<>nil then begin 2723 for i:=TopLvlFromIndex to FTopLvlCount-2 do begin 2724 FTopLvlItems[i]:=FTopLvlItems[i+1]; 2725 FTopLvlItems[i].FIndex:=i; 2726 end; 2727 end; 2728 Dec(FTopLvlCount); 2729 if FTopLvlCount<0 then 2730 TreeNodeError('TTreeNodes.MoveTopLvlNode FTopLvlCount<0'); 2731 if FTopLvlCount<(FTopLvlCapacity shr 2) then ShrinkTopLvlItems; 2732 end; 2733 end else begin 2734 if (TopLvlToIndex>=0) then begin 2735 if Node=nil then 2736 TreeNodeError('TTreeNodes.MoveTopLvlNode inserting nil'); 2737 // insert node 2738 if (FTopLvlCount=FTopLvlCapacity) then GrowTopLvlItems; 2739 inc(FTopLvlCount); 2740 if FTopLvlItems<>nil then begin 2741 for i:=FTopLvlCount-1 downto TopLvlToIndex+1 do begin 2742 FTopLvlItems[i]:=FTopLvlItems[i-1]; 2743 FTopLvlItems[i].FIndex:=i; 2744 end; 2745 FTopLvlItems[TopLvlToIndex]:=Node; 2746 FTopLvlItems[TopLvlToIndex].FIndex:=TopLvlToIndex; 2747 end; 2748 end else begin 2749 // nothing to do 2750 end; 2751 end; 2752end; 2753 2754procedure TTreeNodes.MultiSelect(Node: TTreeNode; ClearWholeSelection: Boolean); 2755var 2756 bGoNext, bOnlySiblings, bOnlyVisible: Boolean; 2757 // 2758 procedure _TakeNext(var N: TTreeNode); 2759 begin 2760 if bGoNext then 2761 begin 2762 if bOnlySiblings and bOnlyVisible then 2763 N := N.GetNextVisibleSibling 2764 else 2765 if bOnlySiblings and not bOnlyVisible then 2766 N := N.GetNextSibling 2767 else 2768 if not bOnlySiblings and bOnlyVisible then 2769 N := N.GetNextVisible 2770 else 2771 N := N.GetNext; 2772 end 2773 else 2774 begin 2775 if bOnlySiblings and bOnlyVisible then 2776 N := N.GetPrevVisibleSibling 2777 else 2778 if bOnlySiblings and not bOnlyVisible then 2779 N := N.GetPrevSibling 2780 else 2781 if not bOnlySiblings and bOnlyVisible then 2782 N := N.GetPrevVisible 2783 else 2784 N := N.GetPrev; 2785 end; 2786 end; 2787 // 2788var 2789 I, FirstNode, LastNode: TTreeNode; 2790begin 2791 if Owner<>nil then Owner.LockSelectionChangeEvent; 2792 bOnlySiblings := Assigned(Owner) and (msSiblingOnly in Owner.MultiSelectStyle); 2793 bOnlyVisible := Assigned(Owner) and (msVisibleOnly in Owner.MultiSelectStyle); 2794 2795 try 2796 if FStartMultiSelected=nil then 2797 begin 2798 FirstNode := Node; 2799 FStartMultiSelected := Node; 2800 end else 2801 FirstNode := FStartMultiSelected; 2802 2803 if ClearWholeSelection then 2804 begin 2805 ClearMultiSelection(True); 2806 end else 2807 begin 2808 //clear only last selection 2809 if Assigned(FLastMultiSelected) then 2810 begin 2811 LastNode := FLastMultiSelected; 2812 bGoNext := (FirstNode.Index <= LastNode.Index); 2813 I := FirstNode; 2814 I.MultiSelected:=False; 2815 while (I<>LastNode) do 2816 begin 2817 _TakeNext(I); 2818 if I=nil then Break; 2819 I.MultiSelected:=False; 2820 end; 2821 end; 2822 if Assigned(Owner) then 2823 Owner.Selected := nil; 2824 end; 2825 2826 //select again 2827 bGoNext := (FirstNode.AbsoluteIndex <= Node.AbsoluteIndex); 2828 I := FirstNode; 2829 I.MultiSelected:=True; 2830 while (I<>Node) do 2831 begin 2832 _TakeNext(I); 2833 if I=nil then Break; 2834 I.MultiSelected:=True; 2835 end; 2836 2837 FStartMultiSelected := FirstNode; 2838 FLastMultiSelected := Node; 2839 finally 2840 if Owner<>nil then Owner.UnlockSelectionChangeEvent; 2841 end; 2842end; 2843 2844procedure TTreeNodes.Assign(Source: TPersistent); 2845var 2846 SrcNodes: TTreeNodes; 2847 SrcStream: TMemoryStream; 2848begin 2849 ClearCache; 2850 if Source is TTreeNodes then begin 2851 SrcNodes := TTreeNodes(Source); 2852 Clear; 2853 SrcStream := TMemoryStream.Create; 2854 try 2855 SrcNodes.WriteData(SrcStream, true); 2856 SrcStream.Position := 0; 2857 ReadData(SrcStream); 2858 finally 2859 SrcStream.Free; 2860 end; 2861 end 2862 else 2863 inherited Assign(Source); 2864end; 2865 2866procedure TTreeNodes.DefineProperties(Filer: TFiler); 2867 2868 function WriteNodes: Boolean; 2869 var 2870 I: Integer; 2871 Nodes: TTreeNodes; 2872 begin 2873 Nodes := TTreeNodes(Filer.Ancestor); 2874 if Nodes = nil then 2875 Result := Count > 0 2876 else if Nodes.Count <> Count then 2877 Result := True 2878 else 2879 begin 2880 Result := False; 2881 for I := 0 to Count - 1 do 2882 begin 2883 Result := not Item[I].IsEqual(Nodes[I]); 2884 if Result then Break; 2885 end 2886 end; 2887 end; 2888 2889begin 2890 inherited DefineProperties(Filer); 2891 Filer.DefineBinaryProperty('Data', @ReadData, @WriteData, WriteNodes); 2892end; 2893 2894procedure TTreeNodes.ReadData(Stream: TStream); 2895var 2896 I, NewCount, MagicNumber: Integer; 2897 DelphiNodeInfo: TDelphiNodeInfo; 2898 StreamVersion: word; 2899begin 2900 Clear; 2901 // -7 for lcl stream 2902 Stream.ReadBuffer(MagicNumber,SizeOf(Integer)); 2903 if MagicNumber=LCLStreamID then begin 2904 // read stream version 2905 Stream.ReadBuffer(StreamVersion,SizeOf(StreamVersion)); 2906 // read top level node count 2907 Stream.ReadBuffer(NewCount, SizeOf(NewCount)); 2908 for I := 0 to NewCount - 1 do 2909 Add(nil, '').ReadData(Stream, StreamVersion); 2910 end else begin 2911 // delphi stream 2912 NewCount:=MagicNumber; 2913 for I := 0 to NewCount - 1 do 2914 Add(nil, '').ReadDelphiData(Stream, @DelphiNodeInfo); 2915 end; 2916end; 2917 2918procedure TTreeNodes.WriteData(Stream: TStream); 2919begin 2920 WriteData(Stream, false); 2921end; 2922 2923procedure TTreeNodes.WriteData(Stream: TStream; WriteDataPointer: boolean); 2924var 2925 ANode: TTreeNode; 2926 MagicNumber: integer; 2927 StreamVersion: word; 2928begin 2929 // -7 for lcl stream 2930 MagicNumber:=LCLStreamID; 2931 Stream.WriteBuffer(MagicNumber,SizeOf(MagicNumber)); 2932 // write stream version 2933 if WriteDataPointer then 2934 StreamVersion:=TTreeNodeWithPointerStreamVersion 2935 else 2936 StreamVersion:=TTreeNodeStreamVersion; 2937 Stream.WriteBuffer(StreamVersion,SizeOf(Word)); 2938 // write top level node count 2939 Stream.WriteBuffer(FTopLvlCount, SizeOf(Integer)); 2940 // write all nodes recursively 2941 ANode := GetFirstNode; 2942 while ANode <> nil do begin 2943 ANode.WriteData(Stream, StreamVersion); 2944 ANode := ANode.GetNextSibling; 2945 end; 2946end; 2947 2948procedure TTreeNodes.ReadExpandedState(Stream: TStream); 2949var 2950 ItemCount, 2951 Index: Integer; 2952 Node: TTreeNode; 2953 NodeExpanded: Boolean; 2954begin 2955 if Stream.Position < Stream.Size then 2956 Stream.ReadBuffer(ItemCount, SizeOf(ItemCount)) 2957 else Exit; 2958 Index := 0; 2959 Node := GetFirstNode; 2960 while (Index < ItemCount) and (Node <> nil) do begin 2961 Stream.ReadBuffer(NodeExpanded, SizeOf(NodeExpanded)); 2962 Node.Expanded := NodeExpanded; 2963 Inc(Index); 2964 Node := Node.GetNext; 2965 end; 2966end; 2967 2968procedure TTreeNodes.WriteExpandedState(Stream: TStream); 2969var 2970 Size: Integer; 2971 ANode: TTreeNode; 2972 NodeExpanded: Boolean; 2973begin 2974 Size := SizeOf(Boolean) * Count; 2975 Stream.WriteBuffer(Size, SizeOf(Size)); 2976 ANode := GetFirstNode; 2977 while (ANode <> nil) do begin 2978 NodeExpanded := ANode.Expanded; 2979 Stream.WriteBuffer(NodeExpanded, SizeOf(Boolean)); 2980 ANode := ANode.GetNext; 2981 end; 2982end; 2983 2984procedure TTreeNodes.ClearCache; 2985begin 2986 FNodeCache.CacheNode := nil; 2987end; 2988 2989procedure TTreeNodes.ConsistencyCheck; 2990var 2991 Node: TTreeNode; 2992 RealCount, i: integer; 2993 OldCache: TNodeCache; 2994begin 2995 if FUpdateCount<0 then 2996 RaiseGDBException('FUpdateCount<0'); 2997 RealCount:=0; 2998 Node:=GetFirstNode; 2999 while Node<>nil do begin 3000 Node.ConsistencyCheck; 3001 inc(RealCount,Node.SubTreeCount); 3002 //DebugLn(' ConsistencyCheck: B ',RealCount,',',Node.SubTreeCount); 3003 Node:=Node.FNextBrother; 3004 end; 3005 //DebugLn(' ConsistencyCheck: B ',RealCount,',',FCount); 3006 if RealCount<>FCount then 3007 RaiseGDBException('RealCount<>FCount'); 3008 if (FTopLvlCapacity<=0) and (FTopLvlItems<>nil) then 3009 RaiseGDBException(''); 3010 if (FTopLvlCapacity>0) and (FTopLvlItems=nil) then 3011 RaiseGDBException(''); 3012 if FTopLvlCapacity<FTopLvlCount then 3013 RaiseGDBException(''); 3014 if (FTopLvlCount<0) then 3015 RaiseGDBException(''); 3016 for i:=0 to FTopLvlCount-1 do begin 3017 Node:=FTopLvlItems[i]; 3018 if (i=0) and (Node.FPrevBrother<>nil) then 3019 RaiseGDBException(''); 3020 if (i>0) and (Node.FPrevBrother<>FTopLvlItems[i-1]) then 3021 RaiseGDBException(''); 3022 if (i<FTopLvlCount-1) and (Node.FNextBrother<>FTopLvlItems[i+1]) 3023 then begin 3024 DebugLn(' CONSISTENCY i=%d FTopLvlCount=%d FTopLvlItems[i]=%p FTopLvlItems[i].FNextBrother=%p FTopLvlItems[i+1]=%p', 3025 [i, FTopLvlCount, Pointer(Node), Pointer(Node.FNextBrother), Pointer(FTopLvlItems[i+1])]); 3026 RaiseGDBException(''); 3027 end; 3028 if (i=FTopLvlCount-1) and (Node.FNextBrother<>nil) then 3029 RaiseGDBException(''); 3030 if Node.FIndex<>i then 3031 RaiseGDBException(''); 3032 end; 3033 if FNodeCache.CacheNode<>nil then begin 3034 OldCache:=FNodeCache; 3035 ClearCache; 3036 if GetNodeFromIndex(OldCache.CacheIndex)<>OldCache.CacheNode then 3037 RaiseGDBException(''); 3038 end; 3039end; 3040 3041procedure TTreeNodes.WriteDebugReport(const Prefix: string; AllNodes: boolean); 3042var 3043 Node: TTreeNode; 3044begin 3045 DbgOut('%s%s.WriteDebugReport Self=%p', [Prefix, ClassName, Pointer(Self)]); 3046 ConsistencyCheck; 3047 DebugLn(''); 3048 if AllNodes then begin 3049 Node:=GetFirstNode; 3050 while Node<>nil do begin 3051 Node.WriteDebugReport(Prefix+' ',true); 3052 Node:=Node.GetNextSibling; 3053 end; 3054 end; 3055end; 3056 3057 3058type 3059 TTreeStrings = class(TStrings) 3060 private 3061 FOwner: TTreeNodes; 3062 protected 3063 function Get(Index: Integer): string; override; 3064 function GetBufStart(Buffer: PChar; var Level: Integer): PChar; 3065 function GetCount: Integer; override; 3066 function GetObject(Index: Integer): TObject; override; 3067 procedure PutObject(Index: Integer; AObject: TObject); override; 3068 procedure SetUpdateState(Updating: Boolean); override; 3069 public 3070 constructor Create(AnOwner: TTreeNodes); 3071 function Add(const S: string): Integer; override; 3072 procedure Clear; override; 3073 procedure Delete(Index: Integer); override; 3074 procedure Insert(Index: Integer; const S: string); override; 3075 procedure LoadTreeFromStream(Stream: TStream); 3076 procedure SaveTreeToStream(Stream: TStream); 3077 function ConsistencyCheck: integer; 3078 procedure WriteDebugReport(const Prefix: string); 3079 property Owner: TTreeNodes read FOwner; 3080 end; 3081 3082constructor TTreeStrings.Create(AnOwner: TTreeNodes); 3083begin 3084 inherited Create; 3085 FOwner := AnOwner; 3086end; 3087 3088function TTreeStrings.Get(Index: Integer): string; 3089const 3090 TabChar = #9; 3091var 3092 Level, I: Integer; 3093 Node: TTreeNode; 3094begin 3095 Result := ''; 3096 Node := Owner.GetNodeFromIndex(Index); 3097 Level := Node.Level; 3098 for I := 0 to Level - 1 do 3099 Result := Result + TabChar; 3100 Result := Result + Node.Text; 3101end; 3102 3103function TTreeStrings.GetBufStart(Buffer: PChar; var Level: Integer): PChar; 3104begin 3105 Level := 0; 3106 while Buffer^ in [' ', #9] do 3107 begin 3108 Inc(Buffer); 3109 Inc(Level); 3110 end; 3111 Result := Buffer; 3112end; 3113 3114function TTreeStrings.GetObject(Index: Integer): TObject; 3115begin 3116 Result := TObject(Owner.GetNodeFromIndex(Index).Data); 3117end; 3118 3119procedure TTreeStrings.PutObject(Index: Integer; AObject: TObject); 3120begin 3121 Owner.GetNodeFromIndex(Index).Data := AObject; 3122end; 3123 3124function TTreeStrings.GetCount: Integer; 3125begin 3126 Result := Owner.Count; 3127end; 3128 3129procedure TTreeStrings.Clear; 3130begin 3131 Owner.Clear; 3132end; 3133 3134procedure TTreeStrings.Delete(Index: Integer); 3135begin 3136 Owner.GetNodeFromIndex(Index).Delete; 3137end; 3138 3139procedure TTreeStrings.SetUpdateState(Updating: Boolean); 3140begin 3141 //SendMessage(Owner.Handle, WM_SETREDRAW, Ord(not Updating), 0); 3142 if not Updating then 3143 Owner.Owner.Invalidate; 3144end; 3145 3146function TTreeStrings.Add(const S: string): Integer; 3147var 3148 Level, OldLevel, I: Integer; 3149 NewStr: string; 3150 Node: TTreeNode; 3151begin 3152 Result := GetCount; 3153 if (Length(S) = 1) and (S[1] = Chr($1A)) then Exit; 3154 Node := nil; 3155 OldLevel := 0; 3156 NewStr := GetBufStart(PChar(S), Level); 3157 if Result > 0 then 3158 begin 3159 Node := Owner.GetNodeFromIndex(Result - 1); 3160 OldLevel := Node.Level; 3161 end; 3162 if (Level > OldLevel) or (Node = nil) then 3163 begin 3164 if Level - OldLevel > 1 then 3165 TreeViewError('TTreeStrings.Add: Invalid level, Level='+IntToStr(Level) 3166 +' OldLevel='+IntToStr(OldLevel)); 3167 end 3168 else begin 3169 for I := OldLevel downto Level do 3170 begin 3171 Node := Node.Parent; 3172 if (Node = nil) and (I - Level > 0) then 3173 TreeViewError('TTreeStrings.Add: Invalid level, Node=nil I='+IntToStr(I) 3174 +' Level='+IntToStr(Level)); 3175 end; 3176 end; 3177 Owner.AddChild(Node, NewStr); 3178end; 3179 3180procedure TTreeStrings.Insert(Index: Integer; const S: string); 3181begin 3182 with Owner do 3183 Insert(GetNodeFromIndex(Index), S); 3184end; 3185 3186procedure TTreeStrings.LoadTreeFromStream(Stream: TStream); 3187var 3188 List: TStringList; 3189 ANode, NextNode: TTreeNode; 3190 ALevel, i: Integer; 3191 CurrStr: string; 3192begin 3193 List := TStringList.Create; 3194 Owner.BeginUpdate; 3195 try 3196 Clear; 3197 List.LoadFromStream(Stream); 3198 ANode := nil; 3199 for i := 0 to List.Count - 1 do 3200 begin 3201 CurrStr := GetBufStart(PChar(List[i]), ALevel); 3202 if ANode = nil then 3203 ANode := Owner.AddChild(nil, CurrStr) 3204 else if ANode.Level = ALevel then 3205 ANode := Owner.AddChild(ANode.Parent, CurrStr) 3206 else if ANode.Level = (ALevel - 1) then 3207 ANode := Owner.AddChild(ANode, CurrStr) 3208 else if ANode.Level > ALevel then 3209 begin 3210 NextNode := ANode.Parent; 3211 while NextNode.Level > ALevel do 3212 NextNode := NextNode.Parent; 3213 ANode := Owner.AddChild(NextNode.Parent, CurrStr); 3214 end 3215 else TreeViewError('TTreeStrings.LoadTreeFromStream: Level=' 3216 +IntToStr(ALevel)+' CuurStr="'+CurrStr+'"'); 3217 end; 3218 finally 3219 Owner.EndUpdate; 3220 List.Free; 3221 end; 3222end; 3223 3224procedure TTreeStrings.SaveTreeToStream(Stream: TStream); 3225const 3226 TabChar = #9; 3227 EndOfLine = #13#10; 3228var 3229 i: Integer; 3230 ANode: TTreeNode; 3231 NodeStr: string; 3232begin 3233 if Count > 0 then 3234 begin 3235 ANode := Owner[0]; 3236 while ANode <> nil do 3237 begin 3238 NodeStr := ''; 3239 for i := 0 to ANode.Level - 1 do NodeStr := NodeStr + TabChar; 3240 NodeStr := NodeStr + ANode.Text + EndOfLine; 3241 Stream.Write(Pointer(NodeStr)^, Length(NodeStr)); 3242 ANode := ANode.GetNext; 3243 end; 3244 end; 3245end; 3246 3247function TTreeStrings.ConsistencyCheck: integer; 3248begin 3249 Result:=0; 3250end; 3251 3252procedure TTreeStrings.WriteDebugReport(const Prefix: string); 3253begin 3254 DebugLn('%sTTreeStrings.WriteDebugReport Self=%p Consistency=%d', [Prefix, Pointer(Self), ConsistencyCheck]); 3255end; 3256 3257 3258{ TCustomTreeView } 3259 3260constructor TCustomTreeView.Create(AnOwner: TComponent); 3261var 3262 Details: TThemedElementDetails; 3263begin 3264 inherited Create(AnOwner); 3265 ControlStyle := ControlStyle - [csCaptureMouse] 3266 + [csDisplayDragImage, csReflector]; 3267 Width := 121; 3268 Height := 97; 3269 Color := clWindow; 3270 3271 FSBVertShowing:=-1; 3272 FSBHorzShowing:=-1; 3273 TabStop := True; 3274 ParentColor := False; 3275// FBackgroundColor := clWindow; 3276 FDefItemHeight := DefaultTreeNodeHeight; 3277 FDefItemSpace := ScaleY(2, 96); 3278 FExpandSignType := tvestTheme; 3279 FExpandSignSize := -1; 3280 Details := ThemeServices.GetElementDetails(ttGlyphOpened); 3281 FThemeExpandSignSize := ThemeServices.GetDetailSize(Details).cx; 3282 FTreeNodes := CreateNodes; 3283 BorderStyle := bsSingle; 3284 BorderWidth := 0; 3285 FMultiSelectStyle := DefaultMultiSelectStyle; 3286 FOptions := DefaultTreeViewOptions; 3287 Items.KeepCollapsedNodes:=KeepCollapsedNodes; 3288 FScrollBars:=ssBoth; 3289 FDragImage := TDragImageList.CreateSize(32, 32); 3290 FIndent:=-1; 3291 FChangeTimer := TTimer.Create(Self); 3292 FChangeTimer.Enabled := False; 3293 FChangeTimer.Interval := 1; 3294 FChangeTimer.OnTimer := @OnChangeTimer; 3295 FImageChangeLink := TChangeLink.Create; 3296 FImageChangeLink.OnChange := @ImageListChange; 3297 FSelectedColor:=clHighlight; 3298 FSelectedFontColor:=clWhite; 3299 FSelectedFontColorUsed:=false; 3300 fSeparatorColor:=clGray; 3301 FStateChangeLink := TChangeLink.Create; 3302 FStateChangeLink.OnChange := @ImageListChange; 3303 FStates:=[tvsMaxLvlNeedsUpdate,tvsMaxRightNeedsUpdate,tvsScrollbarChanged]; 3304 FTreeLineColor := clWindowFrame; 3305 FTreeLinePenStyle := psPattern; 3306 SetLength(FTreeLinePenPattern, 2); 3307 FTreeLinePenPattern[0] := 1; 3308 FTreeLinePenPattern[1] := 1; 3309 FExpandSignColor := clWindowFrame; 3310 FHotTrackColor := clNone; 3311 FDisabledFontColor := clGrayText; 3312 // Accessibility 3313 AccessibleDescription := rsTTreeViewAccessibilityDescription; 3314 AccessibleRole := larTreeView; 3315 FAccessibilityOn := WidgetSet.GetLCLCapability(lcAccessibilitySupport) = LCL_CAPABILITY_YES; 3316 3317 FDragScrollMargin := 20; // Height of 2 areas, on top/bottom of treeview, which auto-scroll treeview up/down 3318 FDragScrollTimer := TTimer.Create(Self); 3319 FDragScrollTimer.Enabled := false; 3320 FDragScrollTimer.Interval := 150; 3321 FDragScrollTimer.OnTimer := @DragScrollTimerTick; 3322end; 3323 3324destructor TCustomTreeView.Destroy; 3325begin 3326 LockSelectionChangeEvent; // prevent change event during destroying 3327 3328 Images:=nil; 3329 FreeThenNil(FTreeNodes); 3330 FreeThenNil(FSaveItems); 3331 FreeThenNil(FDragImage); 3332 FreeThenNil(FImageChangeLink); 3333 FreeThenNil(FStateChangeLink); 3334 inherited Destroy; 3335end; 3336 3337procedure TCustomTreeView.CreateWnd; 3338begin 3339 Exclude(FStates,tvsStateChanging); 3340 FSBHorzShowing:=-1; 3341 FSBVertShowing:=-1; 3342 inherited CreateWnd; 3343end; 3344 3345procedure TCustomTreeView.Click; 3346begin 3347 if not FMouseDownOnFoldingSign then 3348 inherited; 3349end; 3350 3351procedure TCustomTreeView.DblClick; 3352begin 3353 if not FMouseDownOnFoldingSign then 3354 inherited; 3355end; 3356 3357procedure TCustomTreeView.TripleClick; 3358begin 3359 if not FMouseDownOnFoldingSign then 3360 inherited; 3361end; 3362 3363procedure TCustomTreeView.QuadClick; 3364begin 3365 if not FMouseDownOnFoldingSign then 3366 inherited; 3367end; 3368 3369procedure TCustomTreeView.InitializeWnd; 3370begin 3371 inherited InitializeWnd; 3372 UpdateDefaultItemHeight; 3373end; 3374 3375procedure TCustomTreeView.Invalidate; 3376begin 3377 if tvsPainting in FStates then exit; 3378 inherited Invalidate; 3379end; 3380 3381procedure TCustomTreeView.EraseBackground(DC: HDC); 3382begin 3383 // everything is painted, so erasing the background is not needed 3384end; 3385 3386procedure TCustomTreeView.DestroyWnd; 3387begin 3388 Include(FStates, tvsStateChanging); 3389 inherited DestroyWnd; 3390 if Canvas <> nil then 3391 TControlCanvas(Canvas).FreeHandle; 3392 FLastHorzScrollInfo.cbSize := 0; 3393 FLastVertScrollInfo.cbSize := 0; 3394end; 3395 3396procedure TCustomTreeView.DoAutoAdjustLayout( 3397 const AMode: TLayoutAdjustmentPolicy; const AXProportion, AYProportion: Double 3398 ); 3399begin 3400 inherited DoAutoAdjustLayout(AMode, AXProportion, AYProportion); 3401 3402 if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then 3403 begin 3404 DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TCustomTreeView.DoAutoAdjustLayout'){$ENDIF}; 3405 try 3406 if DefaultItemHeightIsStored then 3407 DefaultItemHeight := Round(DefaultItemHeight*AYProportion); 3408 if IndentIsStored then 3409 FIndent := Round(FIndent*AXProportion); 3410 if ExpandSignSizeIsStored then 3411 FExpandSignSize := Round(FExpandSignSize*AXProportion); 3412 finally 3413 EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TCustomTreeView.DoAutoAdjustLayout'){$ENDIF}; 3414 end; 3415 end; 3416end; 3417 3418procedure TCustomTreeView.DoCreateNodeClass(var NewNodeClass: TTreeNodeClass); 3419begin 3420 if Assigned(OnCreateNodeClass) then 3421 OnCreateNodeClass(Self,NewNodeClass); 3422end; 3423 3424procedure TCustomTreeView.BeginAutoDrag; 3425begin 3426 BeginDrag(False); 3427end; 3428 3429procedure TCustomTreeView.BeginEditing(ANode: TTreeNode); 3430var 3431 ARect: TRect; 3432begin 3433 //DebugLn(['TCustomTreeView.BeginEditing tvsIsEditing=',tvsIsEditing in FStates,' Selected=',Selected<>nil]); 3434 if (tvsIsEditing in FStates) or (ANode=nil) then exit; 3435 if (not CanEdit(ANode)) or (not ANode.Visible) then exit; 3436 // if we are asked to edit another node while one is already being edited then 3437 // stop editing that node 3438 if FEditingItem <> nil then 3439 EndEditing; 3440 FEditingItem := ANode; 3441 // make node visible (this will cancel editing, so call this first) 3442 EnsureNodeIsVisible(ANode); 3443 3444 Include(FStates,tvsIsEditing); 3445 if FEditor=nil then begin 3446 FEditor:=TEdit.Create(Self); 3447 FEditor.OnEditingDone:=@EditorEditingDone; 3448 FEditor.OnKeyDown:=@EditorKeyDown; 3449 end; 3450 ARect:=Rect(Max(BorderWidth,ANode.DisplayTextLeft),ANode.Top-ScrolledTop, 3451 ClientWidth-BorderWidth,ANode.Bottom-ScrolledTop); 3452 FEditor.BoundsRect:=ARect; 3453 FEditor.AnchorParallel(akLeft,ARect.Left,Self); 3454 FEditor.AnchorParallel(akRight,BorderWidth,Self); 3455 FEditor.Visible:=true; 3456 FEditor.Parent:=Self; 3457 FEditor.Text:=ANode.Text; 3458 FEditor.SelectAll; 3459 FEditor.SetFocus; 3460end; 3461 3462procedure TCustomTreeView.BeginUpdate; 3463begin 3464 Items.BeginUpdate; 3465 LockSelectionChangeEvent; 3466end; 3467 3468procedure TCustomTreeView.EndUpdate; 3469begin 3470 UnlockSelectionChangeEvent; 3471 if Items.FUpdateCount<=0 then RaiseGDBException('TCustomTreeView.EndUpdate'); 3472 Items.EndUpdate; 3473 if Items.FUpdateCount=0 then begin 3474 // ToDo: only refresh if something changed 3475 UpdateScrollBars; 3476 end; 3477end; 3478 3479function TCustomTreeView.AlphaSort: Boolean; 3480begin 3481 Result := CustomSort(nil); 3482end; 3483 3484function TCustomTreeView.CustomSort(SortProc: TTreeNodeCompare): Boolean; 3485var Node: TTreeNode; 3486begin 3487 Result := False; 3488 if FTreeNodes.Count>0 then begin 3489 BeginUpdate; 3490 if not assigned(SortProc) then SortProc := @DefaultTreeViewSort; 3491 FTreeNodes.SortTopLevelNodes(SortProc); 3492 3493 Node := FTreeNodes.GetFirstNode; 3494 while Node <> nil do begin 3495 if (Node.GetFirstChild<>nil) then Node.CustomSort(SortProc); 3496 Node := Node.GetNext; 3497 end; 3498 Items.ClearCache; 3499 FStates:= FStates+[tvsTopsNeedsUpdate, tvsTopItemNeedsUpdate, 3500 tvsBottomItemNeedsUpdate,tvsScrollbarChanged]; 3501 EndUpdate; 3502 end; 3503end; 3504 3505function TCustomTreeView.DefaultItemHeightIsStored: Boolean; 3506begin 3507 Result := not(tvoAutoItemHeight in Options); 3508end; 3509 3510function TCustomTreeView.DefaultTreeViewSort(Node1, Node2: TTreeNode): Integer; 3511begin 3512 if Assigned(OnCompare) then begin 3513 Result:=0; 3514 OnCompare(Node1.TreeView,Node1, Node2, Result); 3515 end else 3516 Result := Utf8CompareStr(Node1.Text,Node2.Text); 3517end; 3518 3519procedure TCustomTreeView.SetAutoExpand(Value: Boolean); 3520begin 3521 if AutoExpand <> Value then begin 3522 if Value then 3523 Include(FOptions,tvoAutoExpand) 3524 else 3525 Exclude(FOptions,tvoAutoExpand); 3526 end; 3527end; 3528 3529procedure TCustomTreeView.SetHotTrack(Value: Boolean); 3530begin 3531 if HotTrack <> Value then begin 3532 if Value then 3533 Include(FOptions,tvoHotTrack) 3534 else 3535 Exclude(FOptions,tvoHotTrack); 3536 end; 3537end; 3538 3539procedure TCustomTreeView.SetRowSelect(Value: Boolean); 3540begin 3541 if RowSelect <> Value then begin 3542 if Value then 3543 Include(FOptions,tvoRowSelect) 3544 else 3545 Exclude(FOptions,tvoRowSelect); 3546 if FSelectedNode<>nil then 3547 Invalidate; 3548 end; 3549end; 3550 3551procedure TCustomTreeView.SetScrollBars(const Value: TScrollStyle); 3552begin 3553 if (FScrollBars <> Value) then begin 3554 FScrollBars := Value; 3555 Include(FStates,tvsScrollbarChanged); 3556 UpdateScrollBars; 3557 end; 3558end; 3559 3560procedure TCustomTreeView.SetScrolledLeft(AValue: integer); 3561var 3562 OldScrolledLeft: Integer; 3563begin 3564 OldScrolledLeft := FScrolledLeft; 3565 if AValue<0 then AValue:=0; 3566 if AValue=FScrolledLeft then exit; 3567 if AValue>GetMaxScrollLeft then AValue:=GetMaxScrollLeft; 3568 if AValue=FScrolledLeft then exit; 3569 EndEditing(true); 3570 FScrolledLeft:=AValue; 3571 ScrollView(OldScrolledLeft-FScrolledLeft, 0); 3572end; 3573 3574procedure TCustomTreeView.SetScrolledTop(AValue: integer); 3575var 3576 OldScrolledTop: Integer; 3577begin 3578 OldScrolledTop:=FScrolledTop; 3579 if FScrolledTop=AValue then exit; 3580 if AValue<0 then AValue:=0; 3581 if AValue>GetMaxScrollTop then AValue:=GetMaxScrollTop; 3582 if AValue=FScrolledTop then exit; 3583 EndEditing(true); 3584 FScrolledTop:=AValue; 3585 FStates:=FStates+[tvsTopItemNeedsUpdate,tvsBottomItemNeedsUpdate]; 3586 ScrollView(0, OldScrolledTop-FScrolledTop); 3587end; 3588 3589procedure TCustomTreeView.SetToolTips(Value: Boolean); 3590begin 3591 if ToolTips <> Value then begin 3592 if Value then 3593 Include(FOptions,tvoToolTips) 3594 else 3595 Exclude(FOptions,tvoToolTips); 3596 end; 3597end; 3598 3599procedure TCustomTreeView.SetSortType(Value: TSortType); 3600begin 3601 if SortType <> Value then begin 3602 FSortType := Value; 3603 if ((SortType in [stData, stBoth]) and Assigned(OnCompare)) or 3604 (SortType in [stText, stBoth]) then 3605 AlphaSort; 3606 end; 3607end; 3608 3609procedure TCustomTreeView.SetBackgroundColor(Value: TColor); 3610begin 3611 if Color<>Value then begin 3612 Color:=Value; 3613 Invalidate; 3614 end; 3615end; 3616 3617procedure TCustomTreeView.SetSelectedColor(Value: TColor); 3618begin 3619 if FSelectedColor<>Value then begin 3620 FSelectedColor:=Value; 3621 Invalidate; 3622 end; 3623end; 3624 3625procedure TCustomTreeView.SetSelectedFontColor(Value: TColor); 3626begin 3627 if FSelectedFontColor<>Value then begin 3628 FSelectedFontColor:=Value; 3629 Invalidate; 3630 end; 3631end; 3632 3633procedure TCustomTreeView.Paint; 3634begin 3635 DoPaint; 3636end; 3637 3638procedure TCustomTreeView.SetDragMode(Value: TDragMode); 3639begin 3640 inherited SetDragMode(Value); 3641end; 3642 3643procedure TCustomTreeView.SetOptions(NewOptions: TTreeViewOptions); 3644var ChangedOptions: TTreeViewOptions; 3645begin 3646 if FOptions=NewOptions then exit; 3647 ChangedOptions:=(FOptions-NewOptions)+(NewOptions-FOptions); 3648 FOptions:=NewOptions; 3649 if tvoKeepCollapsedNodes in ChangedOptions then 3650 Items.KeepCollapsedNodes:=(tvoKeepCollapsedNodes in FOptions); 3651 if (tvoReadOnly in ChangedOptions) and (not (tvoReadOnly in FOptions)) then 3652 EndEditing; 3653 if (tvoAllowMultiselect in ChangedOptions) then begin 3654 if (tvoAllowMultiselect in FOptions) then begin 3655 if Selected<>nil then 3656 Selected.MultiSelected:=true; 3657 end else begin 3658 Items.ClearMultiSelection; 3659 end; 3660 end; 3661 if tvoAutoItemHeight in ChangedOptions then 3662 UpdateDefaultItemHeight; 3663 if ([tvoHideSelection,tvoReadOnly,tvoShowButtons,tvoShowRoot,tvoShowLines] 3664 * ChangedOptions)<>[] 3665 then 3666 Invalidate; 3667end; 3668 3669procedure TCustomTreeView.UpdateDefaultItemHeight; 3670var 3671 NewDefItemHeight: Integer; 3672 ImageSize, StateImageSize: TSize; 3673begin 3674 if (tvoAutoItemHeight in FOptions) 3675 and HandleAllocated and Canvas.HandleAllocated then begin 3676 NewDefItemHeight:=Canvas.TextHeight(TVAutoHeightString)+FDefItemSpace; 3677 ImageSize := GetImageSize; 3678 if Assigned(FStateImages) then 3679 StateImageSize := StateImages.SizeForPPI[StateImagesWidth, Font.PixelsPerInch]; 3680 if NewDefItemHeight<FDefItemSpace then NewDefItemHeight:=FDefItemSpace; 3681 if (ImageSize.cy > 0) and (ImageSize.cy + FDefItemSpace > NewDefItemHeight) then 3682 NewDefItemHeight:=ImageSize.cy+FDefItemSpace; 3683 if (StateImages<>nil) and (StateImageSize.cy+FDefItemSpace>NewDefItemHeight) then 3684 NewDefItemHeight:=StateImageSize.cy+FDefItemSpace; 3685 if Odd(NewDefItemHeight) then Inc(NewDefItemHeight); 3686 if NewDefItemHeight<>FDefItemHeight then begin 3687 FDefItemHeight:=NewDefItemHeight; 3688 FStates:=FStates+[tvsTopsNeedsUpdate,tvsTopItemNeedsUpdate,tvsBottomItemNeedsUpdate]; 3689 Invalidate; 3690 end; 3691 end; 3692end; 3693 3694procedure TCustomTreeView.UpdateAllTops; 3695var 3696 CurTop: integer; 3697 3698 procedure CalculateTops(Node: TTreeNode); 3699 begin 3700 while Node<>nil do begin 3701 if Node.Visible then begin 3702 Node.fTop:=CurTop; 3703 inc(CurTop, Node.Height); 3704 if Node.Expanded then 3705 CalculateTops(Node.GetFirstChild); 3706 end; 3707 Node:=Node.GetNextSibling; 3708 end; 3709 end; 3710 3711begin 3712 if not (tvsTopsNeedsUpdate in FStates) then exit; 3713 CurTop:=0; 3714 CalculateTops(Items.GetFirstVisibleNode); 3715 Exclude(FStates,tvsTopsNeedsUpdate); 3716 Include(FStates,tvsScrollbarChanged); 3717end; 3718 3719procedure TCustomTreeView.UpdateMaxLvl; 3720 3721 procedure LookInChildsAndBrothers(Node: TTreeNode; CurLvl: integer); 3722 begin 3723 if Node=nil then exit; 3724 if CurLvl>FMaxLvl then FMaxLvl:=CurLvl; 3725 LookInChildsAndBrothers(Node.GetFirstChild,CurLvl+1); 3726 LookInChildsAndBrothers(Node.GetNextSibling,CurLvl); 3727 end; 3728 3729begin 3730 if not (tvsMaxLvlNeedsUpdate in FStates) then exit; 3731 FMaxLvl:=0; 3732 LookInChildsAndBrothers(Items.GetFirstNode,1); 3733 Exclude(FStates,tvsMaxRightNeedsUpdate); 3734end; 3735 3736procedure TCustomTreeView.UpdateMaxRight; 3737const 3738 LargeItemCount = 100; 3739 ReservedWidth = 100; 3740var 3741 Node: TTreeNode; 3742 i: integer; 3743 FMaxTextLen, AIndent: Integer; 3744 Cnt: Integer; 3745begin 3746 if not (tvsMaxRightNeedsUpdate in FStates) then exit; 3747 FMaxRight := 0; 3748 FMaxTextLen := 0; 3749 Node := Items.GetFirstNode; 3750 Cnt := 0; 3751 AIndent := Indent; 3752 while Node <> nil do 3753 begin 3754 if not Node.AreParentsExpandedAndVisible then 3755 begin 3756 Node := Node.GetNext; 3757 Continue; 3758 end; 3759 inc(Cnt); 3760 if (Cnt < LargeItemCount) then 3761 begin 3762 i := Node.DisplayTextRight + ScrolledLeft + AIndent div 2; 3763 end else 3764 begin 3765 // computing DisplayTextRight is too expensive when the tree 3766 // has hundreds of nodes 3767 // => use a heuristic 3768 if length(Node.Text) > FMaxTextLen then 3769 i := Node.DisplayTextRight + ScrolledLeft + ReservedWidth 3770 else 3771 i := FMaxRight; 3772 end; 3773 if FMaxRight < i then 3774 begin 3775 FMaxRight := i; 3776 FMaxTextLen := length(Node.Text); 3777 end; 3778 Node := Node.GetNext; 3779 end; 3780 Exclude(FStates, tvsMaxRightNeedsUpdate); 3781 Include(FStates, tvsScrollbarChanged); 3782end; 3783 3784procedure TCustomTreeView.UpdateTopItem; 3785begin 3786 //DebugLn('TCustomTreeView.UpdateTopItem tvsTopItemNeedsUpdate in FStates=',tvsTopItemNeedsUpdate in FStates); 3787 if (FStates*[tvsTopItemNeedsUpdate,tvsTopsNeedsUpdate]=[]) then exit; 3788 FTopItem:=GetNodeAtY(BorderWidth); 3789 Exclude(FStates,tvsTopItemNeedsUpdate); 3790end; 3791 3792procedure TCustomTreeView.UpdateBottomItem; 3793begin 3794 if (FStates*[tvsTopItemNeedsUpdate,tvsTopsNeedsUpdate,tvsBottomItemNeedsUpdate]=[]) 3795 then exit; 3796// if not (tvsBottomItemNeedsUpdate in FStates) then exit; already above 3797 FBottomItem:=TopItem; 3798 while (FBottomItem<>nil) and (FBottomItem.GetNextVisible<>nil) do 3799 FBottomItem:=FBottomItem.GetNextVisible; 3800 Exclude(FStates,tvsBottomItemNeedsUpdate); 3801end; 3802 3803procedure TCustomTreeView.SetBottomItem(Value: TTreeNode); 3804begin 3805 if HandleAllocated and (Value <> nil) then begin 3806 Value.MakeVisible; 3807 ScrolledTop:=Value.Top+Value.Height-ClientHeight; 3808 end; 3809end; 3810 3811procedure TCustomTreeView.SetSeparatorColor(const AValue: TColor); 3812begin 3813 if fSeparatorColor=AValue then exit; 3814 fSeparatorColor:=AValue; 3815 if tvoShowSeparators in Options then 3816 Invalidate; 3817end; 3818 3819procedure TCustomTreeView.SetShowButton(Value: Boolean); 3820begin 3821 if ShowButtons <> Value then begin 3822 if Value then 3823 Include(FOptions,tvoShowButtons) 3824 else 3825 Exclude(FOptions,tvoShowButtons); 3826 Invalidate; 3827 end; 3828end; 3829 3830procedure TCustomTreeView.SetShowLines(Value: Boolean); 3831begin 3832 if ShowLines <> Value then begin 3833 if Value then 3834 Include(FOptions,tvoShowLines) 3835 else 3836 Exclude(FOptions,tvoShowLines); 3837 Invalidate; 3838 end; 3839end; 3840 3841procedure TCustomTreeView.SetShowRoot(Value: Boolean); 3842begin 3843 if ShowRoot <> Value then begin 3844 if Value then 3845 Include(FOptions,tvoShowRoot) 3846 else 3847 Exclude(FOptions,tvoShowRoot); 3848 Invalidate; 3849 end; 3850end; 3851 3852procedure TCustomTreeView.SetShowScrollBar(Which: Integer; AShow: Boolean); 3853begin 3854 if ((Which in [SB_Horz, SB_BOTH]) and (FSBHorzShowing<>Ord(AShow))) 3855 or ((Which in [SB_Vert, SB_BOTH]) and (FSBVertShowing<>Ord(AShow))) 3856 then 3857 ShowScrollBar(Handle, Which, AShow); 3858 3859 if Which in [SB_Horz, SB_BOTH] then 3860 FSBHorzShowing:=Ord(AShow); 3861 if Which in [SB_Vert, SB_BOTH] then 3862 FSBVertShowing:=Ord(AShow); 3863end; 3864 3865procedure TCustomTreeView.SetShowSeparators(Value: Boolean); 3866begin 3867 if ShowSeparators <> Value then begin 3868 if Value then 3869 Include(FOptions,tvoShowSeparators) 3870 else 3871 Exclude(FOptions,tvoShowSeparators); 3872 Invalidate; 3873 end; 3874end; 3875 3876procedure TCustomTreeView.SetKeepCollapsedNodes(Value: Boolean); 3877begin 3878 if KeepCollapsedNodes=Value then exit; 3879 if Value then 3880 Include(FOptions,tvoKeepCollapsedNodes) 3881 else 3882 Exclude(FOptions,tvoKeepCollapsedNodes); 3883 Items.KeepCollapsedNodes:=Value; 3884end; 3885 3886procedure TCustomTreeView.SetMultiSelect(const AValue: Boolean); 3887begin 3888 if MultiSelect <> AValue then 3889 begin 3890 ClearSelection; 3891 if AValue then 3892 Include(FOptions,tvoAllowMultiselect) 3893 else 3894 Exclude(FOptions,tvoAllowMultiselect); 3895 end; 3896end; 3897 3898procedure TCustomTreeView.SetMultiSelectStyle(const AValue: TMultiSelectStyle); 3899begin 3900 if FMultiSelectStyle=AValue then exit; 3901 FMultiSelectStyle:=AValue; 3902 // there must be at least one multiselectstyle according to docs 3903 // http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/ComCtrls_TCustomTreeView_MultiSelectStyle.html 3904 if FMultiSelectStyle = [] then 3905 FMultiSelectStyle := DefaultMultiSelectStyle; 3906end; 3907 3908procedure TCustomTreeView.SetReadOnly(Value: Boolean); 3909begin 3910 if ReadOnly = Value then exit; 3911 if Value then 3912 Include(FOptions,tvoReadOnly) 3913 else 3914 Exclude(FOptions,tvoReadOnly); 3915 if not Value then EndEditing; 3916end; 3917 3918procedure TCustomTreeView.SetRightClickSelect(Value: Boolean); 3919begin 3920 if Value then 3921 Include(FOptions,tvoRightClickSelect) 3922 else 3923 Exclude(FOptions,tvoRightClickSelect); 3924end; 3925 3926procedure TCustomTreeView.SetHideSelection(Value: Boolean); 3927begin 3928 if HideSelection <> Value then begin 3929 if Value then 3930 Include(FOptions,tvoHideSelection) 3931 else 3932 Exclude(FOptions,tvoHideSelection); 3933 if FSelectedNode<>nil then Invalidate; 3934 end; 3935end; 3936 3937function TCustomTreeView.GetMaxLvl: integer; 3938begin 3939 UpdateMaxRight; 3940 Result:=FMaxRight; 3941end; 3942 3943function TCustomTreeView.GetMaxScrollLeft: integer; 3944begin 3945 UpdateMaxRight; 3946 Result:=FMaxRight-(ClientWidth-2*BorderWidth); 3947 if Result<0 then Result:=0; 3948end; 3949 3950function TCustomTreeView.GetMaxScrollTop: integer; 3951var 3952 LastVisibleNode: TTreeNode; 3953begin 3954 LastVisibleNode:=Items.GetLastExpandedSubNode; 3955 if LastVisibleNode=nil then 3956 Result:=0 3957 else begin 3958 Result:=LastVisibleNode.Top+LastVisibleNode.Height 3959 -ClientHeight+2*integer(BorderWidth); 3960 //DebugLn('>>> ',LastVisibleNode.Text,' ',Result); 3961 if Result<0 then Result:=0; 3962 end; 3963end; 3964 3965function TCustomTreeView.GetNodeAtY(Y: Integer): TTreeNode; 3966// search in all expanded nodes for the node at the screen coordinate Y 3967var 3968 i: integer; 3969begin 3970 Result := nil; 3971 if not Assigned(Items) then 3972 Exit; 3973 if (Y >= BorderWidth) and (Y < ClientHeight - BorderWidth) then 3974 begin 3975 inc(Y, FScrolledTop - BorderWidth); 3976 i := IndexOfNodeAtTop(Items.FTopLvlItems, Items.FTopLvlCount, Y); 3977 if i >= 0 then 3978 begin 3979 Result := Items.FTopLvlItems[i]; 3980 while Result.Visible and Result.Expanded do 3981 begin 3982 i := IndexOfNodeAtTop(Result.FItems, Result.FCount, Y); 3983 if i >= 0 then 3984 Result := Result.Items[i] 3985 else 3986 break; 3987 end; 3988 end; 3989 end; 3990end; 3991 3992function TCustomTreeView.GetNodeDrawAreaWidth: integer; 3993begin 3994 Result:=ClientWidth-BorderWidth*2; 3995end; 3996 3997function TCustomTreeView.GetNodeDrawAreaHeight: integer; 3998begin 3999 Result:=ClientHeight-BorderWidth*2; 4000end; 4001 4002function TCustomTreeView.GetNodeAt(X, Y: Integer): TTreeNode; 4003var 4004 b: Boolean; 4005begin 4006 Result := GetNodeAtY(Y); 4007 if Result = nil then Exit; 4008 if tvoRowSelect in Options then // row select 4009 b := (X < BorderWidth) or (X >= ClientWidth - BorderWidth) 4010 else 4011 b := (X < Result.DisplayStateIconLeft) or (X >= Result.DisplayTextRight); 4012 if b then 4013 Result := nil; 4014end; 4015 4016function TCustomTreeView.GetNodeWithExpandSignAt(X, Y: Integer): TTreeNode; 4017var 4018 b: Boolean; 4019begin 4020 Result := GetNodeAtY(Y); 4021 if Result = nil then Exit; 4022 if tvoRowSelect in Options then // row select 4023 b := (X < BorderWidth) or (X >= ClientWidth - BorderWidth) 4024 else // need to include DisplayExpandSignLeft 4025 b := (X < Result.DisplayExpandSignLeft) or (X >= Result.DisplayTextRight); 4026 if b then 4027 Result := nil; 4028end; 4029 4030procedure TCustomTreeView.GetInsertMarkAt(X, Y: Integer; 4031 out AnInsertMarkNode: TTreeNode; out AnInsertMarkType: TTreeViewInsertMarkType); 4032var 4033 ANode: TTreeNode; 4034 NodeRect: TRect; 4035 NodeMidY: integer; 4036begin 4037 AnInsertMarkNode:=nil; 4038 AnInsertMarkType:=tvimNone; 4039 if Y<0 then Y:=0; 4040 if Y>=ClientHeight then Y:=ClientHeight-1; 4041 ANode:=GetNodeAtY(Y); 4042 if ANode<>nil then 4043 begin 4044 NodeRect:=ANode.DisplayRect(false); 4045 NodeMidY:=NodeRect.Top + (NodeRect.Bottom-NodeRect.Top) div 2; 4046 AnInsertMarkNode:=ANode; 4047 if (X>AnInsertMarkNode.DisplayExpandSignRight) then 4048 if Y>=NodeMidY then begin 4049 // insert as first child of pointed node 4050 AnInsertMarkType:=tvimAsFirstChild; 4051 end else begin 4052 // insert as previous sibling of pointed node 4053 AnInsertMarkType:=tvimAsPrevSibling; 4054 end else begin 4055 if Y>=NodeMidY then begin 4056 if ANode.Expanded and ANode.HasChildren then begin 4057 // insert as first child of pointed node 4058 AnInsertMarkType:=tvimAsFirstChild; 4059 end else begin 4060 // insert as next sibling of pointed node 4061 AnInsertMarkType:=tvimAsNextSibling; 4062 end; 4063 end else begin 4064 // insert as previous sibling of pointed node 4065 AnInsertMarkType:=tvimAsPrevSibling; 4066 end; 4067 end; 4068 end else begin 4069 // insert behind all nodes 4070 ANode:=Items.GetLastExpandedSubNode; 4071 if ANode<>nil then begin 4072 AnInsertMarkNode:=ANode; 4073 if X>AnInsertMarkNode.DisplayExpandSignRight then 4074 // insert as first child of last visible node 4075 AnInsertMarkType:=tvimAsFirstChild 4076 else 4077 // insert as next sibling of last visible node 4078 AnInsertMarkType:=tvimAsNextSibling; 4079 end else begin 4080 // insert as new root 4081 AnInsertMarkNode:=nil; 4082 AnInsertMarkType:=tvimAsFirstChild; 4083 end; 4084 end; 4085 4086 // normalize (try to replace tvimAsPrevSibling) 4087 if (AnInsertMarkType=tvimAsPrevSibling) and (AnInsertMarkNode<>nil) then begin 4088 if (AnInsertMarkNode.GetPrevSibling<>nil) then begin 4089 if (AnInsertMarkNode.GetPrevSibling.Expanded=false) 4090 and (AnInsertMarkNode.GetPrevSibling.IsVisible) then begin 4091 AnInsertMarkNode:=AnInsertMarkNode.GetPrevSibling; 4092 AnInsertMarkType:=tvimAsNextSibling; 4093 end; 4094 end else if (AnInsertMarkNode.Parent<>nil) 4095 and (AnInsertMarkNode.Parent.IsVisible) then begin 4096 AnInsertMarkNode:=AnInsertMarkNode.Parent; 4097 AnInsertMarkType:=tvimAsFirstChild; 4098 end; 4099 end; 4100end; 4101 4102procedure TCustomTreeView.SetInsertMark(AnInsertMarkNode: TTreeNode; 4103 AnInsertMarkType: TTreeViewInsertMarkType); 4104begin 4105 InsertMarkNode:=AnInsertMarkNode; 4106 InsertMarkType:=AnInsertMarkType; 4107end; 4108 4109procedure TCustomTreeView.SetInsertMarkAt(X, Y: integer); 4110var 4111 AnInsertMarkNode: TTreeNode; 4112 AnInsertMarkType: TTreeViewInsertMarkType; 4113begin 4114 GetInsertMarkAt(X,Y,AnInsertMarkNode,AnInsertMarkType); 4115 SetInsertMark(AnInsertMarkNode,AnInsertMarkType); 4116end; 4117 4118function TCustomTreeView.GetHitTestInfoAt(X, Y: Integer): THitTests; 4119// ToDo: Set also flags [htAbove, htBelow, htOnRight, htToLeft, htToRight]; 4120var 4121 Node: TTreeNode; 4122begin 4123 Result := []; 4124 if (X>=0) and (X<ClientWidth) and (Y>=0) and (Y<ClientHeight) 4125 then begin 4126 Node:=GetNodeAtY(Y); 4127 if Node<>nil then begin 4128 Include(Result,htOnItem); 4129 if X<Node.DisplayExpandSignLeft then 4130 Include(Result,htOnIndent) 4131 else if X<Node.DisplayStateIconLeft then 4132 Include(Result,htOnButton) 4133 else if X<Node.DisplayIconLeft then 4134 Include(Result,htOnStateIcon) 4135 else if X<Node.DisplayTextLeft then 4136 Include(Result,htOnIcon) 4137 else if X<Node.DisplayTextRight then 4138 Include(Result,htOnLabel); 4139 end else 4140 Include(Result,htNowhere); 4141 end; 4142end; 4143 4144procedure TCustomTreeView.SetTreeLineColor(Value: TColor); 4145begin 4146 if FTreeLineColor<>Value then begin 4147 FTreeLineColor:=Value; 4148 Invalidate; 4149 end; 4150end; 4151 4152procedure TCustomTreeView.SetTreeNodes(Value: TTreeNodes); 4153begin 4154 Items.Assign(Value); 4155end; 4156 4157procedure TCustomTreeView.SetIndent(Value: Integer); 4158begin 4159 if Value <> Indent then begin 4160 FIndent := Value; 4161 Invalidate; 4162 end; 4163end; 4164 4165procedure TCustomTreeView.FullExpand; 4166var 4167 Node: TTreeNode; 4168begin 4169 Node := Items.GetFirstNode; 4170 while Node <> nil do begin 4171 Node.Expand(True); 4172 Node := Node.GetNextSibling; 4173 end; 4174end; 4175 4176procedure TCustomTreeView.FullCollapse; 4177var 4178 Node: TTreeNode; 4179begin 4180 Node := Items.GetFirstNode; 4181 while Node <> nil do begin 4182 Node.Collapse(True); 4183 Node := Node.GetNextSibling; 4184 end; 4185end; 4186 4187function TCustomTreeView.IsNodeVisible(ANode: TTreeNode): Boolean; 4188begin 4189 Result:=(ANode<>nil) and (ANode.Visible) and (ANode.AreParentsExpandedAndVisible); 4190 if Result then begin 4191 //DebugLn('[TCustomTreeView.IsNodeVisible] B Node=',DbgS(ANode), 4192 // ' ',dbgs(FScrolledTop)+'>=',dbgs(ANode.Top+ANode.Height)+' or =',dbgs(FScrolledTop),'+'+dbgs(ClientHeight)+'<',dbgs(ANode.Top)); 4193 if (FScrolledTop>=ANode.Top+ANode.Height) 4194 or (FScrolledTop+ClientHeight-2*BorderWidth<ANode.Top) 4195 then 4196 Result:=false; 4197 end; 4198 //DebugLn('[TCustomTreeView.IsNodeVisible] END Node=',DbgS(ANode), 4199 //' Node.Text=',ANode.Text,' Visible=',Result); 4200end; 4201 4202function TCustomTreeView.IsNodeHeightFullVisible(ANode: TTreeNode): Boolean; 4203begin 4204 Result:=(ANode<>nil) and (ANode.AreParentsExpandedAndVisible); 4205 if Result then begin 4206 //DebugLn('[TCustomTreeView.IsNodeVisible] B Node=',DbgS(ANode), 4207 //' ',FScrolledTop,'>=',ANode.Top,'+',ANode.Height,' or ',FScrolledTop,'+',ClientHeight,'<',ANode.Top); 4208 if (FScrolledTop>ANode.Top) 4209 or (FScrolledTop+ClientHeight-2*BorderWidth 4210 <ANode.Top+ANode.Height) 4211 then 4212 Result:=false; 4213 end; 4214 //DebugLn('[TCustomTreeView.IsNodeVisible] END Node=',DbgS(ANode), 4215 //' Node.Text=',ANode.Text,' Visible=',Result); 4216end; 4217 4218procedure TCustomTreeView.KeyDown(var Key: Word; Shift: TShiftState); 4219const 4220 EditKey = VK_F2; 4221 EditKeyShift = []; 4222var 4223 lNode: TTreeNode; 4224begin 4225 inherited KeyDown(Key, Shift); 4226 4227 case Key of 4228 4229 VK_DOWN: 4230 begin 4231 MoveToNextNode(AllowMultiSelectWithShift(Shift)); 4232 Key:=VK_UNKNOWN; 4233 end; 4234 4235 VK_UP: 4236 begin 4237 MoveToPrevNode(AllowMultiSelectWithShift(Shift)); 4238 Key:=VK_UNKNOWN; 4239 end; 4240 4241 VK_HOME: 4242 begin 4243 MoveHome(AllowMultiSelectWithShift(Shift)); 4244 Key:=VK_UNKNOWN; 4245 end; 4246 4247 VK_END: 4248 begin 4249 MoveEnd(AllowMultiSelectWithShift(Shift)); 4250 Key:=VK_UNKNOWN; 4251 end; 4252 4253 VK_PRIOR: // Page Up 4254 begin 4255 MovePageUp(AllowMultiSelectWithShift(Shift)); 4256 Key:=VK_UNKNOWN; 4257 end; 4258 4259 VK_NEXT: // Page Down 4260 begin 4261 MovePageDown(AllowMultiSelectWithShift(Shift)); 4262 Key:=VK_UNKNOWN; 4263 end; 4264 4265 else 4266 if not (Key in [VK_LEFT,VK_RIGHT,VK_ADD,VK_SUBTRACT]) then 4267 Exit; 4268 4269 if (tvoAllowMultiSelect in FOptions) and AllowMultiSelectWithShift(Shift) then 4270 lNode := FTreeNodes.FLastMultiSelected 4271 else 4272 lNode := Selected; 4273 4274 case Key of 4275 VK_LEFT: 4276 if lNode <> nil then 4277 begin 4278 if lNode.Expanded then 4279 lNode.Expanded := False 4280 else 4281 if lNode.Parent <> nil then 4282 lNode := lNode.Parent; 4283 Key:=VK_UNKNOWN; 4284 end; 4285 4286 VK_RIGHT: 4287 if lNode <> nil then 4288 begin 4289 if lNode.Expanded then 4290 lNode := lNode.GetNextExpanded 4291 else 4292 lNode.Expanded := True; 4293 Key:=VK_UNKNOWN; 4294 end; 4295 4296 VK_ADD: 4297 if lNode <> nil then 4298 lNode.Expanded := True; 4299 4300 VK_SUBTRACT: 4301 if lNode <> nil then 4302 lNode.Expanded := False; 4303 4304 else 4305 if (Key=EditKey) and (Shift=EditKeyShift) and (not ReadOnly) then 4306 BeginEditing(Selected); 4307 lNode := nil; { No change in selection } 4308 end; 4309 4310 if lNode <> nil then 4311 MoveSelection(lNode, AllowMultiSelectWithShift(Shift)); 4312 end; 4313end; 4314 4315procedure TCustomTreeView.Loaded; 4316begin 4317 inherited Loaded; 4318 if csDesigning in ComponentState then FullExpand; 4319 UpdateDefaultItemHeight; 4320end; 4321 4322function TCustomTreeView.GetTopItem: TTreeNode; 4323begin 4324 if HandleAllocated then 4325 begin 4326 UpdateTopItem; 4327 Result := FTopItem; 4328 end 4329 else 4330 Result := FTopItem; 4331end; 4332 4333procedure TCustomTreeView.HintMouseLeave(Sender: TObject); 4334begin 4335 if FindLCLControl(Mouse.CursorPos)<>Self then 4336 FHintWnd.Hide; 4337end; 4338 4339function TCustomTreeView.IsStoredBackgroundColor: Boolean; 4340begin 4341 result := Color <> clWindow; 4342end; 4343 4344procedure TCustomTreeView.SetTopItem(Value: TTreeNode); 4345begin 4346 if HandleAllocated and (Value <> nil) then 4347 begin 4348 Value.MakeVisible; 4349 ScrolledTop:=Value.Top; 4350 end 4351 else 4352 FTopItem := Value; 4353end; 4354 4355procedure TCustomTreeView.OnChangeTimer(Sender: TObject); 4356begin 4357 FChangeTimer.Enabled := False; 4358 //debugln('TCustomTreeView.OnChangeTimer'); 4359 FCallingChange := True; 4360 try 4361 Change(FSelectedNode); 4362 finally 4363 FCallingChange := False; 4364 end; 4365end; 4366 4367procedure TCustomTreeView.UpdateScrollbars; 4368var 4369 ScrollInfo: TScrollInfo; 4370 MaxScrollLeft, MaxScrollTop: Integer; 4371begin 4372 if not (tvsScrollbarChanged in FStates) then exit; 4373 4374 if not HandleAllocated or (Items.FUpdateCount>0) then 4375 exit; 4376 4377 MaxScrollLeft := GetMaxScrollLeft; 4378 MaxScrollTop := GetMaxScrollTop; 4379 4380 //DebugLn('* TCustomTreeView.UpdateScrollbars Enter *'); 4381 if ScrolledLeft>MaxScrollLeft then ScrolledLeft:=MaxScrollLeft; 4382 if ScrolledTop>MaxScrollTop then ScrolledTop:=MaxScrollTop; 4383 Exclude(FStates,tvsScrollbarChanged); 4384 4385 if fScrollBars in [ssBoth, ssHorizontal, ssAutoBoth, ssAutoHorizontal] then 4386 begin 4387 // horizontal scrollbar 4388 FillChar(ScrollInfo,SizeOf(ScrollInfo),0); 4389 ScrollInfo.cbSize := SizeOf(ScrollInfo); 4390 ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL; 4391 ScrollInfo.nTrackPos := 0; 4392 ScrollInfo.nMin := 0; 4393 ScrollInfo.nPage := Max(1,ClientWidth-2*BorderWidth); 4394 ScrollInfo.nMax := Max(1,MaxScrollLeft+integer(ScrollInfo.nPage)); 4395 ScrollInfo.nPos := Max(FScrolledLeft,0); 4396 if not CompareMem(@ScrollInfo,@FLastHorzScrollInfo,SizeOf(TScrollInfo)) 4397 then begin 4398 if (fScrollBars in [ssAutoBoth, ssAutoHorizontal]) 4399 and (ScrollInfo.nPage>=cardinal(ScrollInfo.nMax)) then begin 4400 //DebugLn(['TCustomTreeView.UpdateScrollbars Hide Horizontal.']); 4401 FLastHorzScrollInfo.cbSize:=0; 4402 SetShowScrollBar(SB_HORZ, false); 4403 end else begin 4404 //DebugLn(['TCustomTreeView.UpdateScrollbars Show Horizontal: nMin=',ScrollInfo.nMin, 4405 //' nMax=',ScrollInfo.nMax,' nPage=',ScrollInfo.nPage, 4406 //' nPos=',ScrollInfo.nPos,' GetMaxScrollLeft=',MaxScrollLeft, 4407 //' ClientW=',ClientWidth, ' MaxRight=',FMaxRight]); 4408 FLastHorzScrollInfo:=ScrollInfo; 4409 SetShowScrollBar(SB_HORZ, true); 4410 SetScrollInfo(Handle, SB_HORZ, ScrollInfo, true); 4411 end; 4412 end; 4413 end else begin 4414 FLastHorzScrollInfo.cbSize:=0; 4415 SetShowScrollBar(SB_HORZ,false); 4416 end; 4417 4418 if fScrollBars in [ssBoth, ssVertical, ssAutoBoth, ssAutoVertical] then begin 4419 // vertical scrollbar 4420 ScrollInfo.cbSize := SizeOf(ScrollInfo); 4421 ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL; 4422 ScrollInfo.nTrackPos := 0; 4423 ScrollInfo.nMin := 0; 4424 ScrollInfo.nPage := Max(1,ClientHeight-FDefItemHeight); 4425 ScrollInfo.nMax := Max(1,MaxScrollTop+integer(ScrollInfo.nPage)); 4426 ScrollInfo.nTrackPos := 0; 4427 ScrollInfo.nPos := Max(0,FScrolledTop); 4428 if not CompareMem(@ScrollInfo,@FLastVertScrollInfo,SizeOf(TScrollInfo)) 4429 then begin 4430 if (fScrollBars in [ssAutoBoth, ssAutoVertical]) 4431 and (ScrollInfo.nPage>=cardinal(ScrollInfo.nMax)) then begin 4432 //DebugLn(['TCustomTreeView.UpdateScrollbars Hide Vertical.']); 4433 ScrollInfo.nPos:=0; 4434 SetScrollInfo(Handle, SB_VERT, ScrollInfo, false); 4435 FLastVertScrollInfo.cbSize:=0; 4436 SetShowScrollBar(SB_VERT, false); 4437 end else begin 4438 //DebugLn(['TCustomTreeView.UpdateScrollbars Show Vertical: nMin=',ScrollInfo.nMin, 4439 //' nMax=',ScrollInfo.nMax,' nPage=',ScrollInfo.nPage, 4440 //' nPos=',ScrollInfo.nPos,' GetMaxScrollTop=',MaxScrollTop, 4441 //' ClientH=',ClientHeight]); 4442 FLastVertScrollInfo:=ScrollInfo; 4443 SetShowScrollBar(SB_VERT, true); 4444 SetScrollInfo(Handle, SB_VERT, ScrollInfo, true); 4445 end; 4446 end; 4447 end else begin 4448 FLastVertScrollInfo.cbSize:=0; 4449 SetShowScrollBar(SB_VERT,false); 4450 end; 4451end; 4452 4453procedure TCustomTreeView.UpdateTooltip(X, Y: integer); 4454var 4455 Node: TTreeNode; 4456 PHint, PLeft: TPoint; 4457 R, TextRect, IntRect: TRect; 4458 CurMonitor: TMonitor; 4459begin 4460 if not (tvoToolTips in FOptions) then exit; 4461 4462 if FHintWnd=nil then 4463 begin 4464 FHintWnd:=THintWindow.Create(Self); 4465 FHintWnd.OnMouseLeave:=@HintMouseLeave; 4466 end; 4467 4468 Node := GetNodeAt(X, Y); 4469 if Node=nil then 4470 begin 4471 FHintWnd.Hide; 4472 exit; 4473 end; 4474 4475 TextRect := Rect(Node.DisplayTextLeft, Node.Top, Node.DisplayTextRight, Node.Top + Node.Height); 4476 OffsetRect(TextRect, 0, -ScrolledTop); 4477 if not PtInRect(TextRect, Point(X, Y)) 4478 or (IntersectRect(IntRect, TextRect, ClientRect) and EqualRect(IntRect, TextRect)) then 4479 begin 4480 FHintWnd.Hide; 4481 exit; 4482 end; 4483 4484 // Get max width for hint from monitor's work area. 4485 CurMonitor := GetParentForm(Self).Monitor; 4486 R := CurMonitor.WorkareaRect; 4487 R := FHintWnd.CalcHintRect(R.Right-R.Left, Node.Text, nil); 4488 4489 if WidgetSet.GetLCLCapability(lcTransparentWindow) = LCL_CAPABILITY_YES then 4490 begin 4491 // Font is explicitly set for transparent hints, otherwise default font is used. 4492 if not FHintWnd.Visible then 4493 begin 4494 FHintWnd.Font.Assign(Self.Font); 4495 FHintWnd.Font.Color := Screen.HintFont.Color; 4496 end; 4497 // Put transparent hint exactly on the node. 4498 PHint := ClientToScreen(Point(TextRect.Left-1, TextRect.Top-3+BorderWidth)); 4499 end 4500 else begin 4501 // By default put hint to the right side of node. 4502 PHint := ClientToScreen(Point(ClientWidth, TextRect.Top-3+BorderWidth)); 4503 if PHint.X + R.Right > CurMonitor.BoundsRect.Right then 4504 begin // No space on the right? Put it to the left side. 4505 PLeft := ClientToScreen(Point(ClientRect.Left, ClientRect.Top)); 4506 if PLeft.X >= R.Right then // enough space on left? 4507 PHint.X := PLeft.X - R.Right; 4508 end; 4509 end; 4510 OffsetRect(R, PHint.X, PHint.Y); 4511 FHintWnd.ActivateHint(R, Node.Text) 4512end; 4513 4514function TCustomTreeView.GetSelection: TTreeNode; 4515begin 4516 if RightClickSelect and Assigned(FRClickNode) then 4517 Result := FRClickNode 4518 else 4519 Result := FSelectedNode; 4520end; 4521 4522function TCustomTreeView.GetSelectionCount: Cardinal; 4523begin 4524 Result := Items.SelectionCount; 4525end; 4526 4527function TCustomTreeView.GetSelections(AIndex: Integer): TTreeNode; 4528begin 4529 if (AIndex >= 0) and (AIndex < Items.SelectionCount) then 4530 Result := Items.GetSelections(AIndex) 4531 else 4532 Result := nil; 4533end; 4534 4535procedure TCustomTreeView.SetSelection(Value: TTreeNode); 4536var 4537 OldNode: TTreeNode; 4538begin 4539 if FSelectedNode = Value then Exit; 4540 if not CanChange(Value) then 4541 exit; 4542 {$IFDEF TREEVIEW_DEBUG} 4543 DebugLn('TCustomTreeView.SetSelection: Changing selection for Node: ', Text); 4544 {$ENDIF} 4545 EndEditing(true); // end editing before FSelectedNode change 4546 OldNode := FSelectedNode; 4547 FSelectedNode := Value; 4548 if Assigned(OldNode) then 4549 OldNode.Selected := False; 4550 if Assigned(Value) then 4551 begin 4552 Value.Selected := True; 4553 Value.MakeVisible; 4554 end; 4555 InternalSelectionChanged; 4556end; 4557 4558function TCustomTreeView.GetShowButtons: boolean; 4559begin 4560 Result:=(tvoShowButtons in FOptions); 4561end; 4562 4563function TCustomTreeView.GetShowLines: boolean; 4564begin 4565 Result:=(tvoShowLines in FOptions); 4566end; 4567 4568function TCustomTreeView.GetShowRoot: boolean; 4569begin 4570 Result:=(tvoShowRoot in FOptions); 4571end; 4572 4573function TCustomTreeView.GetShowSeparators: boolean; 4574begin 4575 Result:=(tvoShowSeparators in FOptions); 4576end; 4577 4578function TCustomTreeView.GetToolTips: boolean; 4579begin 4580 Result:=(tvoToolTips in FOptions); 4581end; 4582 4583procedure TCustomTreeView.SetExpandSignType(Value: TTreeViewExpandSignType); 4584begin 4585 if Value <> FExpandSignType then 4586 begin 4587 FExpandSignType := Value; 4588 Invalidate; 4589 end; 4590end; 4591 4592procedure TCustomTreeView.SetDefaultItemHeight(Value: integer); 4593begin 4594 if (tvoAutoItemHeight in FOptions) and (not (csLoading in ComponentState)) 4595 then exit; 4596 if Value<=0 then Value:=DefaultTreeNodeHeight; 4597 if Value=FDefItemHeight then exit; 4598 FDefItemHeight:=Value; 4599 Include(FStates,tvsTopsNeedsUpdate); 4600 Invalidate; 4601end; 4602 4603function TCustomTreeView.GetAutoExpand: boolean; 4604begin 4605 Result:=(tvoAutoExpand in FOptions); 4606end; 4607 4608function TCustomTreeView.GetBackgroundColor: TColor; 4609begin 4610 Result := Color; 4611end; 4612 4613function TCustomTreeView.GetBottomItem: TTreeNode; 4614begin 4615 if HandleAllocated then begin 4616 UpdateBottomItem; 4617 Result := FBottomItem; 4618 end else 4619 Result := nil; 4620end; 4621 4622function TCustomTreeView.GetDropTarget: TTreeNode; 4623begin 4624 if HandleAllocated then 4625 begin 4626 Result := FLastDropTarget; 4627 end 4628 else 4629 Result := nil; 4630end; 4631 4632function TCustomTreeView.GetHideSelection: boolean; 4633begin 4634 Result:=(tvoHideSelection in FOptions); 4635end; 4636 4637function TCustomTreeView.GetHotTrack: boolean; 4638begin 4639 Result:=(tvoHotTrack in FOptions); 4640end; 4641 4642function TCustomTreeView.GetKeepCollapsedNodes: boolean; 4643begin 4644 Result:=(tvoKeepCollapsedNodes in FOptions); 4645end; 4646 4647function TCustomTreeView.GetMultiSelect: Boolean; 4648begin 4649 Result := (tvoAllowMultiSelect in FOptions); 4650end; 4651 4652function TCustomTreeView.GetReadOnly: boolean; 4653begin 4654 Result:=(tvoReadOnly in FOptions); 4655end; 4656 4657function TCustomTreeView.GetExpandSignSize: integer; 4658begin 4659 if FExpandSignSize>=0 then 4660 Result := FExpandSignSize 4661 else 4662 if ExpandSignType = tvestTheme then 4663 Result := ScaleScreenToFont(FThemeExpandSignSize) 4664 else 4665 Result := Scale96ToFont(DefaultTreeNodeExpandSignSize); 4666end; 4667 4668function TCustomTreeView.GetIndent: Integer; 4669begin 4670 if FIndent<0 then 4671 Result := Scale96ToFont(15) 4672 else 4673 Result := FIndent; 4674end; 4675 4676function TCustomTreeView.GetRightClickSelect: boolean; 4677begin 4678 Result:=(tvoRightClickSelect in FOptions); 4679end; 4680 4681function TCustomTreeView.GetRowSelect: boolean; 4682begin 4683 Result:=(tvoRowSelect in FOptions); 4684end; 4685 4686procedure TCustomTreeView.SetDropTarget(Value: TTreeNode); 4687begin 4688 if HandleAllocated then 4689 if Value <> nil then 4690 Value.DropTarget := True; 4691end; 4692 4693procedure TCustomTreeView.SetExpandSignSize(const AExpandSignSize: integer); 4694begin 4695 if FExpandSignSize = AExpandSignSize then Exit; 4696 FExpandSignSize := AExpandSignSize; 4697 Invalidate; 4698end; 4699 4700function TCustomTreeView.IsEditing: Boolean; 4701begin 4702 Result:=tvsIsEditing in FStates; 4703end; 4704 4705function TCustomTreeView.GetDragImages: TDragImageList; 4706begin 4707 if Assigned(FDragImage) and (FDragImage.Count > 0) then 4708 Result := FDragImage 4709 else 4710 Result := nil; 4711end; 4712 4713function TCustomTreeView.GetBuiltinIconSize: TSize; 4714begin 4715 Result := Types.Size(0, 0); 4716end; 4717 4718function TCustomTreeView.GetImageSize: TSize; 4719begin 4720 if FImages <> nil then 4721 begin 4722 Result := Images.SizeForPPI[ImagesWidth, Font.PixelsPerInch] 4723 end else 4724 Result := GetBuiltinIconSize; 4725end; 4726 4727procedure TCustomTreeView.UpdateInsertMark(X,Y: integer); 4728begin 4729 if (tvoAutoInsertMark in Options) and (not (csDesigning in ComponentState)) 4730 then 4731 SetInsertMarkAt(X,Y) 4732 else 4733 SetInsertMark(nil,tvimNone); 4734end; 4735 4736procedure TCustomTreeView.DoSelectionChanged; 4737var 4738 lAccessibleObject: TLazAccessibleObject; 4739 lSelection: TTreeNode; 4740 lSelectedText: string; 4741begin 4742 // Update the accessibility information 4743 lAccessibleObject := GetAccessibleObject(); 4744 lSelection := Self.Selected; 4745 if lSelection = nil then lSelectedText := '' 4746 else lSelectedText := lSelection.Text; 4747 lAccessibleObject.AccessibleValue := lSelectedText; 4748 4749 if Assigned(OnSelectionChanged) then OnSelectionChanged(Self); 4750end; 4751 4752function TCustomTreeView.IsInsertMarkVisible: boolean; 4753begin 4754 Result:=(FInsertMarkType<>tvimNone) and (FInsertMarkNode<>nil) 4755 and (FInsertMarkNode.IsVisible); 4756end; 4757 4758procedure TCustomTreeView.DoStartDrag(var DragObject: TDragObject); 4759var 4760 P: TPoint; 4761begin 4762 {$IFDEF VerboseDrag} 4763 DebugLn('TCustomTreeView.DoStartDrag A ',Name,':',ClassName); 4764 {$ENDIF} 4765 inherited DoStartDrag(DragObject); 4766 FLastDropTarget := nil; 4767 if FDragNode = nil then begin 4768 GetCursorPos(P); 4769 with ScreenToClient(P) do FDragNode := GetNodeAt(X, Y); 4770 {$IFDEF VerboseDrag} 4771 if FDragNode<>nil then 4772 DebugLn('TCustomTreeView.DoStartDrag DragNode=',FDragNode.Text) 4773 else 4774 DebugLn('TCustomTreeView.DoStartDrag DragNode=nil'); 4775 {$ENDIF} 4776 end; 4777 FPrevToolTips := ToolTips; 4778 ToolTips := False; 4779 FDragScrollTimer.Enabled := true; 4780end; 4781 4782procedure TCustomTreeView.DoEndDrag(Target: TObject; X, Y: Integer); 4783begin 4784 {$IFDEF VerboseDrag} 4785 DebugLn('TCustomTreeView.DoEndDrag A ',Name,':',ClassName); 4786 {$ENDIF} 4787 inherited DoEndDrag(Target, X, Y); 4788 FLastDropTarget := nil; 4789 FDragScrollTimer.Enabled := false; 4790 ToolTips := FPrevToolTips; 4791end; 4792 4793function TCustomTreeView.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; 4794 MousePos: TPoint): Boolean; 4795var 4796 NDelta: integer; 4797begin 4798 Result:=inherited DoMouseWheel(Shift, WheelDelta, MousePos); 4799 if not Result then 4800 begin 4801 NDelta := (WheelDelta * Mouse.WheelScrollLines * DefaultItemHeight) div 120; 4802 ScrolledTop := ScrolledTop - NDelta; 4803 Result := true; 4804 UpdateScrollbars; 4805 end; 4806 UpdateTooltip(MousePos.X, MousePos.Y); 4807end; 4808 4809function TCustomTreeView.DoMouseWheelHorz(Shift: TShiftState; 4810 WheelDelta: Integer; MousePos: TPoint): Boolean; 4811var 4812 NDelta: integer; 4813const 4814 cScrollStep = 50; 4815begin 4816 Result:=inherited DoMouseWheelHorz(Shift, WheelDelta, MousePos); 4817 if not Result then 4818 begin 4819 NDelta := (WheelDelta * cScrollStep) div 120; 4820 ScrolledLeft := ScrolledLeft + NDelta; 4821 Result := true; 4822 end; 4823 UpdateTooltip(MousePos.X, MousePos.Y); 4824end; 4825 4826function TCustomTreeView.DoDragMsg(ADragMessage: TDragMessage; APosition: TPoint; ADragObject: TDragObject; ATarget: TControl; ADocking: Boolean):LRESULT; 4827begin 4828 Result:=inherited; 4829 {$IFDEF VerboseDrag} 4830 DebugLn('TCustomTreeView.DoDragMsg ',Name,':',ClassName,' ',IntToStr(ord(ADragMessage))); 4831 {$ENDIF} 4832 case ADragMessage of 4833 {dmDragMove: 4834 begin 4835 P:=ScreenToClient(Pos); 4836 DoDragOver(Source, P.X, P.Y, AMessage.Result <> 0); 4837 end;} 4838 dmDragLeave: 4839 begin 4840 ADragObject.HideDragImage; 4841 FLastDropTarget := DropTarget; 4842 DropTarget := nil; 4843 ADragObject.ShowDragImage; 4844 end; 4845 dmDragDrop: FLastDropTarget := nil; 4846 end; 4847end; 4848 4849procedure TCustomTreeView.DragOver(Source: TObject; X,Y: Integer; 4850 State: TDragState; var Accept: Boolean); 4851var 4852 Node: TTreeNode; 4853begin 4854 inherited DragOver(Source,X,Y,State,Accept); 4855 Node := GetNodeAt(X, Y); 4856 {$IFDEF VerboseDrag} 4857 DebugLn(['TCustomTreeView.DragOver ',Name,':',ClassName,' ',Node<>nil,' ',Node <> DropTarget,' ',Node = FLastDropTarget]); 4858 DebugLn(['TCustomTreeView.DragOver Source ',Source,':',Source.ClassName]); 4859 {$ENDIF} 4860 if (Node <> nil) 4861 and ((Node <> DropTarget) or (Node = FLastDropTarget)) then 4862 begin 4863 FLastDropTarget := nil; 4864 Node.DropTarget := True; 4865 end; 4866end; 4867 4868procedure TCustomTreeView.DoPaint; 4869var 4870 a,HalfBorderWidth:integer; 4871 SpaceRect, DrawRect: TRect; 4872 Node: TTreeNode; 4873 InsertMarkRect: TRect; 4874begin 4875 if [tvsPainting] * FStates <> [] then Exit; 4876 Include(FStates, tvsPainting); 4877 try 4878 if Focused then 4879 Include(FStates,tvoFocusedPainting) 4880 else 4881 Exclude(FStates,tvoFocusedPainting); 4882 if (tvoAutoItemHeight in fOptions) then 4883 UpdateDefaultItemHeight; 4884 //UpdateScrollbars; 4885 with Canvas do 4886 begin 4887 if IsCustomDrawn(dtControl, cdPrePaint) then 4888 begin 4889 DrawRect := ClientRect; 4890 if not CustomDraw(DrawRect, cdPrePaint) then exit; 4891 end; 4892 // draw nodes 4893 Node := TopItem; 4894 //write('[TCustomTreeView.DoPaint] A Node=',DbgS(Node)); 4895 //if Node<>nil then DebugLn(' Node.Text=',Node.Text) else DebugLn(''); 4896 while Node <> nil do 4897 begin 4898 if Node.Visible then 4899 DoPaintNode(Node); 4900 Node := Node.GetNextVisible; 4901 //write('[TCustomTreeView.DoPaint] B Node=',DbgS(Node)); 4902 //if Node<>nil then DebugLn(' Node.Text=',Node.Text) else DebugLn(''); 4903 end; 4904 SpaceRect := Rect(BorderWidth, BorderWidth, 4905 ClientWidth - BorderWidth, 4906 ClientHeight - BorderWidth); 4907 // draw insert mark for new root node 4908 if (InsertMarkType = tvimAsFirstChild) and (Items.Count = 0) then 4909 begin 4910 Pen.Color := FTreeLineColor; 4911 Brush.Color := FSelectedColor; 4912 InsertMarkRect := SpaceRect; 4913 InsertMarkRect.Bottom := InsertMarkRect.Top + 2; 4914 Rectangle(InsertMarkRect); 4915 SpaceRect.Top := InsertMarkRect.Bottom; 4916 end; 4917 // draw unused space below nodes 4918 Node := BottomItem; 4919 if Node <> nil then 4920 SpaceRect.Top := Node.Top + Node.Height - FScrolledTop + BorderWidth; 4921 //if Node<>nil then DebugLn('BottomItem=',BottomItem.text) else DebugLn('NO BOTTOMITEM!!!!!!!!!'); 4922 // TWinControl(Parent).InvalidateRect(Self,SpaceRect,true); 4923 if (Color <> clNone) and (SpaceRect.Top < SpaceRect.Bottom) then 4924 begin 4925 //DebugLn(' SpaceRect=',SpaceRect.Left,',',SpaceRect.Top,',',SpaceRect.Right,',',SpaceRect.Bottom); 4926 Brush.Color := Color; 4927 FillRect(SpaceRect); 4928 end; 4929 // draw border 4930 HalfBorderWidth := BorderWidth shr 1; 4931 Pen.Color := clGray; 4932 for a := 0 to BorderWidth - 1 do 4933 begin 4934 if a = HalfBorderWidth then 4935 Pen.Color := clBlack; 4936 MoveTo(a, ClientHeight - 1 - a); 4937 LineTo(a, a); 4938 LineTo(ClientWidth - 1 - a, a); 4939 end; 4940 Pen.Color := clWhite; 4941 for a := 0 to BorderWidth - 1 do 4942 begin 4943 if a = HalfBorderWidth then 4944 Pen.Color := clLtGray; 4945 MoveTo(ClientWidth - 1 - a, a); 4946 LineTo(ClientWidth - 1 - a, ClientHeight - 1 - a); 4947 LineTo(a, ClientHeight - 1 - a); 4948 end; 4949 if IsCustomDrawn(dtControl, cdPostPaint) then 4950 begin 4951 DrawRect := ClientRect; 4952 if not CustomDraw(DrawRect, cdPostPaint) then exit; 4953 end; 4954 end; 4955 finally 4956 Exclude(FStates, tvsPainting); 4957 end; 4958end; 4959 4960function InvertNdColor(AColor: TColor): TColor; 4961var 4962 Red, Green, Blue: integer; 4963begin 4964 if AColor<>clHighlight then begin 4965 Result:=clWhite; 4966 Red:=(AColor shr 16) and $ff; 4967 Green:=(AColor shr 8) and $ff; 4968 Blue:=AColor and $ff; 4969 if Red+Green+Blue>$180 then 4970 Result:=clBlack; 4971 //DebugLn(['[TCustomTreeView.DoPaintNode.InvertColor] Result=',Result,' ',Red,',',Green,',',Blue]); 4972 end 4973 else 4974 Result := clHighlightText; 4975end; 4976 4977procedure TCustomTreeView.DoPaintNode(Node: TTreeNode); 4978var 4979 NodeRect: TRect; 4980 VertMid, VertDelta, RealExpandSignSize, RealIndent: integer; 4981 NodeSelected, HasExpandSign: boolean; 4982 4983 procedure DrawVertLine(X, Y1, Y2: Integer); 4984 begin 4985 if Y1 > Y2 then 4986 Exit; 4987 if TreeLinePenStyle = psPattern then 4988 begin 4989 // TODO: implement psPattern support in the LCL 4990 Y1 := Y1 + VertDelta; 4991 while Y1 < Y2 do 4992 begin 4993 Canvas.Pixels[X, Y1] := TreeLineColor; 4994 inc(Y1, 2); 4995 end; 4996 end 4997 else 4998 begin 4999 Canvas.MoveTo(X, Y1); 5000 Canvas.LineTo(X, Y2); 5001 end; 5002 end; 5003 5004 procedure DrawHorzLine(Y, X1, X2: Integer); 5005 begin 5006 if X1 > X2 then 5007 Exit; 5008 if TreeLinePenStyle = psPattern then 5009 begin 5010 // TODO: implement psPattern support in the LCL 5011 while X1 < X2 do 5012 begin 5013 Canvas.Pixels[X1, Y] := TreeLineColor; 5014 inc(X1, 2); 5015 end; 5016 end 5017 else 5018 begin 5019 Canvas.MoveTo(X1, Y); 5020 Canvas.LineTo(X2, Y); 5021 end; 5022 end; 5023 5024 function DrawTreeLines(CurNode: TTreeNode): integer; 5025 // paints tree lines, returns indent 5026 var 5027 CurMid: integer; 5028 begin 5029 if (CurNode <> nil) and ((tvoShowRoot in Options) or (CurNode.Parent<>nil)) then 5030 begin 5031 Result := DrawTreeLines(CurNode.Parent); 5032 if ShowLines then 5033 begin 5034 CurMid := Result + (RealIndent shr 1); 5035 if CurNode = Node then 5036 begin 5037 // draw horizontal line 5038 if HasExpandSign then 5039 DrawHorzLine(VertMid, CurMid + RealExpandSignSize div 2, Result + RealIndent) 5040 else 5041 DrawHorzLine(VertMid, CurMid, Result + RealIndent); 5042 end; 5043 5044 if (CurNode.GetNextVisibleSibling <> nil) then 5045 begin 5046 // draw vertical line to next brother 5047 if (CurNode = Node) and HasExpandSign then 5048 begin 5049 if (Node.Parent = nil) and (Node.GetPrevSibling = nil) then 5050 DrawVertLine(CurMid, VertMid + RealExpandSignSize div 2, NodeRect.Bottom) 5051 else 5052 begin 5053 DrawVertLine(CurMid, NodeRect.Top, VertMid); 5054 DrawVertLine(CurMid, VertMid + RealExpandSignSize div 2 + VertDelta, NodeRect.Bottom); 5055 end; 5056 end 5057 else 5058 if (Node.Parent = nil) and (Node.GetPrevSibling = nil) then 5059 DrawVertLine(CurMid, VertMid + VertDelta, NodeRect.Bottom) 5060 else 5061 DrawVertLine(CurMid, NodeRect.Top, NodeRect.Bottom); 5062 end else 5063 if (CurNode = Node) then 5064 begin 5065 // draw vertical line from top to horizontal line 5066 if HasExpandSign then 5067 begin 5068 if ((InsertMarkNode = Node) and (InsertMarkType = tvimAsNextSibling)) then 5069 begin 5070 DrawVertLine(CurMid, NodeRect.Top, VertMid); 5071 DrawVertLine(CurMid, VertMid + RealExpandSignSize div 2, NodeRect.Bottom - 1); 5072 end 5073 else 5074 DrawVertLine(CurMid, NodeRect.Top, VertMid); 5075 end 5076 else 5077 if ((InsertMarkNode = Node) and (InsertMarkType = tvimAsNextSibling)) then 5078 DrawVertLine(CurMid, NodeRect.Top, NodeRect.Bottom - 1) 5079 else 5080 DrawVertLine(CurMid, NodeRect.Top, VertMid); 5081 end; 5082 end; 5083 inc(Result, RealIndent); 5084 end else 5085 begin 5086 Result := BorderWidth - FScrolledLeft; 5087 if CurNode <> nil then // indent first level of tree with ShowRoot = false a bit 5088 inc(Result, RealIndent shr 2); 5089 end; 5090 end; 5091 5092 procedure DrawExpandSign(MidX, MidY: integer; CollapseSign: boolean); 5093 const 5094 PlusMinusDetail: array[Boolean {Hot}, Boolean {Expanded}] of TThemedTreeview = 5095 ( 5096 (ttGlyphClosed, ttGlyphOpened), 5097 (ttHotGlyphClosed, ttHotGlyphOpened) 5098 ); 5099 var 5100 HalfSize, ALeft, ATop, ARight, ABottom, SmallIndent: integer; 5101 Points: array[0..2] of TPoint; // for triangle 5102 Details: TThemedElementDetails; 5103 R: TRect; 5104 PrevColor: TColor; 5105 const 5106 cShiftHorzArrow = 2; //paint horz arrow N pixels upper than MidY 5107 begin 5108 HalfSize := RealExpandSignSize div 2; 5109 //if not Odd(RealExpandSignSize) then 5110 // Dec(HalfSize); 5111 ALeft := MidX - HalfSize; 5112 ARight := MidX + HalfSize; 5113 ATop := MidY - HalfSize; 5114 ABottom := MidY + HalfSize; 5115 5116 if Assigned(FOnCustomDrawArrow) then 5117 begin 5118 FOnCustomDrawArrow(Self, Rect(ALeft, ATop, ARight, ABottom), not CollapseSign); 5119 Exit 5120 end; 5121 5122 with Canvas do 5123 begin 5124 Pen.Color := FExpandSignColor; 5125 Pen.Style := psSolid; 5126 case ExpandSignType of 5127 tvestTheme: 5128 begin 5129 // draw a themed expand sign. Todo: track hot 5130 R := Rect(ALeft, ATop, ARight, ABottom); 5131 Details := ThemeServices.GetElementDetails(PlusMinusDetail[False, CollapseSign]); 5132 ThemeServices.DrawElement(Canvas.Handle, Details, R, nil); 5133 end; 5134 tvestPlusMinus: 5135 begin 5136 // draw a plus or a minus sign 5137 R := Rect(ALeft, ATop, ARight+1, ABottom+1); //+1 for centering of line in square 5138 Rectangle(R); 5139 SmallIndent := Scale96ToFont(2); 5140 MoveTo(R.Left + SmallIndent, MidY); 5141 LineTo(R.Right - SmallIndent, MidY); 5142 if not CollapseSign then 5143 begin 5144 MoveTo(MidX, R.Top + SmallIndent); 5145 LineTo(MidX, R.Bottom - SmallIndent); 5146 end; 5147 end; 5148 tvestArrow, 5149 tvestArrowFill: 5150 begin 5151 // draw an arrow. down for collapse and right for expand 5152 R := Rect(ALeft, ATop, ARight+1, ABottom+1); //+1 for simmetry of arrow 5153 if CollapseSign then 5154 begin 5155 // draw an arrow down 5156 Points[0] := Point(R.Left, MidY - cShiftHorzArrow); 5157 Points[1] := Point(R.Right - 1, MidY - cShiftHorzArrow); 5158 Points[2] := Point(MidX, R.Bottom - 1 - cShiftHorzArrow); 5159 end else 5160 begin 5161 // draw an arrow right 5162 Points[0] := Point(MidX - 1, ATop); 5163 Points[1] := Point(R.Right - 2, MidY); 5164 Points[2] := Point(MidX - 1, R.Bottom - 1); 5165 end; 5166 5167 if ExpandSignType = tvestArrowFill then 5168 begin 5169 PrevColor := Brush.Color; 5170 Brush.Color := ExpandSignColor; 5171 end; 5172 Polygon(Points, 3, False); 5173 if ExpandSignType = tvestArrowFill then 5174 begin 5175 Brush.Color := PrevColor; 5176 end; 5177 end; 5178 end; 5179 end; 5180 end; 5181 5182 procedure DrawInsertMark; 5183 var 5184 InsertMarkRect: TRect; 5185 x: Integer; 5186 begin 5187 case InsertMarkType of 5188 5189 tvimAsFirstChild: 5190 if InsertMarkNode=Node then begin 5191 // draw insert mark for new first child 5192 with Canvas do begin 5193 // draw virtual tree line 5194 Pen.Color:=TreeLineColor; 5195 // Pen.Style:=TreeLinePenStyle; ToDo: not yet implemented in all widgetsets 5196 x:=Node.DisplayExpandSignRight+RealIndent div 2; 5197 MoveTo(x,NodeRect.Bottom-3); 5198 LineTo(x,NodeRect.Bottom-2); 5199 x:=Node.DisplayExpandSignRight+RealIndent; 5200 LineTo(x,NodeRect.Bottom-2); 5201 Pen.Style:=psSolid; 5202 5203 // draw virtual rectangle 5204 Pen.Color:=TreeLineColor; 5205 Brush.Color:=FSelectedColor; 5206 InsertMarkRect:=Rect(x,NodeRect.Bottom-3, 5207 NodeRect.Right,NodeRect.Bottom-1); 5208 Rectangle(InsertMarkRect); 5209 end; 5210 end; 5211 5212 tvimAsPrevSibling: 5213 if InsertMarkNode=Node then begin 5214 // draw insert mark for new previous sibling 5215 with Canvas do begin 5216 // draw virtual tree line 5217 Pen.Color:=TreeLineColor; 5218 //Pen.Style:=TreeLinePenStyle; ToDo: not yet implemented in all widgetsets 5219 x:=Node.DisplayExpandSignLeft+RealIndent div 2; 5220 MoveTo(x,NodeRect.Top+1); 5221 x:=Node.DisplayExpandSignRight; 5222 LineTo(x,NodeRect.Top+1); 5223 Pen.Style:=psSolid; 5224 5225 // draw virtual rectangle 5226 Pen.Color:=TreeLineColor; 5227 Brush.Color:=FSelectedColor; 5228 InsertMarkRect:=Rect(x,NodeRect.Top, 5229 NodeRect.Right,NodeRect.Top+2); 5230 Rectangle(InsertMarkRect); 5231 end; 5232 end; 5233 5234 tvimAsNextSibling: 5235 if InsertMarkNode=Node then begin 5236 // draw insert mark for new next sibling 5237 with Canvas do begin 5238 // draw virtual tree line 5239 Pen.Color:=TreeLineColor; 5240 //Pen.Style:=TreeLinePenStyle; ToDo: not yet implemented in all widgetsets 5241 x:=Node.DisplayExpandSignLeft+RealIndent div 2; 5242 MoveTo(x,NodeRect.Bottom-3); 5243 LineTo(x,NodeRect.Bottom-2); 5244 x:=Node.DisplayExpandSignRight; 5245 LineTo(x,NodeRect.Bottom-2); 5246 Pen.Style:=psSolid; 5247 5248 // draw virtual rectangle 5249 Pen.Color:=TreeLineColor; 5250 Brush.Color:=FSelectedColor; 5251 InsertMarkRect:=Rect(x,NodeRect.Bottom-3, 5252 NodeRect.Right,NodeRect.Bottom-1); 5253 Rectangle(InsertMarkRect); 5254 end; 5255 end; 5256 5257 end; 5258 end; 5259 5260 procedure DrawBackground(IsSelected: Boolean; ARect: TRect); 5261 var 5262 Details: TThemedElementDetails; 5263 CurBackgroundColor,bclr: TColor; 5264 begin 5265 bclr:=Canvas.Brush.Color; 5266 try 5267 if (tvoRowSelect in Options) and IsSelected then 5268 if tvoThemedDraw in Options then 5269 begin 5270 if tvoFocusedPainting in FStates then 5271 Details := ThemeServices.GetElementDetails(ttItemSelected) 5272 else 5273 Details := ThemeServices.GetElementDetails(ttItemSelectedNotFocus); 5274 if ThemeServices.HasTransparentParts(Details) then 5275 begin 5276 Canvas.Brush.Color := Color; 5277 Canvas.FillRect(ARect); 5278 end; 5279 ThemeServices.DrawElement(Canvas.Handle, Details, ARect, nil); 5280 Exit; 5281 end 5282 else 5283 CurBackgroundColor := FSelectedColor 5284 else 5285 CurBackgroundColor := Color; 5286 if CurBackgroundColor <> clNone then 5287 begin 5288 Canvas.Brush.Color := CurBackgroundColor; 5289 Canvas.FillRect(ARect); 5290 end; 5291 finally 5292 Canvas.Brush.Color := bclr; 5293 end; 5294 end; 5295 5296 procedure DrawNodeText(IsSelected: Boolean; NdRect: TRect; AText: String); 5297 var 5298 Details: TThemedElementDetails; 5299 NeedUnderline: Boolean; 5300 PrevFontStyle: TFontStyles; 5301 PrevFontColor: TColor; 5302 begin 5303 if IsSelected then 5304 begin 5305 if tvoFocusedPainting in FStates then 5306 Details := ThemeServices.GetElementDetails(ttItemSelected) 5307 else 5308 Details := ThemeServices.GetElementDetails(ttItemSelectedNotFocus); 5309 if not (tvoRowSelect in Options) then 5310 if (tvoThemedDraw in Options) then 5311 ThemeServices.DrawElement(Canvas.Handle, Details, NdRect, nil) 5312 else 5313 begin 5314 Canvas.Brush.Color := FSelectedColor; 5315 Canvas.Font.Color := IfThen(FSelectedFontColorUsed, 5316 FSelectedFontColor, InvertNdColor(FSelectedColor)); 5317 Canvas.FillRect(NdRect); 5318 end 5319 else 5320 if not (tvoThemedDraw in Options) then 5321 begin 5322 Canvas.Brush.Color := FSelectedColor; 5323 Canvas.Font.Color := IfThen(FSelectedFontColorUsed, 5324 FSelectedFontColor, InvertNdColor(FSelectedColor)); 5325 Canvas.FillRect(NdRect); 5326 end; 5327 end 5328 else 5329 Details := ThemeServices.GetElementDetails(ttItemNormal); 5330 5331 NeedUnderline := (tvoHotTrack in FOptions) and (Node=FNodeUnderCursor); 5332 if NeedUnderline then 5333 begin 5334 PrevFontStyle := Canvas.Font.Style; 5335 PrevFontColor := Canvas.Font.Color; 5336 Canvas.Font.Style := [fsUnderline]; 5337 if FHotTrackColor<>clNone then 5338 Canvas.Font.Color := FHotTrackColor; 5339 end; 5340 5341 NdRect.Offset(ScaleX(2, 96), 0); 5342 if (tvoThemedDraw in Options) then 5343 begin 5344 if not Enabled then 5345 Details.State := 4; // TmSchema.TREIS_DISABLED = 4 5346 ThemeServices.DrawText(Canvas, Details, AText, NdRect, DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_NOPREFIX, 0); 5347 end 5348 else 5349 begin 5350 if not Enabled and (FDisabledFontColor<>clNone) then 5351 Canvas.Font.Color := FDisabledFontColor; 5352 DrawText(Canvas.Handle, PChar(AText), -1, NdRect, DT_LEFT or DT_VCENTER or DT_SINGLELINE or DT_NOPREFIX); 5353 end; 5354 5355 if NeedUnderline then 5356 begin 5357 Canvas.Font.Style := PrevFontStyle; 5358 Canvas.Font.Color := PrevFontColor; 5359 end; 5360 end; 5361 5362 5363var 5364 x, ImgIndex: integer; 5365 CurTextRect, ImgRect: TRect; 5366 DrawState: TCustomDrawState; 5367 PaintImages: boolean; 5368 OverlayIndex: Integer; 5369 ImageRes, StateImageRes: TScaledImageListResolution; 5370begin 5371 if Assigned(FImages) then 5372 ImageRes := Images.ResolutionForPPI[ImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor]; 5373 if Assigned(FStateImages) then 5374 StateImageRes := StateImages.ResolutionForPPI[StateImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor]; 5375 RealExpandSignSize := ExpandSignSize; 5376 RealIndent := Indent; 5377 NodeRect := Node.DisplayRect(False); 5378 if (NodeRect.Bottom < 0) or (NodeRect.Top >= ClientHeight) then 5379 Exit; 5380 NodeSelected := (Node.Selected) or (Node.MultiSelected); 5381 Canvas.Font.Color := Font.Color; 5382 Canvas.Brush.Color := Color; 5383 PaintImages := True; 5384 if IsCustomDrawn(dtItem, cdPrePaint) then 5385 begin 5386 DrawState := []; 5387 if NodeSelected then 5388 Include(DrawState, cdsSelected); 5389 if Node.Focused then 5390 Include(DrawState, cdsFocused); 5391 if Node.MultiSelected then 5392 Include(DrawState, cdsMarked); 5393 if not CustomDrawItem(Node, DrawState, cdPrePaint, PaintImages) then Exit; 5394 end; 5395 5396 VertMid := NodeRect.Top + (NodeRect.Bottom - NodeRect.Top) div 2; 5397 HasExpandSign := ShowButtons and Node.HasChildren and ((tvoShowRoot in Options) or (Node.Parent <> nil)); 5398 VertDelta := Ord(FDefItemHeight and 3 = 2); 5399 //DebugLn(['[TCustomTreeView.DoPaintNode] Node=',DbgS(Node),' Node.Text=',Node.Text,' NodeRect=',NodeRect.Left,',',NodeRect.Top,',',NodeRect.Right,',',NodeRect.Bottom,' VertMid=',VertMid]); 5400 with Canvas do 5401 begin 5402 // draw background 5403 DrawBackground(NodeSelected, NodeRect); 5404 5405 // draw tree lines 5406 Pen.Color := TreeLineColor; 5407 Pen.Style := TreeLinePenStyle; 5408 if Pen.Style = psPattern then 5409 Pen.SetPattern(FTreeLinePenPattern); 5410 x := DrawTreeLines(Node); 5411 Pen.Style := psSolid; 5412 5413 // draw expand sign 5414 if HasExpandSign then 5415 DrawExpandSign(x - RealIndent + (RealIndent shr 1), VertMid, Node.Expanded); 5416 5417 // draw state icon 5418 if (StateImages <> nil) then 5419 begin 5420 if (Node.StateIndex >= 0) and (Node.StateIndex < StateImages.Count) then 5421 begin 5422 if PaintImages then 5423 StateImageRes.Draw(Canvas, x + 1, NodeRect.Top +(NodeRect.Bottom - NodeRect.Top - StateImageRes.Height) div 2, 5424 Node.StateIndex, True); 5425 Inc(x, StateImageRes.Width + FDefItemSpace); 5426 end; 5427 end; 5428 5429 // draw icon 5430 if (Images = nil) then 5431 begin 5432 imgRect := NodeRect; 5433 imgRect.Left := x+1; 5434 inc(x, DrawBuiltinIcon(Node, imgRect).CX + FDefItemSpace); 5435 end else 5436 begin 5437 if FSelectedNode <> Node then 5438 begin 5439 GetImageIndex(Node); 5440 ImgIndex := Node.ImageIndex 5441 end 5442 else 5443 begin 5444 GetSelectedIndex(Node); 5445 ImgIndex := Node.SelectedIndex; 5446 end; 5447 if (ImgIndex >= 0) and (ImgIndex < Images.Count) then 5448 begin 5449 if PaintImages then 5450 begin 5451 if (Node.OverlayIndex >= 0) then begin 5452 OverlayIndex:=Node.OverlayIndex; 5453 if Images.HasOverlays then begin 5454 ImageRes.DrawOverlay(Canvas, x + 1, NodeRect.Top + (NodeRect.Bottom - NodeRect.Top - ImageRes.Height) div 2, 5455 ImgIndex, OverlayIndex, Node.FNodeEffect); 5456 end else begin 5457 // draw the Overlay using the image from the list 5458 // set an Overlay 5459 Images.OverLay(OverlayIndex,0); 5460 // draw overlay 5461 ImageRes.DrawOverlay(Canvas, x + 1, NodeRect.Top + (NodeRect.Bottom - NodeRect.Top - ImageRes.Height) div 2, 5462 ImgIndex, 0, Node.FNodeEffect); 5463 // reset the Overlay 5464 Images.OverLay(-1,0); 5465 end; 5466 end 5467 else begin 5468 ImageRes.Draw(Canvas, x + 1, NodeRect.Top + (NodeRect.Bottom - NodeRect.Top - ImageRes.Height) div 2, 5469 ImgIndex, Node.FNodeEffect); 5470 end; 5471 end; 5472 Inc(x, ImageRes.Width + FDefItemSpace); 5473 end; 5474 end; 5475 5476 // draw text 5477 if Node.Text <> '' then 5478 begin 5479 CurTextRect := NodeRect; 5480 CurTextRect.Left := x; 5481 CurTextRect.Right := x + TextWidth(Node.Text) + (FDefItemSpace * 2); 5482 DrawNodeText(NodeSelected, CurTextRect, Node.Text); 5483 end; 5484 5485 // draw separator 5486 if (tvoShowSeparators in FOptions) then 5487 begin 5488 Pen.Color:=SeparatorColor; 5489 MoveTo(NodeRect.Left,NodeRect.Bottom-1); 5490 LineTo(NodeRect.Right,NodeRect.Bottom-1); 5491 end; 5492 5493 // draw insert mark 5494 DrawInsertMark; 5495 end; 5496 PaintImages := true; 5497 if IsCustomDrawn(dtItem, cdPostPaint) then 5498 begin 5499 DrawState:=[]; 5500 if Node.Selected then 5501 Include(DrawState,cdsSelected); 5502 if Node.Focused then 5503 Include(DrawState,cdsFocused); 5504 if Node.MultiSelected then 5505 Include(DrawState,cdsMarked); 5506 if not CustomDrawItem(Node,DrawState,cdPostPaint,PaintImages) then exit; 5507 end; 5508end; 5509 5510function TCustomTreeView.DrawBuiltinIcon(ANode: TTreeNode; ARect: TRect): TSize; 5511begin 5512 Result := Size(0, 0); 5513end; 5514 5515procedure TCustomTreeView.GetImageIndex(Node: TTreeNode); 5516begin 5517 if Assigned(FOnGetImageIndex) then 5518 FOnGetImageIndex(Self, Node); 5519end; 5520 5521procedure TCustomTreeView.GetSelectedIndex(Node: TTreeNode); 5522begin 5523 if Assigned(FOnGetSelectedIndex) then FOnGetSelectedIndex(Self, Node); 5524end; 5525 5526function TCustomTreeView.CanChange(Node: TTreeNode): Boolean; 5527begin 5528 Result := True; 5529 if Assigned(Node) and Assigned(FOnChanging) then 5530 FOnChanging(Self, Node, Result); 5531end; 5532 5533procedure TCustomTreeView.Change(Node: TTreeNode); 5534begin 5535 if Assigned(FOnChange) then 5536 FOnChange(Self, Node); 5537end; 5538 5539procedure TCustomTreeView.Delete(Node: TTreeNode); 5540begin 5541 if Assigned(FOnDeletion) then FOnDeletion(Self, Node); 5542end; 5543 5544function TCustomTreeView.ExpandSignSizeIsStored: Boolean; 5545begin 5546 Result := FExpandSignSize >= 0; 5547end; 5548 5549procedure TCustomTreeView.Expand(Node: TTreeNode); 5550begin 5551 UpdateScrollbars; 5552 if Assigned(FOnExpanded) then 5553 FOnExpanded(Self, Node); 5554end; 5555 5556function TCustomTreeView.CanExpand(Node: TTreeNode): Boolean; 5557begin 5558 Result := True; 5559 if Assigned(FOnExpanding)then 5560 FOnExpanding(Self, Node, Result); 5561end; 5562 5563procedure TCustomTreeView.Collapse(Node: TTreeNode); 5564begin 5565 if csDestroying in ComponentState then 5566 exit; 5567 UpdateScrollbars; 5568 if Assigned(FOnCollapsed) then 5569 FOnCollapsed(Self, Node); 5570end; 5571 5572function TCustomTreeView.CanCollapse(Node: TTreeNode): Boolean; 5573begin 5574 Result := True; 5575 if csDestroying in ComponentState then 5576 exit; 5577 if Assigned(FOnCollapsing) then 5578 FOnCollapsing(Self, Node, Result); 5579end; 5580 5581function TCustomTreeView.CanEdit(Node: TTreeNode): Boolean; 5582begin 5583 Result := True; 5584 if Assigned(FOnEditing) then 5585 FOnEditing(Self, Node, Result); 5586end; 5587 5588procedure TCustomTreeView.EndEditing(Cancel: boolean); 5589var 5590 NewText: String; 5591 Node: TTreeNode; 5592begin 5593 //DebugLn(['TCustomTreeView.EndEditing ',DbgSName(Self),' ',tvsIsEditing in FStates,' ',DbgSName(FEditor)]); 5594 if not (tvsIsEditing in FStates) then exit; 5595 Exclude(FStates,tvsIsEditing); 5596 if FEditor<>nil then begin 5597 // get new value fom edit control and hide it 5598 NewText:=''; 5599 if not Cancel then 5600 NewText:=FEditor.Text; 5601 FEditor.Parent:=nil; 5602 5603 // commit new value 5604 if not Cancel then begin 5605 Node:=FEditingItem; 5606 if (Node<>nil) then begin 5607 if Assigned(OnEdited) then 5608 OnEdited(Self,Node,NewText); 5609 Node.Text:=NewText; 5610 end; 5611 end; 5612 if Assigned(FOnEditingEnd) then FOnEditingEnd(Self, FEditingItem, Cancel); 5613 end; 5614 FEditingItem := nil; 5615 Invalidate; 5616end; 5617 5618procedure TCustomTreeView.EnsureNodeIsVisible(ANode: TTreeNode); 5619var b: integer; 5620begin 5621 if ANode=nil then exit; 5622 ANode.ExpandParents; 5623 if ANode.Top<ScrolledTop then 5624 ScrolledTop:=ANode.Top 5625 else begin 5626 b:=ANode.Top+ANode.Height-GetNodeDrawAreaHeight; 5627 if ScrolledTop<b then ScrolledTop:=b; 5628 end; 5629end; 5630 5631function TCustomTreeView.CreateNode: TTreeNode; 5632var 5633 NewNodeClass: TTreeNodeClass; 5634begin 5635 Result := nil; 5636 if Assigned(FOnCustomCreateItem) then 5637 FOnCustomCreateItem(Self, Result); 5638 if Result = nil then 5639 begin 5640 NewNodeClass:=TTreeNode; 5641 DoCreateNodeClass(NewNodeClass); 5642 Result := NewNodeClass.Create(Items); 5643 end; 5644end; 5645 5646function TCustomTreeView.CreateNodes: TTreeNodes; 5647begin 5648 Result := TTreeNodes.Create(Self); 5649end; 5650 5651procedure TCustomTreeView.ImageListChange(Sender: TObject); 5652begin 5653 Invalidate; 5654end; 5655 5656function TCustomTreeView.IndentIsStored: Boolean; 5657begin 5658 Result := FIndent >= 0; 5659end; 5660 5661function TCustomTreeView.NodeIsSelected(aNode: TTreeNode): Boolean; 5662begin 5663 Result := Assigned(aNode) and 5664 (aNode.Selected or ((tvoAllowMultiselect in Options) and aNode.MultiSelected)); 5665end; 5666 5667procedure TCustomTreeView.MouseDown(Button: TMouseButton; Shift: TShiftState; 5668 X, Y: Integer); 5669var 5670 CursorNode: TTreeNode; 5671 CursorNdSelected: Boolean; 5672 LogicalX: Integer; 5673begin 5674 {$IFDEF VerboseDrag} 5675 DebugLn('TCustomTreeView.MouseDown A ',DbgSName(Self),' '); 5676 {$ENDIF} 5677 FMouseDownPos := Point(X,Y); 5678 FStates:=FStates-[tvsEditOnMouseUp,tvsSingleSelectOnMouseUp]; 5679 5680 CursorNode := GetNodeAt(X, Y); 5681 CursorNdSelected := NodeIsSelected(CursorNode); 5682 LogicalX:=X; 5683 5684 //change selection on right click 5685 if (Button = mbRight) and RightClickSelect and //right click 5686 (([ssDouble, ssTriple, ssQuad] * Shift) = []) and //single or first of a multi click 5687 not AllowMultiSelectWithCtrl(Shift) and //only when CTRL is not pressed 5688 (CursorNode <> nil) 5689 then 5690 begin 5691 if not (tvoRowSelect in Options) and 5692 (tvoEmptySpaceUnselect in Options) and 5693 (LogicalX >= CursorNode.DisplayStateIconLeft) and 5694 (LogicalX > CursorNode.DisplayTextRight) then 5695 ClearSelection 5696 else 5697 if not (tvoAllowMultiselect in Options) then 5698 Selected := CursorNode 5699 else 5700 if not CursorNdSelected then 5701 Items.SelectOnlyThis(CursorNode); 5702 end 5703 else // empty space below last node 5704 if (Button = mbRight) and RightClickSelect and (CursorNode = nil) and 5705 (tvoEmptySpaceUnselect in Options) then 5706 ClearSelection; 5707 5708 if not Focused and CanFocus then 5709 SetFocus; 5710 5711 inherited MouseDown(Button, Shift, X, Y); 5712 5713 //CursorNode must be reassigned again - e.g. in OnMouseDown the node can be deleted or moved. 5714 CursorNode := GetNodeWithExpandSignAt(LogicalX, Y); 5715 CursorNdSelected := NodeIsSelected(CursorNode); 5716 5717 //Flag is used for DblClick/TripleClick/QuadClick, so set it before testing ShiftState 5718 FMouseDownOnFoldingSign := 5719 Assigned(CursorNode) and CursorNode.HasChildren and ShowButtons and 5720 (LogicalX >= CursorNode.DisplayExpandSignLeft) and 5721 (LogicalX < CursorNode.DisplayExpandSignRight); 5722 5723 //change selection on left click 5724 if (Button = mbLeft) and //left click 5725 (([ssDouble, ssTriple, ssQuad] * Shift) = []) and //single or first of a multi click 5726 (CursorNode <> nil) then 5727 begin 5728 if FMouseDownOnFoldingSign then 5729 // mousedown occurred on expand sign -> expand/collapse 5730 CursorNode.Expanded := not CursorNode.Expanded 5731 else if (LogicalX >= CursorNode.DisplayStateIconLeft) or (tvoRowSelect in Options) then 5732 begin 5733 // mousedown occurred in text or icon -> select node and begin drag operation 5734 {$IFDEF VerboseDrag} 5735 DebugLn(['TCustomTreeView.MouseDown In Text ',DbgSName(Self),' MouseCapture=',MouseCapture]); 5736 {$ENDIF} 5737 if (Selected = CursorNode) and (LogicalX >= CursorNode.DisplayTextLeft) then 5738 Include(FStates, tvsEditOnMouseUp); 5739 if not (tvoAllowMultiselect in Options) then 5740 Selected := CursorNode 5741 else 5742 begin 5743 if AllowMultiSelectWithShift(Shift) then 5744 begin 5745 Exclude(FStates,tvsEditOnMouseUp); 5746 LockSelectionChangeEvent; 5747 try 5748 Items.MultiSelect(CursorNode, not AllowMultiSelectWithCtrl(Shift)); 5749 finally 5750 UnlockSelectionChangeEvent; 5751 end; 5752 end 5753 else if AllowMultiSelectWithCtrl(Shift) then 5754 begin 5755 Exclude(FStates,tvsEditOnMouseUp); 5756 CursorNode.MultiSelected:=not CursorNode.MultiSelected; 5757 if CursorNode.MultiSelected then 5758 FTreeNodes.FStartMultiSelected := CursorNode; 5759 end 5760 else 5761 begin 5762 if not CursorNdSelected then 5763 Items.SelectOnlyThis(CursorNode) 5764 else 5765 Include(FStates, tvsSingleSelectOnMouseUp); 5766 end; 5767 end; 5768 end 5769 else if tvoEmptySpaceUnselect in Options then 5770 ClearSelection; 5771 end 5772 else// multi click 5773 if not (tvoNoDoubleClickExpand in Options) and (ssDouble in Shift) 5774 and (Button = mbLeft) and (CursorNode<>nil) then 5775 CursorNode.Expanded := not CursorNode.Expanded 5776 else // empty space below last node 5777 if (Button = mbLeft) and (CursorNode = nil) and (tvoEmptySpaceUnselect in Options) and 5778 not AllowMultiSelectWithShift(Shift) and not AllowMultiSelectWithCtrl(Shift) then 5779 ClearSelection; 5780end; 5781 5782procedure TCustomTreeView.MouseMove(Shift: TShiftState; X, Y: Integer); 5783begin 5784 inherited MouseMove(Shift, x, y); 5785 if (tvoAutoInsertMark in FOptions) then 5786 UpdateInsertMark(X,Y); 5787 UpdateTooltip(X, Y); 5788 UpdateHotTrack(X, Y); 5789end; 5790 5791procedure TCustomTreeView.UpdateHotTrack(X, Y: Integer); 5792begin 5793 FNodeUnderCursor := nil; 5794 if Cursor = crHandPoint then 5795 Cursor := crDefault; 5796 if not (tvoHotTrack in FOptions) then Exit; 5797 5798 FNodeUnderCursor := GetNodeAt(X, Y); 5799 if Assigned(FNodeUnderCursor) then 5800 Cursor := crHandPoint; 5801 Invalidate; 5802end; 5803 5804procedure TCustomTreeView.MouseUp(Button: TMouseButton; Shift: TShiftState; 5805 X, Y: Integer); 5806var 5807 aMouseDownNode, aMouseUpNode: TTreeNode; 5808begin 5809 // must hide hint window in mouse up to receive redirected mouse up messages 5810 if (FHintWnd<>nil) and FHintWnd.Visible then 5811 FHintWnd.Hide; 5812 inherited MouseUp(Button, Shift, X, Y); 5813 if (Button = mbRight) and (Shift = [ssRight]) and Assigned(PopupMenu) then 5814 exit; 5815 if Button=mbLeft then 5816 begin 5817 MouseCapture := False; 5818 if FStates * [tvsDblClicked, tvsTripleClicked, tvsQuadClicked] = [] then 5819 begin 5820 //AquirePrimarySelection; 5821 aMouseDownNode:=GetNodeAt(FMouseDownPos.X,FMouseDownPos.Y); 5822 aMouseUpNode:=GetNodeAt(X,Y); 5823 if (abs(FMouseDownPos.X-X)+abs(FMouseDownPos.Y-Y)<10) 5824 and (aMouseDownNode=aMouseUpNode) then 5825 begin 5826 // mouse up on mouse-down node 5827 if (tvsEditOnMouseUp in FStates) and (not ReadOnly) then 5828 BeginEditing(Selected) 5829 else if (tvsSingleSelectOnMouseUp in FStates) then 5830 Items.SelectOnlyThis(aMouseUpNode); 5831 end; 5832 end; 5833 end; 5834 FStates:=FStates-[tvsDblClicked,tvsTripleClicked,tvsQuadClicked, 5835 tvsEditOnMouseUp,tvsSingleSelectOnMouseUp]; 5836end; 5837 5838procedure TCustomTreeView.MoveEnd(ASelect: Boolean); 5839var 5840 lNode: TTreeNode; 5841begin 5842 lNode := Items.GetLastExpandedSubNode; 5843 if lNode <> nil then 5844 MoveSelection(lNode, ASelect); 5845end; 5846 5847procedure TCustomTreeView.MoveHome(ASelect: Boolean); 5848var 5849 lNode: TTreeNode; 5850begin 5851 lNode := Items.GetFirstVisibleNode; 5852 if lNode <> nil then 5853 MoveSelection(lNode, ASelect); 5854end; 5855 5856procedure TCustomTreeView.MovePageDown(ASelect: Boolean); 5857var 5858 I: Integer; 5859 lNode: TTreeNode; 5860begin 5861 if tvoAllowMultiSelect in FOptions then 5862 lNode := FTreeNodes.FLastMultiSelected 5863 else 5864 lNode := Selected; 5865 if lNode = nil then 5866 lNode := Items.GetFirstVisibleNode; 5867 5868 if lNode <> nil then 5869 begin 5870 I := Pred(ClientHeight div DefaultItemHeight); 5871 5872 while (I > 0) do 5873 if lNode.GetNextExpanded <> nil then 5874 begin 5875 lNode := lNode.GetNextExpanded; 5876 Dec(I); 5877 end 5878 else Break; 5879 5880 end; 5881 if lNode <> nil then 5882 MoveSelection(lNode, ASelect); 5883end; 5884 5885procedure TCustomTreeView.MovePageUp(ASelect: Boolean); 5886var 5887 I: Integer; 5888 lNode: TTreeNode; 5889begin 5890 if tvoAllowMultiSelect in FOptions then 5891 lNode := FTreeNodes.FLastMultiSelected 5892 else 5893 lNode := Selected; 5894 if lNode = nil then 5895 lNode := Items.GetFirstVisibleNode; 5896 5897 if lNode <> nil then 5898 begin 5899 I := Pred(ClientHeight div DefaultItemHeight); 5900 5901 while (I > 0) do 5902 if lNode.GetPrevExpanded <> nil then 5903 begin 5904 lNode := lNode.GetPrevExpanded; 5905 Dec(I); 5906 end 5907 else Break; 5908 5909 end; 5910 if lNode <> nil then 5911 MoveSelection(lNode, ASelect); 5912end; 5913 5914procedure TCustomTreeView.MoveSelection(ANewNode: TTreeNode; ASelect: Boolean); 5915begin 5916 if tvoAllowMultiSelect in FOptions then 5917 begin 5918 if ASelect then 5919 FTreeNodes.MultiSelect(ANewNode, False) 5920 else begin 5921 FTreeNodes.SelectOnlyThis(ANewNode); 5922 end; 5923 end else 5924 Selected := ANewNode; 5925 ANewNode.MakeVisible; 5926 5927 UpdateScrollbars; 5928end; 5929 5930procedure TCustomTreeView.MouseLeave; 5931begin 5932 FStates:=FStates-[tvsDblClicked,tvsTripleClicked,tvsQuadClicked, 5933 tvsEditOnMouseUp,tvsSingleSelectOnMouseUp]; 5934 if Assigned(FHintWnd) and FHintWnd.Visible 5935 and ((WidgetSet.GetLCLCapability(lcTransparentWindow) = LCL_CAPABILITY_YES) 5936 or not PtInRect(ClientRect, ScreenToClient(Mouse.CursorPos))) then 5937 FHintWnd.Hide; 5938 5939 if tvoHotTrack in FOptions then 5940 begin 5941 FNodeUnderCursor:=nil; 5942 Cursor:=crDefault; 5943 Invalidate; 5944 end; 5945 5946 inherited MouseLeave; 5947end; 5948 5949procedure TCustomTreeView.NodeChanged(Node: TTreeNode; ChangeReason: TTreeNodeChangeReason); 5950begin 5951 if assigned(FOnNodeChanged) then 5952 OnNodeChanged(self,Node,ChangeReason); 5953end; 5954 5955function TCustomTreeView.NodeHasChildren(Node: TTreeNode): Boolean; 5956begin 5957 if Assigned(FOnHasChildren) then 5958 Result := FOnHasChildren(Self, Node) 5959 else 5960 Result := false; 5961end; 5962 5963procedure TCustomTreeView.Notification(AComponent: TComponent; Operation: TOperation); 5964begin 5965 inherited Notification(AComponent, Operation); 5966 if Operation = opRemove then begin 5967 if AComponent = Images then Images := nil; 5968 if AComponent = StateImages then StateImages := nil; 5969 end; 5970end; 5971 5972procedure TCustomTreeView.SetImages(Value: TCustomImageList); 5973var 5974 AImageHeight: Integer; 5975begin 5976 if Images = Value then exit; 5977 if Images <> nil then 5978 Images.UnRegisterChanges(FImageChangeLink); 5979 FImages := Value; 5980 if Images <> nil then begin 5981 Images.RegisterChanges(FImageChangeLink); 5982 Images.FreeNotification(Self); 5983 AImageHeight := Images.HeightForPPI[ImagesWidth, Font.PixelsPerInch]; 5984 if DefaultItemHeight<AImageHeight+FDefItemSpace then 5985 DefaultItemHeight:=AImageHeight+FDefItemSpace; 5986 end; 5987 Invalidate; 5988end; 5989 5990procedure TCustomTreeView.SetImagesWidth(const aImagesWidth: Integer); 5991begin 5992 if FImagesWidth = aImagesWidth then Exit; 5993 FImagesWidth := aImagesWidth; 5994 Invalidate; 5995end; 5996 5997procedure TCustomTreeView.SetInsertMarkNode(const AValue: TTreeNode); 5998var 5999 InvalidateNeeded: Boolean; 6000begin 6001 if FInsertMarkNode=AValue then exit; 6002 InvalidateNeeded:=IsInsertMarkVisible; 6003 FInsertMarkNode:=AValue; 6004 InvalidateNeeded:=InvalidateNeeded or IsInsertMarkVisible; 6005 if InvalidateNeeded then Invalidate; 6006end; 6007 6008procedure TCustomTreeView.SetInsertMarkType( 6009 const AValue: TTreeViewInsertMarkType); 6010var 6011 InvalidateNeeded: Boolean; 6012begin 6013 if FInsertMarkType=AValue then exit; 6014 InvalidateNeeded:=IsInsertMarkVisible; 6015 FInsertMarkType:=AValue; 6016 InvalidateNeeded:=InvalidateNeeded or IsInsertMarkVisible; 6017 if InvalidateNeeded then Invalidate; 6018end; 6019 6020procedure TCustomTreeView.SetStateImages(Value: TCustomImageList); 6021var 6022 AStateImageHeight: Integer; 6023begin 6024 if FStateImages=Value then exit; 6025 if StateImages <> nil then 6026 StateImages.UnRegisterChanges(FStateChangeLink); 6027 FStateImages := Value; 6028 if StateImages <> nil then begin 6029 StateImages.RegisterChanges(FStateChangeLink); 6030 StateImages.FreeNotification(Self); 6031 AStateImageHeight := StateImages.HeightForPPI[StateImagesWidth, Font.PixelsPerInch]; 6032 if DefaultItemHeight<AStateImageHeight+FDefItemSpace then 6033 DefaultItemHeight:=AStateImageHeight+FDefItemSpace; 6034 end; 6035 Invalidate; 6036end; 6037 6038procedure TCustomTreeView.SetStateImagesWidth(const aStateImagesWidth: Integer); 6039begin 6040 if FStateImagesWidth = aStateImagesWidth then Exit; 6041 FStateImagesWidth := aStateImagesWidth; 6042 Invalidate; 6043end; 6044 6045procedure TCustomTreeView.LoadFromFile(const FileName: string); 6046var 6047 Stream: TStream; 6048begin 6049 Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); 6050 try 6051 LoadFromStream(Stream); 6052 finally 6053 Stream.Free; 6054 end; 6055end; 6056 6057procedure TCustomTreeView.LoadFromStream(Stream: TStream); 6058begin 6059 with TTreeStrings.Create(Items) do 6060 try 6061 LoadTreeFromStream(Stream); 6062 finally 6063 Free; 6064 end; 6065end; 6066 6067procedure TCustomTreeView.SaveToFile(const FileName: string); 6068var 6069 Stream: TStream; 6070begin 6071 Stream := TFileStream.Create(FileName, fmCreate); 6072 try 6073 SaveToStream(Stream); 6074 finally 6075 Stream.Free; 6076 end; 6077end; 6078 6079procedure TCustomTreeView.SaveToStream(Stream: TStream); 6080begin 6081 with TTreeStrings.Create(Items) do 6082 try 6083 SaveTreeToStream(Stream); 6084 finally 6085 Free; 6086 end; 6087end; 6088 6089procedure TCustomTreeView.ScrollView(DeltaX, DeltaY: Integer); 6090var 6091 ScrollArea: TRect; 6092 ScrollFlags: Integer; 6093begin 6094 if (DeltaX=0) and (DeltaY=0) then 6095 Exit; 6096 6097 Include(FStates,tvsScrollbarChanged); 6098 ScrollFlags := SW_INVALIDATE or SW_ERASE; 6099 ScrollArea := ClientRect; 6100 InflateRect(ScrollArea, -BorderWidth, -BorderWidth); 6101 ScrollWindowEx(Handle, DeltaX, DeltaY, @ScrollArea, @ScrollArea, 0, nil, ScrollFlags); 6102 UpdateScrollbars; 6103end; 6104 6105procedure TCustomTreeView.WMVScroll(var Msg: TLMScroll); 6106begin 6107 case Msg.ScrollCode of 6108 // Scrolls to start / end of the text 6109 SB_TOP: ScrolledTop := 0; 6110 SB_BOTTOM: ScrolledTop := GetMaxScrollTop; 6111 // Scrolls one line up / down 6112 SB_LINEDOWN: ScrolledTop := ScrolledTop + FDefItemHeight; 6113 SB_LINEUP: ScrolledTop := ScrolledTop - FDefItemHeight; 6114 // Scrolls one page of lines up / down 6115 SB_PAGEDOWN: ScrolledTop := ScrolledTop + ClientHeight 6116 - FDefItemHeight; 6117 SB_PAGEUP: ScrolledTop := ScrolledTop - ClientHeight 6118 + FDefItemHeight; 6119 // Scrolls to the current scroll bar position 6120 SB_THUMBPOSITION, 6121 SB_THUMBTRACK: ScrolledTop := Msg.Pos; 6122 6123 SB_ENDSCROLL: ; // Ends scrolling 6124 end; 6125end; 6126 6127procedure TCustomTreeView.WMHScroll(var Msg: TLMScroll); 6128begin 6129 case Msg.ScrollCode of 6130 // Scrolls to start / end of the text 6131 SB_LEFT: ScrolledLeft := 0; 6132 SB_RIGHT: ScrolledLeft := GetMaxScrollLeft; 6133 // Scrolls one line left / right 6134 SB_LINERIGHT: ScrolledLeft := ScrolledLeft + FDefItemHeight div 2; 6135 SB_LINELEFT: ScrolledLeft := ScrolledLeft - FDefItemHeight div 2; 6136 // Scrolls one page of lines left / right 6137 SB_PAGERIGHT: ScrolledLeft := ScrolledLeft + ClientHeight 6138 - FDefItemHeight; 6139 SB_PAGELEFT: ScrolledLeft := ScrolledLeft - ClientHeight 6140 + FDefItemHeight; 6141 // Scrolls to the current scroll bar position 6142 SB_THUMBPOSITION, 6143 SB_THUMBTRACK: ScrolledLeft := Msg.Pos; 6144 6145 SB_ENDSCROLL: ;// Ends scrolling 6146 end; 6147end; 6148 6149procedure TCustomTreeView.WMLButtonDown(var AMessage: TLMLButtonDown); 6150begin 6151 {$IFDEF VerboseDrag} 6152 DebugLn('TCustomTreeView.WMLButtonDown A ',Name,':',ClassName,' '); 6153 {$ENDIF} 6154 Exclude(FStates,tvsDragged); 6155 inherited WMLButtonDown(AMessage); 6156 {$IFDEF VerboseDrag} 6157 DebugLn('TCustomTreeView.WMLButtonDown END ',Name,':',ClassName,' '); 6158 {$ENDIF} 6159end; 6160 6161procedure TCustomTreeView.WMSetFocus(var Message: TLMSetFocus); 6162begin 6163 Invalidate; 6164 inherited; 6165end; 6166 6167procedure TCustomTreeView.WMKillFocus(var Message: TLMKillFocus); 6168begin 6169 Invalidate; 6170 inherited; 6171end; 6172 6173procedure TCustomTreeView.Resize; 6174begin 6175 FStates:=FStates+[tvsScrollbarChanged,tvsBottomItemNeedsUpdate]; 6176 inherited Resize; 6177 UpdateScrollbars; 6178end; 6179 6180function TCustomTreeView.GetSelectedChildAccessibleObject: TLazAccessibleObject; 6181var 6182 lNode: TTreeNode; 6183begin 6184 Result := nil; 6185 lNode := GetSelection(); 6186 if lNode = nil then Exit; 6187 Result := FAccessibleObject.GetChildAccessibleObjectWithDataObject(lNode); 6188end; 6189 6190function TCustomTreeView.GetChildAccessibleObjectAtPos(APos: TPoint): TLazAccessibleObject; 6191var 6192 lNode: TTreeNode; 6193begin 6194 Result := nil; 6195 lNode := GetNodeAt(APos.X, APos.Y); 6196 //if lNode = nil then DebugLn('[TCustomTreeView.GetChildAccessibleObjectAtPos] lNode=nil') 6197 //else DebugLn('[TCustomTreeView.GetChildAccessibleObjectAtPos] lNode=' + lNode.Text); 6198 if lNode = nil then Exit; 6199 Result := FAccessibleObject.GetChildAccessibleObjectWithDataObject(lNode); 6200end; 6201 6202procedure TCustomTreeView.InternalSelectionChanged; 6203begin 6204 if FSelectionChangeEventLock > 0 then 6205 Include(FStates, tvsSelectionChanged) 6206 else 6207 begin 6208 Exclude(FStates, tvsSelectionChanged); 6209 DoSelectionChanged; 6210 FChangeTimer.Enabled := False; 6211 if not FCallingChange then // Prevent recursive loop from OnChange handler. 6212 FChangeTimer.Enabled := True; 6213 end; 6214end; 6215 6216function TCustomTreeView.AllowMultiSelectWithCtrl(AState: TShiftState): Boolean; 6217begin 6218 Result := (ssCtrl in AState) and (msControlSelect in FMultiSelectStyle); 6219end; 6220 6221function TCustomTreeView.AllowMultiSelectWithShift(AState: TShiftState): Boolean; 6222begin 6223 Result := (ssShift in AState) and (msShiftSelect in FMultiSelectStyle); 6224end; 6225 6226class procedure TCustomTreeView.WSRegisterClass; 6227begin 6228 inherited WSRegisterClass; 6229 RegisterCustomTreeView; 6230end; 6231 6232class function TCustomTreeView.GetControlClassDefaultSize: TSize; 6233begin 6234 Result.CX := 121; 6235 Result.CY := 97; 6236end; 6237 6238procedure TCustomTreeView.Added(Node: TTreeNode); 6239begin 6240 if Assigned(OnAddition) then OnAddition(Self,Node); 6241end; 6242 6243{ CustomDraw support } 6244 6245procedure TCustomTreeView.EditorEditingDone(Sender: TObject); 6246var 6247 WasFocused: Boolean; 6248begin 6249 WasFocused := (FEditor<>nil) and FEditor.Focused; 6250 EndEditing; 6251 if WasFocused then 6252 SetFocus; 6253end; 6254 6255procedure TCustomTreeView.EditorKeyDown(Sender: TObject; var Key: Word; 6256 Shift: TShiftState); 6257var 6258 WasFocused: Boolean; 6259begin 6260 if (Key = VK_ESCAPE) or (Key = VK_RETURN) then 6261 begin 6262 WasFocused := Assigned(FEditor) and FEditor.Focused; 6263 EndEditing(Key = VK_ESCAPE); 6264 if WasFocused then 6265 SetFocus; 6266 Key := 0; // key was handled 6267 end; 6268end; 6269 6270procedure TCustomTreeView.CanvasChanged(Sender: TObject); 6271begin 6272 Include(FStates,tvsCanvasChanged); 6273end; 6274 6275procedure TCustomTreeView.DragScrollTimerTick(Sender: TObject); 6276const 6277 cScrollDelta = 10; 6278var 6279 Pnt: TPoint; 6280begin 6281 Pnt := ScreenToClient(Mouse.CursorPos); 6282 if (Pnt.X >= 0) and (Pnt.X < ClientWidth) then 6283 begin 6284 if (Pnt.Y >= 0) and (Pnt.Y < FDragScrollMargin) then 6285 ScrolledTop := ScrolledTop - cScrollDelta 6286 else 6287 if (Pnt.Y >= ClientHeight-FDragScrollMargin) and (Pnt.Y < ClientHeight) then 6288 ScrolledTop := ScrolledTop + cScrollDelta; 6289 end; 6290end; 6291 6292procedure TCustomTreeView.ClearSelection(KeepPrimary: Boolean); 6293begin 6294 if tvoAllowMultiSelect in FOptions then 6295 Items.ClearMultiSelection(not KeepPrimary) 6296 else 6297 if not KeepPrimary then Selected := nil; 6298end; 6299 6300function TCustomTreeView.IsCustomDrawn(Target: TCustomDrawTarget; 6301 Stage: TCustomDrawStage): Boolean; 6302begin 6303 { Tree view doesn't support erase notifications } 6304 if Stage = cdPrePaint then begin 6305 if Target = dtItem then 6306 Result := Assigned(FOnCustomDrawItem) 6307 or Assigned(FOnAdvancedCustomDrawItem) 6308 else if Target = dtControl then 6309 Result := Assigned(FOnCustomDraw) or Assigned(FOnAdvancedCustomDraw) or 6310 Assigned(FOnCustomDrawItem) or Assigned(FOnAdvancedCustomDrawItem) 6311 else 6312 Result := False; 6313 end else begin 6314 if Target = dtItem then 6315 Result := Assigned(FOnAdvancedCustomDrawItem) 6316 else if Target = dtControl then 6317 Result := Assigned(FOnAdvancedCustomDraw) 6318 or Assigned(FOnAdvancedCustomDrawItem) 6319 else 6320 Result := False; 6321 end; 6322end; 6323 6324function TCustomTreeView.CustomDraw(const ARect: TRect; 6325 Stage: TCustomDrawStage): Boolean; 6326begin 6327 Result := True; 6328 if (Stage = cdPrePaint) and Assigned(FOnCustomDraw) then 6329 FOnCustomDraw(Self, ARect, Result); 6330 if Assigned(FOnAdvancedCustomDraw) then 6331 FOnAdvancedCustomDraw(Self, ARect, Stage, Result); 6332end; 6333 6334function TCustomTreeView.CustomDrawItem(Node: TTreeNode; 6335 State: TCustomDrawState; 6336 Stage: TCustomDrawStage; var PaintImages: Boolean): Boolean; 6337begin 6338 Result := True; 6339 PaintImages := True; 6340 if (Stage = cdPrePaint) and Assigned(FOnCustomDrawItem) then 6341 FOnCustomDrawItem(Self, Node, State, Result); 6342 if Assigned(FOnAdvancedCustomDrawItem) then 6343 FOnAdvancedCustomDrawItem(Self, Node, State, Stage, PaintImages, Result); 6344end; 6345 6346procedure TCustomTreeView.ConsistencyCheck; 6347var OldMaxRight, OldLastTop, OldMaxLvl: integer; 6348 OldTopItem, OldBottomItem: TTreeNode; 6349begin 6350 if Canvas=nil then 6351 RaiseGDBException('Canvas=nil'); 6352 if FDefItemHeight<0 then 6353 RaiseGDBException('FDefItemHeight='+IntToStr(FDefItemHeight)); 6354 if FIndent<0 then 6355 RaiseGDBException('FIndent='+IntToStr(FIndent)); 6356 if FMaxRight<0 then 6357 RaiseGDBException('FMaxRight='+IntToStr(FMaxRight)); 6358 if FTreeNodes=nil then 6359 RaiseGDBException('FTreeNodes=nil'); 6360 FTreeNodes.ConsistencyCheck; 6361 if Items.FUpdateCount<0 then 6362 RaiseGDBException('FUpdateCount='+IntToStr(Items.FUpdateCount)); 6363 if (not (tvsTopsNeedsUpdate in FStates)) then begin 6364 if Items.GetLastSubNode<>nil then begin 6365 OldLastTop:=Items.GetLastSubNode.Top; 6366 Include(FStates,tvsTopsNeedsUpdate); 6367 UpdateAllTops; 6368 if OldLastTop<>Items.GetLastSubNode.Top then 6369 RaiseGDBException('OldLastTop='+DbgS(OldLastTop) 6370 +'<>Items.GetLastSubNode.Top='+DbgS(Items.GetLastSubNode.Top)); 6371 end; 6372 end; 6373 if not (tvsMaxRightNeedsUpdate in FStates) then begin 6374 OldMaxRight:=FMaxRight; 6375 Include(FStates,tvsMaxRightNeedsUpdate); 6376 UpdateMaxRight; 6377 if OldMaxRight<>FMaxRight then 6378 RaiseGDBException('OldMaxRight<>FMaxRight'); 6379 end; 6380 if not (tvsMaxLvlNeedsUpdate in FStates) then begin 6381 OldMaxLvl:=FMaxLvl; 6382 Include(FStates,tvsMaxLvlNeedsUpdate); 6383 UpdateMaxLvl; 6384 if OldMaxLvl<>FMaxLvl then 6385 RaiseGDBException('OldMaxLvl<>FMaxLvl'); 6386 end; 6387 if (tvsIsEditing in FStates) and (FSelectedNode=nil) then 6388 RaiseGDBException(''); 6389 if (FSelectedNode<>nil) then begin 6390 if not FSelectedNode.IsVisible then 6391 RaiseGDBException('not FSelectedNode.IsVisible'); 6392 end; 6393 if not (tvsTopItemNeedsUpdate in FStates) then begin 6394 OldTopItem:=FTopItem; 6395 UpdateTopItem; 6396 if FTopItem<>OldTopItem then 6397 RaiseGDBException('FTopItem<>OldTopItem'); 6398 end; 6399 if not (tvsBottomItemNeedsUpdate in FStates) then begin 6400 OldBottomItem:=FBottomItem; 6401 UpdateBottomItem; 6402 if FBottomItem<>OldBottomItem then 6403 RaiseGDBException('FBottomItem<>OldBottomItem'); 6404 end; 6405end; 6406 6407procedure TCustomTreeView.WriteDebugReport(const Prefix: string; AllNodes: boolean); 6408begin 6409 DbgOut('%s%s.WriteDebugReport Self=%p', [Prefix, ClassName, Pointer(Self)]); 6410 ConsistencyCheck; 6411 DebugLn(''); 6412 if AllNodes then begin 6413 Items.WriteDebugReport(Prefix+' ',true); 6414 end; 6415end; 6416 6417procedure TCustomTreeView.LockSelectionChangeEvent; 6418begin 6419 inc(FSelectionChangeEventLock); 6420end; 6421 6422procedure TCustomTreeView.UnlockSelectionChangeEvent; 6423begin 6424 dec(FSelectionChangeEventLock); 6425 if FSelectionChangeEventLock<0 then 6426 RaiseGDBException('TCustomTreeView.UnlockSelectionChangeEvent'); 6427 if (FSelectionChangeEventLock=0) and (tvsSelectionChanged in FStates) then 6428 InternalSelectionChanged; 6429end; 6430 6431function TCustomTreeView.GetFirstMultiSelected: TTreeNode; 6432begin 6433 Result := Items.FFirstMultiSelected; 6434end; 6435 6436function TCustomTreeView.GetLastMultiSelected: TTreeNode; 6437begin 6438 Result := Items.FLastMultiSelected; 6439end; 6440 6441procedure TCustomTreeView.Select(Node: TTreeNode; ShiftState: TShiftState = []); 6442begin 6443 if (tvoAllowMultiSelect in FOptions) and AllowMultiSelectWithCtrl(ShiftState) then 6444 Node.Selected := True 6445 else begin 6446 ClearSelection; 6447 Selected := Node; 6448 if (tvoAllowMultiSelect in FOptions) then 6449 Node.Selected := True; 6450 end; 6451end; 6452 6453procedure TCustomTreeView.Select(const Nodes: array of TTreeNode); 6454var 6455 I: Integer; 6456begin 6457 ClearSelection; 6458 if Length(Nodes)>0 then begin 6459 Selected := Nodes[0]; 6460 if tvoAllowMultiSelect in FOptions then 6461 for I := Low(Nodes) to High(Nodes) do 6462 Nodes[I].Selected := True; 6463 end; 6464end; 6465 6466procedure TCustomTreeView.Select(Nodes: TList); 6467var 6468 I: Integer; 6469begin 6470 ClearSelection; 6471 if Nodes.Count>0 then begin 6472 Selected := TTreeNode(Nodes[0]); 6473 if tvoAllowMultiSelect in FOptions then 6474 for I := 0 to Nodes.Count - 1 do 6475 TTreeNode(Nodes[I]).Selected := True; 6476 end; 6477end; 6478 6479function TCustomTreeView.SelectionVisible: boolean; 6480var 6481 ANode: TTreeNode; 6482begin 6483 ANode:=GetFirstMultiSelected; 6484 if (ANode<>nil) and (ANode.GetNextMultiSelected<>nil) then begin 6485 // 2 or more elements => a real multi selection => 6486 // is visible if even one of its nodes is partly visible 6487 while (ANode<>nil) do begin 6488 if ANode.IsVisible then begin 6489 Result:=true; 6490 exit; 6491 end; 6492 ANode:=ANode.GetNextMultiSelected; 6493 end; 6494 Result:=false; 6495 end else begin 6496 if ANode=nil then 6497 ANode:=Selected; 6498 Result:=(ANode<>nil) and (ANode.IsFullHeightVisible); 6499 end; 6500end; 6501 6502procedure TCustomTreeView.MakeSelectionVisible; 6503var 6504 ANode: TTreeNode; 6505begin 6506 if SelectionVisible then exit; 6507 ANode:=GetFirstMultiSelected; 6508 if (ANode=nil) then 6509 ANode:=Selected; 6510 if ANode=nil then exit; 6511 ANode.MakeVisible; 6512end; 6513 6514procedure TCustomTreeView.ClearInvisibleSelection; 6515var 6516 ANode: TTreeNode; 6517begin 6518 if tvoAllowMultiSelect in FOptions then begin 6519 Items.ClearMultiSelection(True); // Now clears all multi-selected 6520 end 6521 else begin 6522 ANode := Selected; // Clear a single selection only if not visible 6523 if Assigned(ANode) and not ANode.Visible then 6524 ANode.Selected:=False; // Selected := nil; 6525 end; 6526end; 6527 6528procedure TCustomTreeView.MoveToNextNode(ASelect: Boolean); 6529var 6530 ANode: TTreeNode; 6531begin 6532 if tvoAllowMultiSelect in FOptions then 6533 ANode := FTreeNodes.FLastMultiSelected 6534 else 6535 ANode := Selected; 6536 if ANode <> nil then 6537 ANode := ANode.GetNextVisible 6538 else 6539 ANode := FTreeNodes.GetFirstVisibleNode; 6540 if ANode <> nil then 6541 MoveSelection(ANode, ASelect); 6542end; 6543 6544procedure TCustomTreeView.MoveToPrevNode(ASelect: Boolean); 6545var 6546 ANode: TTreeNode; 6547begin 6548 if tvoAllowMultiSelect in FOptions then 6549 ANode := FTreeNodes.FLastMultiSelected 6550 else 6551 ANode := Selected; 6552 if ANode <> nil then 6553 ANode := ANode.GetPrevVisible 6554 else 6555 ANode := Items.GetFirstVisibleNode; 6556 if ANode <> nil then 6557 MoveSelection(ANode, ASelect); 6558end; 6559 6560function TCustomTreeView.StoreCurrentSelection: TStringList; 6561var 6562 ANode: TTreeNode; 6563begin 6564 Result:=TStringList.Create; 6565 ANode:=Selected; 6566 while ANode<>nil do begin 6567 Result.Insert(0,ANode.Text); 6568 ANode:=ANode.Parent; 6569 end; 6570end; 6571 6572procedure TCustomTreeView.ApplyStoredSelection(ASelection: TStringList; FreeList: boolean); 6573var 6574 ANode: TTreeNode; 6575 CurText: string; 6576begin 6577 ANode:=nil; 6578 while ASelection.Count>0 do begin 6579 CurText:=ASelection[0]; 6580 if ANode=nil then 6581 ANode:=Items.GetFirstNode 6582 else 6583 ANode:=ANode.GetFirstChild; 6584 while (ANode<>nil) and (ANode.Text<>CurText) do 6585 ANode:=ANode.GetNextSibling; 6586 if ANode=nil then break; 6587 ASelection.Delete(0); 6588 end; 6589 if ANode<>nil then 6590 Selected:=ANode; 6591 if FreeList then 6592 ASelection.Free; 6593end; 6594 6595// back to comctrls.pp 6596