fctreeview.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:166k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1.     else begin
  2.        if (tvoEditText in Options) then SetComCtlStyle(Self, TVS_EDITLABELS, True);
  3.     end;
  4.   end;
  5. end;
  6. {procedure TfcCustomTreeView.SetHideSelection(Value: Boolean);
  7. begin
  8.   if HideSelection <> Value then
  9.   begin
  10.     FHideSelection := Value;
  11.     SetComCtlStyle(Self, TVS_SHOWSELALWAYS, not Value);
  12.     Invalidate;
  13.   end;
  14. end;
  15. }
  16. function TfcCustomTreeView.GetNodeAt(X, Y: Integer): TfcTreeNode;
  17. var
  18.   HitTest: TTVHitTestInfo;
  19. begin
  20.   with HitTest do
  21.   begin
  22.     pt.X := X;
  23.     pt.Y := Y;
  24.     if TreeView_HitTest(Handle, HitTest) <> nil then
  25.       Result := Items.GetNode(HitTest.hItem)
  26.     else Result := nil;
  27.   end;
  28. end;
  29. function TfcCustomTreeView.GetHitTestInfoAt(X, Y: Integer): TfcHitTests;
  30. var
  31.   HitTest: TTVHitTestInfo;
  32.   DisplayRect: TRect;
  33.   Node: TfcTreeNode;
  34. begin
  35.   Result := [];
  36.   with HitTest do
  37.   begin
  38.     pt.X := X;
  39.     pt.Y := Y;
  40.     TreeView_HitTest(Handle, HitTest);
  41.     if (flags and TVHT_ABOVE) <> 0 then Include(Result, fchtAbove);
  42.     if (flags and TVHT_BELOW) <> 0 then Include(Result, fchtBelow);
  43.     if (flags and TVHT_NOWHERE) <> 0 then Include(Result, fchtNowhere);
  44.     if (flags and TVHT_ONITEM) <> 0 then Include(Result, fchtOnItem);
  45.     if (flags and TVHT_ONITEMBUTTON) <> 0 then Include(Result, fchtOnButton);
  46.     if (flags and TVHT_ONITEMICON) <> 0 then Include(Result, fchtOnIcon);
  47.     if (flags and TVHT_ONITEMINDENT) <> 0 then Include(Result, fchtOnIndent);
  48.     if (flags and TVHT_ONITEMLABEL) <> 0 then Include(Result, fchtOnLabel);
  49.     if (flags and TVHT_ONITEMRIGHT) <> 0 then Include(Result, fchtOnRight);
  50.     if (flags and TVHT_ONITEMSTATEICON) <> 0 then Include(Result, fchtOnStateIcon);
  51.     if (flags and TVHT_TOLEFT) <> 0 then Include(Result, fchtToLeft);
  52.     if (flags and TVHT_TORIGHT) <> 0 then Include(Result, fchtToRight);
  53.     if (fchtOnItem in Result) then
  54.     begin
  55.        node:= GetNodeAt(X, Y);
  56.        if (Images<>Nil) and (node<>nil) and (node.imageindex=-2) then
  57.        begin
  58.           DisplayRect:= Node.DisplayRect(True);
  59.           if (x>=DisplayRect.Left) and (x<=DisplayRect.Right) then
  60.           begin
  61.              Include(Result, fchtOnLabel);
  62.              Exclude(Result, fchtOnIcon);
  63.           end
  64.        end
  65.     end;
  66.   end;
  67. end;
  68. procedure TfcCustomTreeView.SeTfcTreeNodes(Value: TfcTreeNodes);
  69. begin
  70.   Items.Assign(Value);
  71. end;
  72. procedure TfcCustomTreeView.SetIndent(Value: Integer);
  73. begin
  74.   if Value <> Indent then TreeView_SetIndent(Handle, Value);
  75. end;
  76. function TfcCustomTreeView.GetIndent: Integer;
  77. begin
  78.   Result := TreeView_GetIndent(Handle)
  79. end;
  80. procedure TfcCustomTreeView.FullExpand;
  81. var
  82.   Node: TfcTreeNode;
  83. begin
  84.   Node := Items.GetFirstNode;
  85.   while Node <> nil do
  86.   begin
  87.     Node.Expand(True);
  88.     Node := Node.GetNextSibling;
  89.   end;
  90.   ResetStateImages;
  91. end;
  92. procedure TfcCustomTreeView.FullCollapse;
  93. var
  94.   Node: TfcTreeNode;
  95. begin
  96.   Node := Items.GetFirstNode;
  97.   while Node <> nil do
  98.   begin
  99.     Node.Collapse(True);
  100.     Node := Node.GetNextSibling;
  101.   end;
  102. end;
  103. procedure TfcCustomTreeView.Loaded;
  104. begin
  105.   inherited Loaded;
  106.   if csDesigning in ComponentState then FullExpand;
  107. end;
  108. function TfcCustomTreeView.GetTopItem: TfcTreeNode;
  109. begin
  110.   if HandleAllocated then
  111.     Result := Items.GetNode(TreeView_GetFirstVisible(Handle))
  112.   else Result := nil;
  113. end;
  114. procedure TfcCustomTreeView.SetTopItem(Value: TfcTreeNode);
  115. begin
  116.   if HandleAllocated and (Value <> nil) then
  117.     TreeView_SelectSetFirstVisible(Handle, Value.ItemId);
  118. end;
  119. procedure TfcCustomTreeView.OnChangeTimer(Sender: TObject);
  120. begin
  121.   FChangeTimer.Enabled := False;
  122.   Change(TfcTreeNode(FChangeTimer.Tag));
  123. end;
  124. function TfcCustomTreeView.GetSelection: TfcTreeNode;
  125. begin
  126.   if HandleAllocated then
  127.   begin
  128.      Result := Items.GetNode(TreeView_GetSelection(Handle));
  129.   end
  130.   else Result := nil;
  131. end;
  132. procedure TfcCustomTreeView.SetSelection(Value: TfcTreeNode);
  133. begin
  134.   if Value <> nil then Value.Selected := True
  135.   else TreeView_SelectItem(Handle, nil);
  136. end;
  137. procedure TfcCustomTreeView.SetChangeDelay(Value: Integer);
  138. begin
  139.   FChangeTimer.Interval := Value;
  140. end;
  141. function TfcCustomTreeView.GetChangeDelay: Integer;
  142. begin
  143.   Result := FChangeTimer.Interval;
  144. end;
  145. function TfcCustomTreeView.GetDropTarget: TfcTreeNode;
  146. begin
  147.   if HandleAllocated then
  148.   begin
  149.     Result := Items.GetNode(TreeView_GetDropHilite(Handle));
  150.     if Result = nil then Result := FLastDropTarget;
  151.   end
  152.   else Result := nil;
  153. end;
  154. procedure TfcCustomTreeView.SetDropTarget(Value: TfcTreeNode);
  155. begin
  156.   if HandleAllocated then
  157.     if Value <> nil then Value.DropTarget := True
  158.     else TreeView_SelectDropTarget(Handle, nil);
  159. end;
  160. function TfcCustomTreeView.GetNodeFromItem(const Item: TTVItem): TfcTreeNode;
  161. begin
  162.   with Item do
  163.     if (state and TVIF_PARAM) <> 0 then Result := Pointer(lParam)
  164.     else Result := Items.GetNode(hItem);
  165. end;
  166. function TfcCustomTreeView.IsEditing: Boolean;
  167. var
  168.   ControlHand: HWnd;
  169. begin
  170.   ControlHand := TreeView_GetEditControl(Handle);
  171.   Result := (ControlHand <> 0) and IsWindowVisible(ControlHand);
  172. end;
  173. function fcWinItemStateTokwItemState(WinItemState: UINT): TfcItemStates;
  174. begin
  175.   result := [];
  176.   if WinItemState and CDIS_SELECTED <> 0 then include(result, fcisSelected);
  177.   if WinItemState and CDIS_GRAYED <> 0 then include(result, fcisGrayed);
  178.   if WinItemState and CDIS_DISABLED <> 0 then include(result, fcisDisabled);
  179.   if WinItemState and CDIS_CHECKED <> 0 then include(result, fcisChecked);
  180.   if WinItemState and CDIS_FOCUS <> 0 then include(result, fcisFocused);
  181.   if WinItemState and CDIS_DEFAULT <> 0 then include(result, fcisDefault);
  182.   if WinItemState and CDIS_HOT <> 0 then include(result, fcisHot);
  183.   if WinItemState and CDIS_MARKED <> 0 then include(result, fcisMarked);
  184.   if WinItemState and CDIS_INDETERMINATE <> 0 then include(result, fcisIndeterminate);
  185. end;
  186. procedure TfcCustomTreeView.PaintItem(node: TfcTreeNode);
  187. begin
  188. end;
  189. procedure TfcCustomTreeView.CNNotify(var Message: TWMNotify);
  190. //const
  191. //  TVN_BEGINLABELEDIT = TVN_FIRST-10;
  192. var
  193.   Node: TfcTreeNode;
  194.   MousePos: TPoint;
  195.   R: TRect;
  196.   DefaultDraw: Boolean;
  197.   TmpItem: TTVItem;
  198.   ANode: TfcTreeNode;
  199.   TempText: string; // -ksw (Added for tvoUnderscoreAllowed option)
  200. begin
  201.   with Message do
  202.     case NMHdr^.code of
  203.       NM_CUSTOMDRAW:
  204.         with PNMCustomDraw(NMHdr)^ do
  205.         begin
  206.           Result := CDRF_DODEFAULT;
  207.           if dwDrawStage = CDDS_PREPAINT then
  208.           begin
  209.             FIndent := TreeView_GetIndent(self.Handle);
  210.             if True then // IsCustomDrawn(dtControl, cdPrePaint) then
  211.             begin
  212.               FCanvas.Handle := dc;
  213.               Canvas.Font := Font;
  214.               Canvas.Brush := Brush;
  215.               R := ClientRect;
  216.               BeginPainting;
  217.               DisplayedItems:= 0;
  218. {              DefaultDraw := CustomDraw(R, cdPrePaint);
  219.               if not DefaultDraw then
  220.               begin
  221.                 Result := CDRF_SKIPDEFAULT;
  222.                 Exit;
  223.               end;}
  224.             end;
  225.             if True then //IsCustomDrawn(dtControl, cdPostPaint) then
  226.               Result := CDRF_NOTIFYPOSTPAINT;
  227.             if True then //IsCustomDrawn(dtItem, cdPrePaint) then
  228.               Result := Result or CDRF_NOTIFYITEMDRAW else
  229.               Result := Result or CDRF_DODEFAULT;
  230.           end
  231.           else if dwDrawStage = CDDS_ITEMPREPAINT then
  232.           begin
  233.             FillChar(TmpItem, SizeOf(TmpItem), 0);
  234.             TmpItem.hItem := HTREEITEM(dwItemSpec);
  235.             Node := GetNodeFromItem(TmpItem);
  236.             inc(DisplayedItems);
  237.             if Node <> nil then
  238.             begin
  239. //              {$ifdef fcdelphi4up}
  240. //              FCanvas.Handle := hdc;
  241. //              {$else}
  242. //              FCanvas.Handle := dc;
  243. //              {$endif}
  244.               Canvas.Font := Font;
  245.               Canvas.Brush := Brush;
  246.               { Unlike the list view, the tree view doesn't override the text
  247.                 foreground and background colors of selected items. }
  248.               if uItemState and CDIS_SELECTED <> 0 then
  249.               begin
  250.                 Canvas.Font.Color := clHighlightText;
  251.                 Canvas.Brush.Color := clHighlight;
  252.               end;
  253.               Canvas.Font.OnChange := CanvasChanged;
  254.               Canvas.Brush.OnChange := CanvasChanged;
  255.               DefaultDraw:= True;
  256. //              DefaultDraw := CustomDrawItem(Node,
  257. //                TCustomDrawState(Word(uItemState)), cdPrePaint);
  258.               Result := Result or CDRF_SKIPDEFAULT; {ww}
  259.               if DefaultDraw and FCanvasChanged then
  260.               begin
  261.                 FCanvasChanged := False;
  262.                 Canvas.Font.OnChange := nil;
  263.                 Canvas.Brush.OnChange := nil;
  264.                 with PNMTVCustomDraw(NMHdr)^ do
  265.                 begin
  266.                   clrText := ColorToRGB(Canvas.Font.Color);
  267.                   clrTextBk := ColorToRGB(Canvas.Brush.Color);
  268.                   SelectObject(dc, Canvas.Font.Handle);
  269.                   Result := Result or CDRF_NEWFONT;
  270.                 end;
  271.               end;
  272.               if DefaultDraw then
  273.               begin
  274.                 ANode := TfcTreeNode(lItemlParam);
  275.                 rc := ANode.DisplayRect(True);
  276.                 BeginItemPainting(ANode, rc,
  277.                    fcWinItemStateTokwItemState(uItemState));
  278.                 EndItemPainting(TfcTreeNode(lItemlParam),
  279.                    rc, fcWinItemStateTokwItemState(uItemState));
  280.               end;
  281.               PaintItem(Node);
  282. //              FCanvas.Handle := 0;
  283.               if True then //IsCustomDrawn(dtItem, cdPostPaint) then
  284.                 Result := Result or CDRF_NOTIFYPOSTPAINT;
  285.             end;
  286.           end
  287.           else if dwDrawStage = CDDS_POSTPAINT then
  288.           begin
  289. //             if DisplayedItems>0 then
  290. //             begin
  291.                 EndPainting;
  292.                 FCanvas.Handle := 0;
  293. //             end;
  294.           end;
  295.         end;
  296.       TVN_BEGINDRAG:
  297.         begin
  298.           FDragged := True;
  299.           with PNMTreeView(NMHdr)^ do
  300.             FDragNode := GetNodeFromItem(ItemNew);
  301.         end;
  302.       TVN_BEGINLABELEDIT:
  303.         begin
  304. {          Result:= 1;
  305.           Node := GetNodeFromItem(PTVDispInfo(NMHdr)^.Item);
  306.           DisplayRect:= Node.DisplayRect(True);
  307.           if FEditControl=Nil then
  308.           begin
  309.              FEditControl:= TEdit.create(self);
  310.           end;
  311.           FEditControl.parent:= self;
  312.           TEdit(FEditControl).AutoSize:= False;
  313.           FEditControl.Left:= DisplayRect.Left;
  314.           FEditControl.Top:= DisplayRect.Top;
  315.           FEditControl.Height:= DisplayRect.Bottom - DisplayRect.Top;
  316.           FEditControl.Visible:= True;
  317.           TEdit(FEditControl).Text:= Node.Text;
  318. //          TEdit(FEditControl).Ctl3d:= False;
  319.           TEdit(FEditControl).SelectAll;
  320.           TEdit(FEditControl).SetFocus;
  321.           exit;
  322. }
  323.           with PTVDispInfo(NMHdr)^ do
  324.             if Dragging or not CanEdit(GetNodeFromItem(item)) then
  325.               Result := 1;
  326.           if Result = 0 then
  327.           begin
  328.             FEditHandle := TreeView_GetEditControl(Handle);
  329.             FDefEditProc := Pointer(GetWindowLong(FEditHandle, GWL_WNDPROC));
  330.             SetWindowLong(FEditHandle, GWL_WNDPROC, LongInt(FEditInstance));
  331. //            sp:= self.ClientToScreen(Point(0, 0));
  332.             EditNode := GetNodeFromItem(PTVDispInfo(NMHdr)^.Item);
  333.             Patch[0]:= True; { 6/14/99 - Skip Validate in EditWndProc }
  334. //            DisplayRect:= Node.DisplayRect(True);
  335. //
  336. //            GetWindowRect(self.handle, r);
  337. //            SetWindowPos(FEditHandle, 0, 1,1,0,0, //sp.x + DisplayRect.Left, sp.y + DisplayRect.Top, 0, 0,
  338. //              SWP_NOZORDER OR SWP_NOSIZE OR SWP_NOACTIVATE);
  339.           end;
  340.         end;
  341.       TVN_ENDLABELEDIT:
  342.         begin
  343.            Edit(PTVDispInfo(NMHdr)^.item);
  344.         end;
  345.       TVN_ITEMEXPANDING:
  346.         if not FManualNotify then
  347.         begin
  348.           with PNMTreeView(NMHdr)^ do
  349.           begin
  350.             Node := GetNodeFromItem(ItemNew);
  351.             if (action = TVE_EXPAND) and not CanExpand(Node) then
  352.               Result := 1
  353.             else if (action = TVE_COLLAPSE) and
  354.               not CanCollapse(Node) then Result := 1;
  355.           end;
  356.         end;
  357.       TVN_ITEMEXPANDED:
  358.         if not FManualNotify then
  359.         begin
  360.           with PNMTreeView(NMHdr)^ do
  361.           begin
  362.             Node := GetNodeFromItem(itemNew);
  363.             if (action = TVE_EXPAND) then Expand(Node)
  364.             else if (action = TVE_COLLAPSE) then Collapse(Node);
  365.           end;
  366.         end;
  367.       TVN_SELCHANGINGA, TVN_SELCHANGINGW:
  368.         begin
  369.           if SkipChangeMessages then exit; { RSW}
  370.           if not CanChange(GetNodeFromItem(PNMTreeView(NMHdr)^.itemNew)) then
  371.             Result := 1;
  372.         end;
  373.       TVN_SELCHANGEDA, TVN_SELCHANGEDW:
  374.         begin
  375.           if SkipChangeMessages then exit; { RSW}
  376.           with PNMTreeView(NMHdr)^ do
  377.             if FChangeTimer.Interval > 0 then
  378.             with FChangeTimer do
  379.             begin
  380.               Enabled := False;
  381.               Tag := Integer(GetNodeFromItem(itemNew));
  382.               Enabled := True;
  383.             end
  384.             else
  385.               Change(GetNodeFromItem(itemNew));
  386.           end;
  387.       TVN_DELETEITEM:
  388. //          if not FStateChanging then   {5/16/2000 - PYW - Fix Memory leak from code left over from Delphi 3}
  389.           begin
  390.              Node := GetNodeFromItem(PNMTreeView(NMHdr)^.itemOld);
  391.              if Node <> nil then
  392.              begin
  393.                Node.FItemId := nil;
  394.                FChangeTimer.Enabled := False;
  395.                if FStateChanging then Node.Delete
  396.                else Items.Delete(Node);
  397.              end;
  398.           end;
  399.       TVN_SETDISPINFO:
  400.         with PTVDispInfo(NMHdr)^ do
  401.         begin
  402.           Node := GetNodeFromItem(item);
  403.           if (Node <> nil) and ((item.mask and TVIF_TEXT) <> 0) then
  404.             Node.Text := item.pszText;
  405.         end;
  406.       TVN_GETDISPINFO:
  407.         with PTVDispInfo(NMHdr)^ do
  408.         begin
  409.           Node := GetNodeFromItem(item);
  410.           if Node <> nil then
  411.           begin
  412.             TempText := Node.Text;  // -ksw  (Added for tvoUnderscoreAllowed option)
  413. //            if tvoUnderscoreAllowed in FOptions then TempText := fcStripAmpersands(Node.Text);  // -ksw Added
  414.             if (item.mask and TVIF_TEXT) <> 0 then
  415.               StrLCopy(item.pszText, PChar(TempText), item.cchTextMax); // -ksw (Changed to use the TempText var)
  416.             if (item.mask and TVIF_IMAGE) <> 0 then
  417.             begin
  418.               GetImageIndex(Node);
  419.               item.iImage := Node.ImageIndex;
  420.             end;
  421.             if (item.mask and TVIF_SELECTEDIMAGE) <> 0 then
  422.             begin
  423.               GetSelectedIndex(Node);
  424.               item.iSelectedImage := Node.SelectedIndex;
  425.             end;
  426.           end;
  427.         end;
  428.       NM_RCLICK:
  429.         begin
  430.             GetCursorPos(MousePos);
  431.             with PointToSmallPoint(ScreenToClient(MousePos)) do
  432.             begin
  433.               FRClickNode := GetNodeAt(X, Y);
  434.               if RightClickSelects and (FRClickNode<>nil) then Selected:= FRClickNode
  435.               else begin
  436.                  if FRClickNode<>nil then InvalidateNode(FRClickNode);
  437.               end;
  438.               InvalidateNoErase; { 4/21/99 - Workaround for Microsoft Tree bug which causes last
  439.                                     node to not paint correctly  }
  440.               Perform(WM_RBUTTONUP, 0, MakeLong(X, Y));
  441.               {6/9/2000 - PYW - Prevent default button handling for treeview}
  442.               Message.Result := 1;
  443.             end;
  444.         end;
  445. //      TVN_BEGINLABELEDIT:
  446. //        begin
  447. //        end;
  448. {      TVN_GETINFOTIP:
  449.         begin
  450.            if fcUpdatedComCtlVersion then begin
  451.               ToolTipHandle:= SendMessage(Handle, TVM_GETTOOLTIPS, 0, 0);
  452.               with PNMTVGetInfoTip(NMHdr)^ do
  453.               begin
  454.                  Node:= Items.GetNode(hItem);
  455.                  HintTimer:= TTimer.create(self);
  456.                  HintTimer.OnTimer:=HintTimerEvent;
  457.                  HintTimer.Interval:=250;
  458.                  HintTimer.Enabled:= True;
  459. //                 strpcopy(pszText, Node.Text);
  460.               end;
  461.               if (ToolTipHandle <> 0) and (LastHintNode<>Node) then
  462.               begin
  463.                  FreeHintWindow;
  464.                  sp:= self.ClientToScreen(Point(0, 0));
  465.                  DisplayRect:= Node.DisplayRect(True);
  466.                  R.Left:= DisplayRect.Left + sp.x - 1;
  467.                  R.Top:= DisplayRect.Top + sp.y - 2;
  468.                  R.Right:= R.Left + Canvas.TextWidth(Node.Text) + 6;
  469.                  R.Bottom:= R.Top + Canvas.TextHeight(Node.Text) + 2;
  470.                  if DisplayRect.Left+Canvas.TextWidth(Node.Text)>
  471.                     GetEffectiveWidth then
  472.                  begin
  473.                     LastHintNode:= Node;
  474.                     HintWindow:= CreateHintWindow;
  475.                     HintWindow.ActivateHint(R, Node.Text);
  476.                  end
  477.                  else LastHintNode:= nil;
  478.               end
  479.            end;
  480.         end;}
  481.     end;
  482. end;
  483. function TfcCustomTreeView.GetDragImages: {$ifdef fcDelphi4Up}TDragImageList{$else}TCustomImageList{$endif};
  484. begin
  485.   if (FDragImage <> nil) and (FDragImage.Count > 0) then // Added check to make sure FDragImage is not nil -ksw (11/30/98)
  486.     Result := FDragImage else
  487.     Result := nil;
  488. end;
  489. procedure TfcCustomTreeView.WndProc(var Message: TMessage);
  490. begin
  491. {   if (csDesigning in ComponentState) then
  492.    begin
  493.       if (Message.Msg = wm_lbuttondown) or (Message.Msg = wm_vscroll) then
  494.       begin
  495.          ControlState := ControlState + [csLButtonDown];
  496.          Dispatch(Message);
  497.          exit;
  498.       end;
  499.    end;
  500. }
  501.   {$ifdef fcDelphi4Up}
  502.   if not (csDesigning in ComponentState) and ((Message.Msg = WM_LBUTTONDOWN) or
  503.     (Message.Msg = WM_LBUTTONDBLCLK)) and not Dragging and
  504.     (DragMode = dmAutomatic) and (DragKind = dkDrag) then
  505.   begin
  506.     if not IsControlMouseMsg(TWMMouse(Message)) then
  507.     begin
  508.       ControlState := ControlState + [csLButtonDown];
  509.       Dispatch(Message);
  510.     end;
  511.   end
  512.   else inherited WndProc(Message);
  513.   {$else}
  514.   inherited WndProc(Message);
  515.   {$endif}
  516. end;
  517. procedure TfcCustomTreeView.DoStartDrag(var DragObject: TDragObject);
  518. var
  519.   ImageHandle: HImageList;
  520.   DragNode: TfcTreeNode;
  521.   P: TPoint;
  522. begin
  523.   inherited DoStartDrag(DragObject);
  524.   DragNode := FDragNode;
  525.   FLastDropTarget := nil;
  526.   FDragNode := nil;
  527.   if DragNode = nil then
  528.   begin
  529.     GetCursorPos(P);
  530.     with ScreenToClient(P) do DragNode := GetNodeAt(X, Y);
  531.   end;
  532.   if DragNode <> nil then
  533.   begin
  534.     ImageHandle := TreeView_CreateDragImage(Handle, DragNode.ItemId);
  535.     if ImageHandle <> 0 then
  536.     begin
  537.       // 2/3/2000 - Optimize imagelist usage so that resources are better
  538.       if FDragImage=nil then
  539.       begin
  540.         {$ifdef fcDelphi4Up}
  541.         FDragImage := TDragImageList.CreateSize(32, 32);
  542.         {$else}
  543.         FDragImage := TImageList.CreateSize(32, 32);
  544.         {$endif}
  545.       end;
  546.       with FDragImage do
  547.       begin
  548.         Handle := ImageHandle;
  549.         SetDragImage(0, 2, 2);
  550.       end;
  551.     end
  552.   end;
  553. end;
  554. procedure TfcCustomTreeView.DoEndDrag(Target: TObject; X, Y: Integer);
  555. begin
  556.   inherited DoEndDrag(Target, X, Y);
  557.   FLastDropTarget := nil;
  558. end;
  559. procedure TfcCustomTreeView.CMDrag(var Message: TCMDrag);
  560. begin
  561.   inherited;
  562.   with Message, DragRec^ do
  563.     case DragMessage of
  564.       dmDragMove:
  565.         with ScreenToClient(Pos) do
  566.           DoDragOver(Source, X, Y, Message.Result <> 0);
  567.       dmDragLeave:
  568.         begin
  569.           TDragObject(Source).HideDragImage;
  570.           FLastDropTarget := DropTarget;
  571.           DropTarget := nil;
  572.           TDragObject(Source).ShowDragImage;
  573.         end;
  574.       dmDragDrop: FLastDropTarget := nil;
  575.     end;
  576. end;
  577. procedure TfcCustomTreeView.DoDragOver(Source: TDragObject; X, Y: Integer; CanDrop: Boolean);
  578. var
  579.   Node: TfcTreeNode;
  580. begin
  581.   Node := GetNodeAt(X, Y);
  582.   if (Node <> nil) and
  583.     ((Node <> DropTarget) or (Node = FLastDropTarget)) then
  584.   begin
  585.     FLastDropTarget := nil;
  586.     TDragObject(Source).HideDragImage;
  587.     Node.DropTarget := True;
  588.     TDragObject(Source).ShowDragImage;
  589.   end;
  590. end;
  591. procedure TfcCustomTreeView.GetImageIndex(Node: TfcTreeNode);
  592. begin
  593.   if Assigned(FOnGetImageIndex) then FOnGetImageIndex(Self, Node);
  594. end;
  595. procedure TfcCustomTreeView.GetSelectedIndex(Node: TfcTreeNode);
  596. begin
  597.   if Assigned(FOnGetSelectedIndex) then FOnGetSelectedIndex(Self, Node);
  598. end;
  599. function TfcCustomTreeView.CanChange(Node: TfcTreeNode): Boolean;
  600. begin
  601.   Result := True;
  602.   if Assigned(FOnChanging) then FOnChanging(Self, Node, Result);
  603. end;
  604. procedure TfcCustomTreeView.Change(Node: TfcTreeNode);
  605. begin
  606.   if Assigned(FOnChange) then FOnChange(Self, Node);
  607. end;
  608. procedure TfcCustomTreeView.Delete(Node: TfcTreeNode);
  609. begin
  610.   if Assigned(FOnDeletion) then FOnDeletion(Self, Node);
  611.   if Assigned(FOnItemChange) then FOnItemChange(self, Node, icaDelete, NULL);
  612. end;
  613. procedure TfcCustomTreeView.Expand(Node: TfcTreeNode);
  614. begin
  615.   if Assigned(FOnExpanded) then FOnExpanded(Self, Node);
  616. end;
  617. function TfcCustomTreeView.CanExpand(Node: TfcTreeNode): Boolean;
  618. begin
  619.   Result := True;
  620.   if Assigned(FOnExpanding) then FOnExpanding(Self, Node, Result);
  621.   { 5/21/99 - Complete editing  }
  622.   if IsEditing and (Selected<>nil) and
  623.      (Selected.HasAsParent(Node) or (Selected=Node)) then
  624.   begin
  625.      Selected.EndEdit(False);
  626.      result:= False;
  627.   end;
  628.   if InLoading then result:= False;
  629. end;
  630. procedure TfcCustomTreeView.Collapse(Node: TfcTreeNode);
  631. begin
  632.   if Assigned(FOnCollapsed) then FOnCollapsed(Self, Node);
  633. end;
  634. function TfcCustomTreeView.CanCollapse(Node: TfcTreeNode): Boolean;
  635. begin
  636.   Result := True;
  637.   if Assigned(FOnCollapsing) then FOnCollapsing(Self, Node, Result);
  638.   { 5/21/99 - Complete editing  }
  639.   if IsEditing and (Selected<>nil) and
  640.      (Selected.HasAsParent(Node) or (Selected=Node)) then
  641.   begin
  642.      Selected.EndEdit(False);
  643.      result:= False;
  644.   end
  645. end;
  646. function TfcCustomTreeView.CanEdit(Node: TfcTreeNode): Boolean;
  647. begin
  648.   Result := True;
  649.   if Assigned(FOnEditing) then FOnEditing(Self, Node, Result);
  650. end;
  651. procedure TfcCustomTreeView.Edit(const Item: TTVItem);
  652. var
  653.   S: string;
  654.   Node: TfcTreeNode;
  655. begin
  656.   with Item do
  657.     if pszText <> nil then
  658.     begin
  659.       S := pszText;
  660.       Node := GetNodeFromItem(Item);
  661.       if Assigned(FOnEdited) then FOnEdited(Self, Node, S);
  662.       if Node <> nil then Node.Text := S;
  663.       EditNode := Nil;
  664.     end;
  665. end;
  666. function TfcCustomTreeView.CreateNode: TfcTreeNode;
  667. begin
  668.   Result := NodeClass.Create(Items);
  669.   if Assigned(OnItemChange) then OnItemChange(self, result, icaAdd, NULL);
  670. end;
  671. procedure TfcCustomTreeView.SetImageList(Value: HImageList; Flags: Integer);
  672. begin
  673.   if HandleAllocated then TreeView_SetImageList(Handle, Value, Flags);
  674. end;
  675. procedure TfcCustomTreeView.ImageListChange(Sender: TObject);
  676. var
  677.   ImageHandle: HImageList;
  678. begin
  679.   if HandleAllocated then
  680.   begin
  681.     if TCustomImageList(Sender).HandleAllocated then
  682.       ImageHandle := TCustomImageList(Sender).Handle
  683.     else
  684.       ImageHandle := 0;
  685.     if Sender = Images then
  686.       SetImageList(ImageHandle, TVSIL_NORMAL)
  687.     else if Sender = StateImages then
  688.       SetImageList(ImageHandle, TVSIL_STATE);
  689.   end;
  690. end;
  691. procedure TfcCustomTreeView.Notification(AComponent: TComponent;
  692.   Operation: TOperation);
  693. begin
  694.   inherited Notification(AComponent, Operation);
  695.   if Operation = opRemove then
  696.   begin
  697.     if AComponent = Images then Images := nil;
  698.     if AComponent = StateImages then StateImages := nil;
  699.   end;
  700. end;
  701. procedure TfcCustomTreeView.ResetStateImages;
  702. var Node: TfcTreeNode;
  703. begin
  704.    if Images<>nil then begin
  705.       Node := Items.GetFirstNode;
  706.       while Node <> nil do
  707.       begin
  708.           if (Node.StateIndex <>(Node.GetStateIndex shr 12) -1) then
  709.             Node.StateIndex:= Node.StateIndex;
  710.           Node := Node.GetNext;
  711.       end
  712.    end
  713. end;
  714. procedure TfcCustomTreeView.SetImages(Value: TCustomImageList);
  715. begin
  716.   if Images <> nil then
  717.     Images.UnRegisterChanges(FImageChangeLink);
  718.   FImages := Value;
  719.   if Images <> nil then
  720.   begin
  721.     Images.RegisterChanges(FImageChangeLink);
  722.     Images.FreeNotification(Self);
  723.     SetImageList(Images.Handle, TVSIL_NORMAL);
  724.     { Imagelist changing after nodes loaded causes stateindex to no longer be -1 }
  725.     { Therefore scan all nodes and reset the ones that are incorrect }
  726.     { 2/1/99 - Reset state images due to Microsoft TreeView resetting StateImage list }
  727.     if StateImages <> nil then
  728.     begin
  729.       SetImageList(StateImages.Handle, TVSIL_STATE)
  730.     end;
  731.     ResetStateImages;
  732. //    if not (csLoading in ComponentState) then RecreateWnd; // 2/2/99
  733.   end
  734.   else SetImageList(0, TVSIL_NORMAL);
  735. end;
  736. procedure TfcCustomTreeView.SetStateImages(Value: TCustomImageList);
  737. var PrevStateImages: TCustomImageList;
  738. begin
  739.   PrevStateImages:= StateImages;
  740.   if StateImages <> nil then
  741.     StateImages.UnRegisterChanges(FStateChangeLink);
  742.   FStateImages := Value;
  743.   if StateImages <> nil then
  744.   begin
  745.     StateImages.RegisterChanges(FStateChangeLink);
  746.     StateImages.FreeNotification(Self);
  747.     SetImageList(StateImages.Handle, TVSIL_STATE)
  748.   end;
  749. //  else SetImageList(0, TVSIL_STATE);
  750.   if PrevStateImages<>Value then
  751.   begin
  752.      if (Value=Nil) then
  753.      begin
  754.        TreeView_SetImageList(Handle, FFixBugImageList.Handle, TVSIL_STATE);
  755.        RecreateWnd;
  756.      end
  757.   end
  758. end;
  759. procedure TfcCustomTreeView.LoadFromFile(const FileName: string);
  760. var
  761.   Stream: TStream;
  762. begin
  763.   Stream := TFileStream.Create(FileName, fmOpenRead);
  764.   try
  765.     LoadFromStream(Stream);
  766.   finally
  767.     Stream.Free;
  768.   end;
  769. end;
  770. procedure TfcCustomTreeView.LoadFromStream(Stream: TStream);
  771. begin
  772.    Stream.Position := 0;
  773.    Items.ReadData(Stream);
  774. {  with TTreeStrings.Create(Items) do
  775.     try
  776.       LoadTreeFromStream(Stream);
  777.     finally
  778.       Free;
  779.   end;}
  780. end;
  781. procedure TfcCustomTreeView.SaveToFile(const FileName: string);
  782. var
  783.   Stream: TStream;
  784. begin
  785.   Stream := TFileStream.Create(FileName, fmCreate);
  786.   try
  787.      SaveToStream(Stream);
  788.   finally
  789.      Stream.Free;
  790.   end;
  791. end;
  792. procedure TfcCustomTreeView.SaveToStream(Stream: TStream);
  793. begin
  794.     Items.WriteData(Stream);
  795. {  with TTreeStrings.Create(Items) do
  796.     try
  797.       SaveTreeToStream(Stream);
  798.     finally
  799.       Free;
  800.   end;}
  801. end;
  802. procedure TfcCustomTreeView.WMRButtonDown(var Message: TWMRButtonDown);
  803. begin
  804.   InvalidateNoErase; { 4/21/99 - Workaround for Microsoft Tree bug which causes last
  805.                                  node to not paint correctly  }
  806.   inherited;
  807. end;
  808. procedure TfcCustomTreeView.WMRButtonUp(var Message: TWMRButtonUp);
  809. begin
  810.   inherited;
  811. end;
  812. procedure TfcCustomTreeView.WMLButtonDown(var Message: TWMLButtonDown);
  813. var
  814.   {$ifdef fcDelphi4Up}
  815.   Node: TfcTreeNode;
  816.   {$endif}
  817.   MousePos, SP: TPoint;
  818.   hitTest: TfcHitTests;
  819. begin
  820.    GetCursorPos(MousePos);
  821.    SP:= ScreenToClient(MousePos);
  822.    hitTest:= GetHitTestInfoAt(sp.x, sp.y);
  823.    BeforeMouseDownNode:= Selected;
  824.    ClickedNode:= GetNodeAt(sp.x, sp.y) as TfcTreeNode;
  825.    if (fchtOnButton in hitTest) {and (ssLeft in Shift) }then
  826.    begin
  827.       if tvoExpandButtons3D in Options then
  828.       begin
  829.          MouseNode:= GetNodeAt(sp.x, sp.y) as TfcTreeNode;
  830.          MouseLoop(sp.x, sp.y);
  831.          MouseNode:= nil;
  832.          exit;
  833.       end
  834.    end;
  835.   { 7/3/99 - Only call Invalidate if not in expanding/collapsing}
  836.   if not (fchtOnButton in hitTest) then
  837.     InvalidateNoErase; { 4/21/99 - Workaround for Microsoft Tree bug which causes last
  838.                                  node to not paint correctly  }
  839.   {$ifdef fcDelphi4Up}
  840.   FDragged := False;
  841.   FDragNode := nil;
  842.   try
  843.     inherited;
  844.     if (DragMode = dmAutomatic) and (DragKind = dkDrag) then
  845.     begin
  846.       SetFocus;
  847.       if not FDragged then
  848.       begin
  849.         GetCursorPos(MousePos);
  850.         with PointToSmallPoint(ScreenToClient(MousePos)) do
  851.           Perform(WM_LBUTTONUP, 0, MakeLong(X, Y));
  852.       end
  853.       else begin
  854.         Node := GetNodeAt(Message.XPos, Message.YPos);
  855.         if Node <> nil then
  856.         begin
  857.           Node.Focused := True;
  858.           Node.Selected := True;
  859.           BeginDrag(False);
  860.         end;
  861.       end;
  862.     end;
  863.   finally
  864.     FDragNode := nil;
  865.   end;
  866.   {$else}
  867.   inherited;
  868.   {$endif}
  869. end;
  870. procedure TfcCustomTreeView.WMNotify(var Message: TWMNotify);
  871. var
  872.   Node: TfcTreeNode;
  873.   MaxTextLen: Integer;
  874.   Pt: TPoint;
  875. begin
  876.   with Message do
  877.     if NMHdr^.code = TTN_NEEDTEXTW then
  878.     begin
  879.       // Work around NT COMCTL32 problem with tool tips >= 80 characters
  880.       GetCursorPos(Pt);
  881.       Pt := ScreenToClient(Pt);
  882.       Node := GetNodeAt(Pt.X, Pt.Y);
  883.       if (Node = nil) or (Node.Text = '') or
  884.         (PToolTipTextW(NMHdr)^.uFlags and TTF_IDISHWND = 0) then Exit;
  885.       if (fcGetComCtlVersion >= fcComCtlVersionIE4) and (Length(Node.Text) < 80) then
  886.       begin
  887.         inherited;
  888.         Exit;
  889.       end;
  890.       FWideText := Node.Text;
  891.       MaxTextLen := SizeOf(PToolTipTextW(NMHdr)^.szText) div SizeOf(WideChar);
  892.       if Length(FWideText) >= MaxTextLen then
  893.         SetLength(FWideText, MaxTextLen - 1);
  894.       PToolTipTextW(NMHdr)^.lpszText := PWideChar(FWideText);
  895.       FillChar(PToolTipTextW(NMHdr)^.szText, MaxTextLen, 0);
  896.       Move(Pointer(FWideText)^, PToolTipTextW(NMHdr)^.szText, Length(FWideText) * SizeOf(WideChar));
  897.       PToolTipTextW(NMHdr)^.hInst := 0;
  898.       SetWindowPos(NMHdr^.hwndFrom, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE or
  899.         SWP_NOSIZE or SWP_NOMOVE or SWP_NOOWNERZORDER);
  900.       Result := 1;
  901.     end
  902.     else inherited;
  903. end;
  904. { CustomDraw support }
  905. procedure TfcCustomTreeView.CanvasChanged;
  906. begin
  907.   FCanvasChanged := True;
  908. end;
  909. {function TfcCustomTreeView.IsCustomDrawn(Target: TCustomDrawTarget;
  910.   Stage: TCustomDrawStage): Boolean;
  911. begin
  912.   result:= True;
  913. end;
  914. }
  915. //function TfcCustomTreeView.CustomDraw(const ARect: TRect; Stage: TCustomDrawStage): Boolean;
  916. //begin
  917. //  Result := True;
  918. //  if Assigned(FOnCustomDraw) then FOnCustomDraw(Self, ARect, Result);
  919. //end;
  920. //function TfcCustomTreeView.CustomDrawItem(Node: TfcTreeNode; State: TCustomDrawState;
  921. //  Stage: TCustomDrawStage): Boolean;
  922. //begin
  923. //  Result := True;
  924. //  if Assigned(FOnCustomDrawItem) then FOnCustomDrawItem(Self, Node, State, Result);
  925. //end;
  926. procedure TfcTreeNode.SetCheckboxType(val: TfcTreeViewCheckboxType);
  927. begin
  928.    if (val<>FCheckboxType) {or (StateIndex>1) }then  {4/26/99 - RSW Don't reset state index }
  929.    begin
  930.       FCheckboxType:= val;
  931.       if val<>tvctNone then
  932.          StateIndex:= 1
  933.       else
  934.          StateIndex:= -1
  935.    end
  936. end;
  937. function TfcTreeNode.GetMultiSelected: Boolean;
  938. begin
  939.   result := FMultiSelected {or (TreeView.MultiSelectAttributes.AlwaysIncludeSelectedItem and Selected)};
  940. end;
  941. procedure TfcCustomTreeView.DoToggleCheckbox(Node: TfcTreeNode);
  942. begin
  943.    if Assigned(OnToggleCheckbox) then FOnToggleCheckbox(self, Node);
  944. end;
  945. procedure TfcTreeNode.SetChecked(val : boolean);
  946. var curNode: TfcTreeNode;
  947. begin
  948.    if FChecked<>val then
  949.    begin
  950.       FChecked:= val;
  951.       if self.IsRadioGroup then
  952.       begin
  953.          if val=False then exit;
  954.          { Unselect all siblings }
  955.          CurNode:= Treeview.GetFirstSibling(Self);
  956.          while curNode<>Nil do begin
  957.             if CurNode<>self then begin
  958.                curNode.checked:= False;
  959.                Treeview.InvalidateNode(CurNode);
  960.             end;
  961.             curNode:= TfcTreeNode(curNode.GetNextSibling);
  962.          end;
  963.       end
  964.       else if TreeView.MultiSelectCheckboxNeeded(self) then MultiSelected:= val;
  965.       Treeview.InvalidateNode(self);
  966.       TreeView.DoToggleCheckbox(self);
  967.    end;
  968. end;
  969. procedure TfcTreeNode.SetGrayed(val : boolean);
  970. var curNode: TfcTreeNode;
  971. begin
  972.    if FGrayed<>val then
  973.    begin
  974.       FGrayed:= val;
  975.       if self.IsRadioGroup then
  976.       begin
  977.          { Toggle gray for all siblings }
  978.          CurNode:= Treeview.GetFirstSibling(Self);
  979.          while curNode<>Nil do begin
  980.             if CurNode<>self then begin
  981.                curNode.Grayed:= val;
  982.                Treeview.InvalidateNode(CurNode);
  983.             end;
  984.             curNode:= TfcTreeNode(curNode.GetNextSibling);
  985.          end;
  986.       end;
  987.       Treeview.InvalidateNode(self);
  988.    end;
  989. end;
  990. procedure TfcTreeNode.SetMultiSelected(Value: Boolean);
  991. begin
  992.   if MultiSelected <> Value then
  993.   begin
  994.     TreeView.MultiSelectNode(self, Value, true);  // This method will set the FMultiSelect var
  995.   end;
  996. end;
  997. function TfcTreeNode.GetStateIndex: integer;
  998. var
  999.   Item: TTVItem;
  1000. begin
  1001.   with Item do
  1002.   begin
  1003.     mask := TVIF_STATE or TVIF_HANDLE;
  1004.     stateMask := TVIS_STATEIMAGEMASK;
  1005.     hItem := ItemId;
  1006. //    state := IndexToStateImageMask(Value + 1);
  1007.   end;
  1008.   TreeView_GetItem(Handle, Item);
  1009.   result:= Item.state;
  1010. end;
  1011. Function TfcTreeNode.IsRadioGroup: boolean;
  1012. begin
  1013.    result:= CheckboxType=tvctRadioGroup;
  1014. end;
  1015. Function TfcTreeNode.GetSortText: string;
  1016. begin
  1017.    with TreeView do begin
  1018.       result:= Text;
  1019.    end
  1020. end;
  1021. constructor TfcTVMultiSelectAttributes.Create(Owner: TComponent);
  1022. begin
  1023.    TreeView:= Owner as TfcCustomTreeView;
  1024.    FMultiSelectCheckbox:= True;
  1025. //   FAlwaysIncludeSelectedItem := False;
  1026.    FAutoUnselect:= True;
  1027. end;
  1028. procedure TfcTVMultiSelectAttributes.Assign(Source: TPersistent);
  1029. var tsa: TfcTVMultiSelectAttributes;
  1030. begin
  1031.    If Source is TfcTVMultiSelectAttributes then
  1032.    begin
  1033.       tsa:= TfcTVMultiSelectAttributes(Source);
  1034.       Enabled:= tsa.Enabled;
  1035.       MultiSelectCheckbox:= tsa.MultiSelectCheckbox;
  1036.       MultiSelectLevel:= tsa.MultiSelectLevel;
  1037.    end
  1038.    else inherited Assign(Source);
  1039. end;
  1040. procedure TfcTVMultiSelectAttributes.SetEnabled(val: boolean);
  1041. var Node: TfcTreeNode;
  1042. begin
  1043.    if val<>FEnabled then
  1044.    begin
  1045.       if FEnabled=True then
  1046.       begin
  1047.          { Set all Stateimages of 1 to -1 unless checkboxType is checkbox}
  1048.          Node := TreeView.Items.GetFirstNode;
  1049.          while Node <> nil do
  1050.          begin
  1051. //             if (Node.StateIndex=1) and (Node.ShowCheckbox<>0) and
  1052.              if (Node.StateIndex=1) and (Node.CheckboxType=tvctNone) and
  1053.                (TreeView.ValidMultiSelectLevel(Node.Level)) then
  1054.                  Node.StateIndex:= -1;
  1055.              Node := TfcTreeNode(Node.GetNext);
  1056.          end;
  1057.       end;
  1058.       FEnabled:= val;
  1059.       TreeView.invalidate;
  1060.    end
  1061. end;
  1062. procedure TfcTVMultiSelectAttributes.SetMultiSelectCheckBox(val: boolean);
  1063. begin
  1064.    if val<>FMultiSelectCheckbox then
  1065.    begin
  1066.       FMultiSelectCheckbox:= val;
  1067.       if not val then Treeview.ClearStateImageIndexes;
  1068.       TreeView.invalidate;
  1069.    end
  1070. end;
  1071. procedure TfcTVMultiSelectAttributes.SetMultiSelectLevel(val: integer);
  1072. begin
  1073.    if val<>FMultiSelectLevel then
  1074.    begin
  1075.       FMultiSelectLevel:= val;
  1076.       (TreeView as TfcCustomTreeView).UnselectAllNodes(Nil);
  1077.       if val>=0 then Treeview.ClearStateImageIndexes;
  1078.       TreeView.Invalidate;
  1079.    end
  1080. end;
  1081. function TfcCustomTreeView.ValidMultiSelectLevel(ALevel: Integer): Boolean;
  1082. begin
  1083.   result := (FMultiSelectAttributes.MultiSelectLevel = ALevel) or
  1084.             (FMultiSelectAttributes.MultiSelectLevel = -1);
  1085. end;
  1086. Procedure TfcCustomTreeView.UnselectAll;
  1087. begin
  1088.    UnselectAllNodes(nil);
  1089. end;
  1090. Procedure TfcCustomTreeView.UnselectAllNodes(IgnoreNode: TfcTreeNode);
  1091. var curNode: TfcTreeNode;
  1092.     i: integer;
  1093. begin
  1094.    for i:= 0 to FMultiSelectList.count-1 do begin
  1095.       curNode:= TfcTreeNode(FMultiSelectList[i]);
  1096.       if (curNode<>IgnoreNode) then begin
  1097.          curNode.FMultiSelected:= False;
  1098.          if FMultiSelectAttributes.MultiSelectCheckbox then
  1099.            curNode.checked:= False;
  1100.          if IsVisible(curNode, True) then begin
  1101.             InvalidateNode(curNode);
  1102.          end
  1103.       end
  1104.    end;
  1105.    FMultiSelectList.Clear;
  1106.    if IgnoreNode<>nil then FMultiSelectList.Add(IgnoreNode);
  1107. end;
  1108. procedure TfcCustomTreeView.MultiSelectNode(Node: TfcTreeNode; Select: boolean; redraw: boolean);
  1109. begin
  1110.    if (not ValidMultiSelectLevel(Node.Level)) {and
  1111.       (FMultiSelectAttributes.MultiSelectLevel>=0)} then exit;
  1112.    if FMultiSelectAttributes.MultiSelectCheckbox then
  1113.       (Node as TfcTreeNode).checked:= Select;
  1114.    if (Select <> (Node as TfcTreeNode).FMultiSelected) then
  1115.    begin
  1116.       if Select then FMultiSelectList.Add(Node)
  1117.       else FMultiSelectList.Remove(Node);
  1118.       (Node as TfcTreeNode).FMultiSelected:= Select;
  1119.       if redraw and IsVisible(Node, True) then begin
  1120.          InvalidateNode(Node);
  1121.       end
  1122.    end
  1123. end;
  1124. function TfcCustomTreeView.IsVisible(Node: TfcTreeNode; PartialOK: Boolean): Boolean;
  1125. var r: TRect;
  1126.     i: integer;
  1127. begin
  1128.   r := ItemRect(Node, True);
  1129.   if PartialOK then i := r.Top else i := r.Bottom;
  1130.   result := (i < Height - FBorderWidth * 2) and
  1131.             (r.Bottom>0)
  1132. end;
  1133. Procedure TfcCustomTreeView.InvalidateNode(Node: TfcTreeNode);
  1134. var r: TRect;
  1135. begin
  1136.     if Node=nil then exit;
  1137.     r := ItemRect(Node, False);
  1138.     if (r.Left=r.right) then exit;
  1139.     InvalidateRect(Handle, @r, False);
  1140. end;
  1141. function TfcCustomTreeView.LevelRect(ANode: TfcTreeNode): TRect;
  1142. const wwIMAGEMARGIN = 4;
  1143. begin
  1144.   result := ItemRect(ANode, True);
  1145.   result.Right := result.Left+0;
  1146.   result.Left := result.Left+0 - FIndent;
  1147.   if UseImages(ANode) then
  1148.      OffSetRect(result, -(TImageList(Images).Width + 2{ + wwIMAGEMARGIN}), 0);
  1149.   { 3/8/99 - Don't allow state images that are a multple of 16, as Microsoft does not currently support this }
  1150.   if UseStateImages(ANode) or
  1151.      ((ANode.StateIndex>0) and ((ANode.StateIndex mod 16)<>0)) then
  1152.   begin
  1153.      if StateImages<>Nil then
  1154.         OffsetRect(result, -TImageList(StateImages).Width, 0)
  1155.      else
  1156.         OffsetRect(result, -TImageList(FFixBugImageList).Width, 0);
  1157.   end;
  1158.   if UseImages(ANode) {and
  1159.      not (UseStateImages(ANode) or (ANode.StateIndex>0)) }then begin
  1160.      result.Left:= result.Left + 1;
  1161.      result.Right:= result.Right + 1;
  1162.   end
  1163. end;
  1164. function TfcCustomTreeView.ItemRect(Node: TfcTreeNode; LabelOnly: Boolean): TRect;
  1165. begin
  1166.   result := Rect(0,0,0,0);
  1167.   if Node = nil then Exit;
  1168.   result := Node.DisplayRect(LabelOnly);
  1169. end;
  1170. Function TfcCustomTreeView.GetFirstSibling(Node: TfcTreeNode): TfcTreeNode;
  1171. var parentNode: TfcTreeNode;
  1172. begin
  1173.    if Node=Nil then
  1174.       parentNode:= Node
  1175.    else
  1176.       parentNode:= Node.parent;
  1177.    if parentNode=nil then
  1178.        result := Items.GetFirstNode as TfcTreeNode
  1179.    else
  1180.        result := TfcTreeNode(parentNode.GetFirstChild);
  1181. end;
  1182. procedure TfcCustomTreeView.DoDrawText(TreeView: TfcCustomTreeView;
  1183.          Node: TfcTreeNode; ARect: TRect; AItemState: TfcItemStates;
  1184.          var DefaultDrawing: boolean);
  1185. begin
  1186.    DefaultDrawing:= True;
  1187.    if Assigned(FOnDrawText) then FOnDrawText(Self, Node, ARect, AItemState, defaultDrawing); // -ksw (Added Canvas parameter)
  1188. end;
  1189. procedure TfcCustomTreeView.Compare(Node1, Node2: TfcTreeNode;
  1190.    lParam: integer; var Result: integer);
  1191. begin
  1192.    if Assigned(OnCompare) then
  1193.       OnCompare(self, Node1, Node2, lParam, Result)
  1194.    else
  1195.       Result := lstrcmp(PChar(Node1.Text), PChar(Node2.Text));
  1196. end;
  1197. function TfcCustomTreeView.GetDisplayText(Node: TfcTreeNode): string;
  1198. begin
  1199.    result:= node.text;
  1200. end;
  1201. procedure TfcCustomTreeView.LoadCanvasDefaults(Node: TfcTreeNode; AItemState: TfcItemStates);
  1202. begin
  1203.     Canvas.Font.Assign(Font);
  1204.     Canvas.Brush.Color := Color;
  1205.     if (fcisSelected in AItemState) and not (tvoHideSelection in Options) then
  1206.        Canvas.Brush.Color := clBtnFace;
  1207.     if (((fcisSelected in AItemState) and ((DropTarget = nil) or (DropTarget = Node))) or Node.DropTarget)
  1208.        and not FMultiSelectAttributes.enabled then
  1209.     begin
  1210.        if Focused then begin
  1211.           Canvas.Brush.Color := clHighlight;
  1212.           Canvas.Font.Color := clHighlightText;
  1213.        end
  1214.        else if not (tvoHideSelection in Options) then begin
  1215.            if InactiveFocusColor<>Color then
  1216.               Canvas.Brush.Color:= InactiveFocusColor
  1217.            else
  1218.               Canvas.Brush.Color:= clGray;
  1219.           Canvas.Font.Color:= Font.Color;
  1220.        end
  1221.     end
  1222.     else if FMultiSelectAttributes.enabled then
  1223.     begin
  1224.        if (Node as TfcTreeNode).FMultiSelected then
  1225.        begin
  1226.           Canvas.Brush.Color := clHighlight;
  1227.           Canvas.Font.Color := clHighlightText;
  1228.        end
  1229.        else begin
  1230.           Canvas.Brush.Color := Color;
  1231.           Canvas.Font.Color := Font.Color;
  1232.        end
  1233.     end
  1234.     else begin
  1235.       Canvas.Brush.Color := Color;
  1236.       Canvas.Font.Color := Font.Color;
  1237.     end;
  1238. end;
  1239. procedure fcTreeViewError(const Msg: string);
  1240. begin
  1241.   raise EfcTreeViewError.Create(Msg);
  1242. end;
  1243. function TfcCustomTreeView.ProcessKeyPress(Key: char; shift: TShiftState): boolean;
  1244. begin
  1245.    result:= false;
  1246. end;
  1247. function TfcCustomTreeView.IsRowSelect: boolean;
  1248. begin
  1249.    result:= tvoRowSelect in Options;
  1250. //   result:= tvoRowSelect in Options;
  1251. end;
  1252. procedure TfcCustomTreeView.BeginItemPainting(Node: TfcTreeNode; ARect: TRect; AItemState: TfcItemStates);
  1253. begin
  1254. end;
  1255. procedure TfcCustomTreeView.EndPainting;
  1256. var r: TRect;
  1257.     state: TfcItemStates;
  1258. begin
  1259.   if FCanvas = Canvas then exit;
  1260.   { workaround for TreeView bug in Microsoft control when there is exactly one
  1261.     item displayed }
  1262.   if (Items.Count>0) and (DisplayedItems=0) and True then
  1263.   begin
  1264.      r := Items[0].DisplayRect(True);
  1265.      state:= [fcisSelected];
  1266.      if focused then
  1267.         state:= state + [fcisFocused];
  1268.      EndItemPainting(Items[0], r, state);
  1269.   end;
  1270.   if Items.Count>0 then begin { 1/24/99 -
  1271.                                 If no records, then clearing done by erasebckground message
  1272.                                 Don't rely upn paintcanvas as BeginPainting may not be called
  1273.                                 if Items.Count is 0. }
  1274.     r := ClientRect;
  1275.     OffsetRect(r, 1, 0);
  1276.     FCanvas.CopyMode:= cmSrcCopy;
  1277.     FCanvas.CopyRect(
  1278.      Rect(0,0,Canvas.ClipRect.Right,Canvas.ClipRect.Bottom),
  1279.      Canvas, Canvas.ClipRect);
  1280.   end;
  1281. end;
  1282. procedure TfcCustomTreeView.BeginPainting;
  1283. begin
  1284.   { Ensure that stateimages does not have exactly 1 image when painting a checkbox}
  1285.   { A bug in the Microsoft Tree control causs a gpf when there is exactly 1 state image when
  1286.     clicking on a checkbox }
  1287.   if (StateImages<>Nil) and (StateImages.Count=1) and
  1288.      not (csDesigning in ComponentState) then
  1289.   begin
  1290.     StateImages.Clear;
  1291.     TreeView_SetImageList(Handle, FFixBugImageList.Handle, TVSIL_STATE);
  1292.   end;
  1293.   if FCanvas = Canvas then exit;
  1294.   { Fill with background color }
  1295.   FPaintBitmap.Width := Width;
  1296.   FPaintBitmap.Height := Height;
  1297.   FPaintBitmap.Canvas.Brush.Color := color;
  1298.   FPaintBitmap.Canvas.FillRect(Rect(0, 0, FPaintBitmap.Width, FPaintBitmap.Height));
  1299. end;
  1300. procedure TfcCustomTreeView.CalcNodeAttributes(Node: TfcTreeNode; AItemState: TfcItemStates);
  1301. begin
  1302.     if Assigned(FOnCalcNodeAttributes) then
  1303.       FOnCalcNodeAttributes(Self, Node as TfcTreeNode, AItemState);
  1304. end;
  1305. procedure TfcCustomTreeView.EndItemPainting(Node: TfcTreeNode; ARect: TRect; AItemState: TfcItemStates);
  1306. const HDM_ORDERTOINDEX = HDM_FIRST + 15;
  1307.       HDM_GETORDERARRAY = HDM_FIRST + 17;
  1308.       DrawTextUnderscoreStyles: array[Boolean] of Integer = (DT_NOPREFIX, 0);
  1309.       wwTEXTPADDING = 4;
  1310. var r, FocusRect, CalcRect: TRect;
  1311.     DefaultDrawing: boolean;
  1312.     flags:Longint;
  1313. begin
  1314.   { Imagelist changing after nodes loaded causes stateindex to no longer be -1 }
  1315.   if (Node.StateIndex <>(Node.GetStateIndex shr 12) -1) then
  1316.   begin
  1317.      Node.StateIndex:= Node.StateIndex;
  1318. //     exit;  { RSW - Don't paint now, let Microsoft control paint this later}
  1319.   end;
  1320.   PaintLines(Node);
  1321.   if MultiSelectCheckBoxNeeded(Node) then Node.StateIndex:= 1;
  1322.   LoadCanvasDefaults(Node, AItemState);
  1323.   if (fcisHot in AItemState) or
  1324.      ((tvoAutoURL in Options) and (Node.StringData<>'')) then
  1325.   begin
  1326.      if not (fcisSelected in AItemState) or not Focused then { 4/8/99 - No focus still paint clBlue }
  1327.         Canvas.Font.Color:= clBlue;
  1328.      Canvas.Font.Style:= [fsUnderline];
  1329.   end;
  1330.   CalcNodeAttributes(Node, AItemState);
  1331.   Canvas.Refresh;
  1332.   TreeView_GetItemRect(Handle, Node.ItemID, r, False);
  1333.   r.Left := ARect.Left;
  1334.   if Canvas.Font.Style * [fsBold, fsItalic] <> [] then begin
  1335.     flags := 0;
  1336.     CalcRect := ARect;
  1337.     //5/10/2002 - Use DrawText to calculate font based on current canvas settings from OnCalcNodeAttributes event.
  1338.     DrawText(Canvas.Handle, PChar(GetDisplayText(Node)), -1, calcrect, flags or DT_CALCRECT);
  1339. //    ARect := Rect(ARect.Left, ARect.Top, ARect.Left +
  1340. //                   Canvas.TextWidth(GetDisplayText(Node)) + wwTEXTPADDING, ARect.Bottom);
  1341.     ARect := Rect(ARect.Left, ARect.Top, ARect.Left + (CalcRect.Right-CalcRect.Left) + wwTEXTPADDING, ARect.Bottom);
  1342.   end;
  1343.   Canvas.Pen.Color := Color;
  1344.   FocusRect:= ARect;
  1345.   if IsRowSelect then
  1346.   begin
  1347.     FocusRect.Right:= Width-4;
  1348.     if UseImages(node) then dec(FocusRect.Left, TImageList(Images).Width);
  1349.     if UseStateImages(node) then begin
  1350.        if StateImages<>nil then
  1351.           dec(FocusRect.Left, TImageList(StateImages).Width)
  1352.        else
  1353.           dec(FocusRect.Left, FixBugImageListSize)
  1354.     end;
  1355.     if UseImages(node) or UseStateImages(node) then dec(FocusRect.Left, 4);
  1356.     if not MultiSelectAttributes.enabled then
  1357.        FocusRect.Bottom := FocusRect.Bottom + 1;
  1358.   end;
  1359.   if (not IsEditing) or not (fcIsSelected in AItemState) then
  1360.   begin
  1361.     if (Canvas.Brush.Color <> clNone) and
  1362.        ((not MultiSelectAttributes.enabled) or Node.MultiSelected) then
  1363.     begin
  1364.       if (fcisFocused in AItemState) then
  1365.          Canvas.Rectangle(FocusRect.Left, FocusRect.Top, FocusRect.Right, FocusRect.Bottom)
  1366.       else begin
  1367.          FocusRect.bottom:= FocusRect.Bottom - 1;
  1368.          Canvas.FillRect(FocusRect);
  1369.          FocusRect.bottom:= FocusRect.Bottom + 1;
  1370.       end
  1371.     end
  1372.   end;
  1373.   PaintImage(Node, AItemState);
  1374.   InflateRect(ARect, -1, -1);
  1375.   ARect.Left:= ARect.Left + 1;
  1376.   SetBkMode(Canvas.Handle, TRANSPARENT);
  1377.   try
  1378.      if (not IsEditing) or not (fcIsSelected in AItemState) then
  1379.      begin
  1380.         DoDrawText(self, Node, ARect, AItemState, DefaultDrawing);
  1381.         if DefaultDrawing then begin
  1382.            Canvas.DrawText(GetDisplayText(Node), ARect, DT_END_ELLIPSIS or DrawTextUnderscoreStyles[False]);
  1383.            if (fcisFocused in AItemState) and Focused and
  1384.                ((not IsRowSelect) or MultiSelectAttributes.enabled) then
  1385.            begin
  1386.               Canvas.DrawFocusRect(FocusRect);
  1387.            end
  1388.         end
  1389.      end
  1390.   finally
  1391.      SetBkMode(Canvas.Handle, OPAQUE);
  1392.   end
  1393. end;
  1394. (*
  1395. procedure TfcCustomTreeView.EndItemPainting(Node: TfcTreeNode; ARect: TRect; AItemState: TfcItemStates);
  1396. const HDM_ORDERTOINDEX = HDM_FIRST + 15;
  1397.       HDM_GETORDERARRAY = HDM_FIRST + 17;
  1398.       DrawTextUnderscoreStyles: array[Boolean] of Integer = (DT_NOPREFIX, 0);
  1399.       fcTEXTPADDING = 4;
  1400. var r: TRect;
  1401.     DefaultDrawing: boolean;
  1402. begin
  1403.   { Imagelist changing after nodes loaded causes stateindex to no longer be -1 }
  1404.   if (Node.StateIndex <>(Node.GetStateIndex shr 12) -1) then
  1405.   begin
  1406.      Node.StateIndex:= Node.StateIndex;
  1407.   end;
  1408.   PaintLines(Node);
  1409.   if MultiSelectCheckBoxNeeded(Node) then Node.StateIndex:= 1;
  1410.   LoadCanvasDefaults(Node, AItemState);
  1411.   CalcNodeAttributes(Node, AItemState);
  1412.   Canvas.Refresh;
  1413.   if Canvas.Font.Style * [fsBold, fsItalic] <> [] then
  1414.     ARect := Rect(ARect.Left, ARect.Top, ARect.Left +
  1415.                   Canvas.TextWidth(GetDisplayText(Node)) + fcTEXTPADDING, ARect.Bottom);
  1416.   Canvas.Pen.Color := Color;
  1417.   r := ARect;
  1418.   if RowSelect then
  1419.   begin
  1420.     ARect.Left := LevelRect(Node).Left;
  1421.     ARect.Right := Width;
  1422.   end;
  1423.   if Canvas.Brush.Color <> clNone then
  1424.   begin
  1425.     if (isFocused in AItemState) then
  1426.        Canvas.Rectangle(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom)
  1427.     else Canvas.FillRect(ARect);
  1428.   end;
  1429.   if RowSelect then ARect.Left := r.Left;
  1430.   PaintImage(Node, AItemState);
  1431.   TreeView_GetItemRect(Handle, Node.ItemID, r, False);
  1432.   r.Left := ARect.Left;
  1433.   InflateRect(ARect, -1, -1);
  1434.   SetBkMode(Canvas.Handle, TRANSPARENT);
  1435.   try
  1436.      DoDrawText(self, Node, ARect, AItemState, DefaultDrawing);
  1437.      if DefaultDrawing then begin
  1438.         Canvas.DrawText(GetDisplayText(Node), ARect, DT_END_ELLIPSIS or DrawTextUnderscoreStyles[tvoUnderscoreAllowed in FOptions]);
  1439.         if (isFocused in AItemState) and Focused then begin
  1440.            InflateRect(ARect, 1, 1);
  1441.            Canvas.Brush.Color := clBlack;
  1442.            SetTextColor(Canvas.Handle, clWhite);
  1443.            Canvas.DrawFocusRect(ARect);
  1444.         end
  1445.      end
  1446.   finally
  1447.      SetBkMode(Canvas.Handle, OPAQUE);
  1448.   end
  1449. end;
  1450. *)
  1451. procedure TfcCustomTreeView.PaintButton(Node: TfcTreeNode;
  1452.    pt: TPoint; Size: integer);
  1453. var offset: integer;
  1454.     drawRect: TRect;
  1455.     OrigColor: TColor;
  1456.     StateFlags: Word;
  1457.     {$ifdef fcUseThemeManager}
  1458.     Details: TThemedElementDetails;
  1459.     {$endif}
  1460. begin
  1461.    OrigColor:= Canvas.Brush.Color;
  1462.    if not (tvoShowButtons in Options) then Exit;
  1463.    offset:= Size;
  1464.    Offset:= Offset div 2;
  1465.    DrawRect.Left:= pt.x - offset;
  1466.    DrawRect.Right:= pt.x + offset+1;
  1467.    DrawRect.Top:= pt.y-offset;
  1468.    DrawRect.Bottom:= pt.y+offset+1;
  1469.    if tvoExpandButtons3D in Options then
  1470.    begin
  1471.       Canvas.Brush.Color := clBtnFace;
  1472.       Canvas.FillRect(DrawRect);
  1473.       Canvas.Brush.Color := clBtnShadow;
  1474.       StateFlags:= DFCS_BUTTONPUSH;
  1475.       if Down and (MouseNode=Node) then
  1476.          StateFlags := StateFlags or DFCS_PUSHED;
  1477.       with DrawRect do
  1478.          DrawFrameControl(Canvas.Handle, Rect(Left, Top, Right+1, Bottom+1),
  1479.               DFC_BUTTON, StateFlags);
  1480.       if ColorToRGB(Color)=clWhite then
  1481.       begin
  1482.          with DrawRect, Canvas do begin
  1483.            Pen.Color := clBtnFace;
  1484.            Polyline([Point(Left-1, Bottom), Point(Left-1, Top-1), Point(Right+1, Top-1)]);
  1485.          end;
  1486.       end;
  1487.       if Down and (MouseNode = Node) then
  1488.       begin
  1489.          pt.x:= pt.x+1;
  1490.          pt.y:= pt.y+1;
  1491.          DrawRect.Left:= DrawRect.Left + 1;
  1492.          DrawRect.Top:= DrawRect.Top + 1;
  1493.          DrawRect.Right:= DrawRect.Right + 1;
  1494.          DrawRect.Bottom:= DrawRect.Bottom + 1;
  1495.       end;
  1496.       Canvas.Brush.Color := Color;
  1497.       Canvas.Pen.Color := clBlack;
  1498.       if not Node.Expanded then
  1499.          Canvas.Polyline([Point(pt.x, DrawRect.Top+Offset div 2), Point(pt.x, DrawRect.Bottom-Offset div 2)]);
  1500.       Canvas.Polyline([Point(DrawRect.Left+Offset div 2, pt.y), Point(DrawRect.Right-Offset div 2, pt.y)]);
  1501.       Canvas.Brush.Color:= OrigColor;
  1502.    end
  1503.    else begin
  1504.       if fcUseThemes(self) then
  1505. //      if ThemeServices.ThemesEnabled then
  1506.       begin
  1507.         {$ifdef fcUseThemeManager}
  1508.         if Node.expanded then
  1509.           Details := ThemeServices.GetElementDetails(ttGlyphOpened)
  1510.         else
  1511.           Details := ThemeServices.GetElementDetails(ttGlyphClosed);
  1512.         ThemeServices.DrawElement(Canvas.Handle, Details, DrawRect);
  1513.         {$endif}
  1514.       end
  1515.       else begin
  1516.          Canvas.Brush.Color := clWhite;
  1517.          Canvas.FillRect(DrawRect);
  1518.          Canvas.Brush.Color := clBtnShadow;
  1519.          Canvas.FrameRect(DrawRect);
  1520.          Canvas.Brush.Color := Color;
  1521.          Canvas.Pen.Color := clBlack;
  1522.          if not Node.Expanded then
  1523.             Canvas.Polyline([Point(pt.x, DrawRect.Top+Offset div 2), Point(pt.x, DrawRect.Bottom-Offset div 2)]);
  1524.          Canvas.Polyline([Point(DrawRect.Left+Offset div 2, pt.y), Point(DrawRect.Right-Offset div 2, pt.y)]);
  1525.          Canvas.Brush.Color:= OrigColor;
  1526.       end
  1527.    end;
  1528. end;
  1529. Function TfcCustomTreeView.GetCenterPoint(ARect: TRect): TPoint;
  1530. var r: TRect;
  1531. begin
  1532.    r:= ARect;
  1533.    if odd(fcRectHeight(r) div 2) then
  1534.       result.y:= r.Top + (fcRectHeight(r)+2) div 2
  1535.    else
  1536.       result.y:= r.Top + (fcRectHeight(r)) div 2;
  1537.    if odd(fcRectWidth(r) div 2) then
  1538.       result.x:= r.Left + (fcRectWidth(r)) div 2
  1539.    else
  1540.       result.x:= r.Left + (fcRectWidth(r)) div 2;
  1541. end;
  1542. procedure TfcCustomTreeView.PaintLines(Node: TfcTreeNode);
  1543. var LevelNode: TfcTreeNode;
  1544.     r: TRect;
  1545.     LineTop, LineBottom: TPoint;
  1546.     X,Y: integer;
  1547.     LineStartX: integer;
  1548.     size: integer;
  1549.     Function GetStartX(Node: TfcTreeNode): integer;
  1550.     var Offset : integer;
  1551.         r: TRect;
  1552.     begin
  1553.         r := LevelRect(Node);
  1554.         Offset:= (((r.Bottom - r.Top) div 2) div 2)+2;
  1555.         Offset:= fcMin(Offset, MaxCheckboxSize);
  1556.         result:= r.Left + offset + 1; //r.Right + Offset + 1;
  1557. {        if Node.Level>0 then begin
  1558.            r:= LevelRect(Node.parent);
  1559.            Offset:= ((fcRectHeight(r) div 2) div 2)+2;
  1560.            Offset:= fcMin(Offset, MaxCheckboxSize);
  1561.            result:= r.Right + Offset + 1;
  1562.         end
  1563.         else begin
  1564.            r := LevelRect(Node);
  1565.            result:= r.Left + fcRectWidth(r) div 2;
  1566.         end}
  1567.     end;
  1568. begin
  1569.   r:= LevelRect(Node);
  1570.   if tvoShowLines in Options then
  1571.   begin
  1572.     Canvas.Pen.Color := FLineColor; //clBtnShadow; { for line drawing }
  1573.     LevelNode := Node;
  1574.     while (LevelNode <> nil) and ((LevelNode.Level = 0) or (LevelNode.Parent <> nil)) do
  1575.     begin
  1576.       with ItemRect(Node, False) do
  1577.       begin
  1578.         r := LevelRect(LevelNode);
  1579.         r.Top := Top;
  1580.         r.Bottom := Bottom;
  1581.       end;
  1582.       LineStartX:= GetStartX(LevelNode);
  1583.       LineTop:= Point(LineStartX, r.Top);
  1584.       LineBottom:= Point(LineStartX, r.Bottom);
  1585.       if LevelNode.Level = Node.Level then
  1586.       begin
  1587.        if (Node.GetPrevSibling = nil) and (Node.Parent = nil) then
  1588.           inc(LineTop.y, GetCenterPoint(r).y);
  1589.         if Node.GetNextSibling = nil then
  1590.           dec(LineBottom.y, fcRectHeight(r) div 2 - 1);
  1591.       end;
  1592.       if (LevelNode.GetNextSibling <> nil) or (LevelNode.Level = Node.Level) then
  1593.         Canvas.DottedLine(LineTop, LineBottom);
  1594.       LevelNode := LevelNode.Parent;
  1595.     end;
  1596.     r := LevelRect(Node);
  1597.     if (tvoShowRoot in Options) or (Node.Level <> 0) then
  1598.     begin
  1599.       y:= GetCenterPoint(r).y;
  1600.       x:= GetStartX(Node);
  1601.       if UseStateImages(Node) or MultiSelectAttributes.enabled then
  1602.       begin
  1603.          if (CheckboxNeeded(Node as TfcTreeNode)) then
  1604.          begin
  1605.             r.right:= r.right - 2;
  1606.          end
  1607.       end;
  1608.       Canvas.DottedLine(Point(x, y), Point(r.Right, y));
  1609.     end
  1610.   end;
  1611.   if Node.HasChildren then begin
  1612.      size:= (fcRectHeight(r) div 2);
  1613.      size:= fcMax(size, 8);
  1614.      PaintButton(Node, Point(GetStartX(Node), GetCenterPoint(r).y), size)
  1615.   end
  1616. end;
  1617. procedure TfcCustomTreeView.PaintImage(Node: TfcTreeNode; State: TfcItemStates);
  1618. const ItemChecked: array[Boolean] of Integer = (0, DFCS_CHECKED);
  1619.       CheckBoxFlat: array[Boolean] of Integer = (0, DFCS_FLAT);
  1620.       DrawSelected: array[Boolean] of Integer = (ILD_NORMAL, ILD_SELECTED);
  1621.       Grayed: array[Boolean] of Integer = (0, DFCS_INACTIVE);
  1622. var r: TRect;
  1623.     x: Integer;
  1624.     Index: Integer;
  1625.     cp: TPoint;
  1626.     offset: integer;
  1627.     Style: UINT;
  1628.     BlendColor: TColorRef;
  1629.     ARect: TRect;
  1630.     {$ifdef fcUseThemeManager}
  1631.     Details: TThemedElementDetails;
  1632.     CheckboxStyle: TThemedButton;
  1633.     PaintRect: TRect;
  1634.     {$endif}
  1635.     {$ifdef fcUseThemeManager}
  1636.       function IsHotRadioOrCheckbox: boolean;
  1637.       var SP, MousePos: TPoint;
  1638.           hitTest: TfcHitTests;
  1639.       begin
  1640.           GetCursorPos(MousePos);
  1641.           SP:= ScreenToClient(MousePos);
  1642.           hitTest:= GetHitTestInfoAt(sp.x, sp.y);
  1643.           ClickedNode:= GetNodeAt(sp.x, sp.y) as TfcTreeNode;
  1644.           result:= (ClickedNode=Node) and (fchtOnStateIcon in hittest);
  1645.       end;
  1646.     function GetRadioButtonThemeStyle(Pressed: boolean): TThemedButton;
  1647.     begin
  1648.          if not Enabled then
  1649.          begin
  1650.             Result:= tbRadioButtonCheckedDisabled
  1651.          end
  1652.          else begin
  1653.             if Node.checked then
  1654.             begin
  1655.               if Pressed then
  1656.                  Result:= tbRadioButtonCheckedPressed
  1657.               else begin
  1658.                  if IsHotRadioOrCheckbox then
  1659.                     Result:= tbRadioButtonCheckedHot
  1660.                  else
  1661.                     Result:= tbRadioButtonCheckedNormal
  1662.               end
  1663.             end
  1664.             else begin
  1665.               if Pressed  then
  1666.                  Result:= tbRadioButtonUncheckedPressed
  1667.               else begin
  1668.                  if IsHotRadioOrCheckBox then
  1669.                    Result:= tbRadioButtonUncheckedHot
  1670.                  else
  1671.                    Result:= tbRadioButtonUncheckedNormal
  1672.               end
  1673.             end;
  1674.          end;
  1675.     end;
  1676.     {$endif}
  1677. begin
  1678.   r := LevelRect(Node);
  1679.   if not((Images = nil) or
  1680.     ((Node.ImageIndex < 0)) or
  1681.      (Node.ImageIndex >= Images.Count)) then
  1682.   begin
  1683.     x := r.Right;
  1684.     if UseStateImages(Node) then
  1685.     begin
  1686.        if StateImages<>nil then
  1687.           inc(x, TImageList(StateImages).Width)
  1688.        else
  1689.           inc(x, FixBugImageListSize);
  1690.     end;
  1691.     if (not (fcisSelected in State)) or (Node.SelectedIndex < 0) or (Node.SelectedIndex >= Images.Count) then
  1692.       Index := Node.ImageIndex
  1693.     else Index := Node.SelectedIndex;
  1694.     Style:=
  1695.        DrawSelected[((Node.Selected and (DropTarget = nil)) or Node.DropTarget) and (TImageList(Images).BlendColor <> clNone)
  1696.           and not IsRowSelect];
  1697.     if Node.Cut then
  1698.     begin
  1699.        Style:= Style or ILD_BLEND50;
  1700.        BlendColor:= clWhite;
  1701.     end
  1702.     else begin
  1703.        BlendColor:= ColorToRGB(TImageList(Images).BlendColor);
  1704.     end;
  1705.     if Node.OverlayIndex>=0 then
  1706.       Style:= Style or UINT(IndexToOverlayMask(Node.OverlayIndex+1));
  1707.     ImageList_DrawEx(Images.Handle, Index, Canvas.Handle,
  1708.       x, r.Top + (r.Bottom - r.Top - TImageList(Images).Height) div 2, 0, 0,
  1709.       CLR_NONE, BlendColor, Style);
  1710.   end;
  1711.   if UseStateImages(Node) then
  1712.   begin
  1713.     if (not CheckboxNeeded(Node as TfcTreeNode)) then
  1714.        StateImages.Draw(Canvas, r.right, r.Top + (r.Bottom-r.Top-TImageList(StateImages).Height) div 2, Node.StateIndex)
  1715. //       StateImages.Draw(Canvas, r.Right, r.Top, Node.StateIndex)
  1716.     else begin
  1717.       cp:= GetCenterPoint(r);
  1718.       Offset:= ((fcRectHeight(r) div 2) div 2)+2;
  1719.       Offset:= fcMin(Offset, MaxCheckboxSize);
  1720.       if Node.CheckboxType=tvctRadioGroup then
  1721.       begin
  1722.          ARect:= Rect(r.right+1, cp.y-offset, r.Right + 2*offset+2, cp.y+offset+1);
  1723.          if fcUseThemes(self) then
  1724.          begin
  1725.            {$ifdef fcUseThemeManager}
  1726.            CheckboxStyle:= GetRadioButtonThemeStyle(False);
  1727.            Details := ThemeServices.GetElementDetails(CheckboxStyle);
  1728.            PaintRect := ARect;
  1729.            ThemeServices.DrawElement(Canvas.Handle, Details, PaintRect);
  1730.            PaintRect := ThemeServices.ContentRect(Canvas.Handle, Details, PaintRect);
  1731.            {$endif}
  1732.          end
  1733.          else begin
  1734.            DrawFrameControl(Canvas.Handle, Rect(r.right+1, cp.y-offset, r.Right + 2*offset+2, cp.y+offset+1),
  1735.            DFC_BUTTON,
  1736.            Grayed[Node.Grayed] or
  1737.            DFCS_BUTTONRADIO or CheckBoxFlat[tvoFlatCheckBoxes in Options] or ItemChecked[Node.checked])
  1738.          end
  1739.       end
  1740.       else begin
  1741.          ARect:= Rect(r.right+1, cp.y-offset, r.Right + 2*offset+2, cp.y+offset+1);
  1742.          if fcUseThemes(self) then
  1743.          begin
  1744.            {$ifdef fcUseThemeManager}
  1745.            if Node.Grayed then
  1746.            begin
  1747.               if Node.checked then CheckboxStyle:= tbCheckboxCheckedDisabled
  1748.               else CheckboxStyle:= tbCheckboxUnCheckedDisabled
  1749.            end
  1750.            else begin
  1751.               if IsHotRadioOrCheckBox then
  1752.               begin
  1753.                  if Node.checked then CheckboxStyle:= tbCheckboxCheckedHot
  1754.                  else CheckboxStyle:= tbCheckboxUnCheckedHot
  1755.               end
  1756.               else begin
  1757.                  if Node.checked then CheckboxStyle:= tbCheckboxCheckedNormal
  1758.                  else CheckboxStyle:= tbCheckboxUnCheckedNormal
  1759.               end;
  1760.            end;
  1761.            Details := ThemeServices.GetElementDetails(CheckboxStyle);
  1762.            PaintRect := ARect;
  1763.            ThemeServices.DrawElement(Canvas.Handle, Details, PaintRect);
  1764.            PaintRect := ThemeServices.ContentRect(Canvas.Handle, Details, PaintRect);
  1765.            {$endif}
  1766.          end
  1767.          else begin
  1768.            DrawFrameControl(Canvas.Handle, ARect,
  1769.               DFC_BUTTON, Grayed[Node.Grayed] or
  1770.                  DFCS_BUTTONCHECK or CheckBoxFlat[tvoFlatCheckBoxes in Options]
  1771.                  or ItemChecked[Node.checked])
  1772.          end
  1773.       end
  1774.     end;
  1775.   end;
  1776. end;
  1777. procedure TfcCustomTreeView.SetOptions(Value: TfcTreeViewOptions);
  1778. const TVS_FULLROWSELECT       = $1000;
  1779.       TVS_TRACKSELECT         = $0200;
  1780. var ChangedOptions: TfcTreeViewOptions;
  1781. begin
  1782.   if not HandleAllocated then begin
  1783.      FOptions := Value;
  1784.      exit;
  1785.   end;
  1786.   if Value <> FOptions then
  1787.   begin
  1788.     ChangedOptions := (FOptions + Value) - (FOptions * Value);
  1789.     if tvoRowSelect in ChangedOptions then
  1790.        SetComCtlStyle(Self, TVS_FULLROWSELECT, tvoRowSelect in Value);
  1791.     if tvoShowRoot in ChangedOptions then
  1792.        SetComCtlStyle(Self, TVS_LINESATROOT, tvoShowRoot in Value);
  1793.     if tvoShowLines in ChangedOptions then
  1794.        SetComCtlStyle(Self, TVS_HASLINES, tvoShowLines in Value);
  1795.     if tvoShowButtons in ChangedOptions then
  1796.        SetComCtlStyle(Self, TVS_HASBUTTONS, tvoShowButtons in Value);
  1797.     if tvoHideSelection in ChangedOptions then
  1798.        SetComCtlStyle(Self, TVS_SHOWSELALWAYS, not (tvoHideSelection in Value));
  1799.     if tvoHotTrack in ChangedOptions then
  1800.        SetComCtlStyle(Self, TVS_TRACKSELECT, tvoHotTrack in Value);
  1801.     if tvoEditText in ChangedOptions then
  1802.        SetComCtlStyle(Self, TVS_EDITLABELS, tvoEditText in Value);
  1803.     FOptions := Value;
  1804.     Invalidate;
  1805.   end;
  1806. end;
  1807. Function TfcCustomTreeView.MultiSelectCheckboxNeeded(Node: TfcTreeNode): boolean;
  1808. begin
  1809.    if Node=nil then result:= False
  1810.    else with FMultiSelectAttributes do
  1811.       result:= Enabled and MultiSelectCheckbox and (ValidMultiSelectLevel(Node.Level))
  1812. end;
  1813. Function TfcCustomTreeView.CheckboxNeeded(Node: TfcTreeNode): boolean;
  1814. begin
  1815.    result:= MultiSelectCheckBoxNeeded(Node) or
  1816.             (TfcTreeNode(Node).CheckboxType<>tvctNone);
  1817. end;
  1818. function TfcCustomTreeView.UseImages(Node: TfcTreeNode): Boolean;
  1819. begin
  1820.    result:= (Images<>nil) and (Node.ImageIndex<>-2);
  1821. end;
  1822. function TfcCustomTreeView.UseStateImages(Node: TfcTreeNode): Boolean;
  1823. begin
  1824.   { 3/8/99 - Don't allow StateIndex to be multiple of 16,
  1825.    as the Microsoft TreeView control ignores these state images }
  1826.   result := ((StateImages <> nil) and
  1827.    (Node.StateIndex>=1) and ((Node.StateIndex mod 16)<>0)) and
  1828. //  result := ((StateImages <> nil) and (Node.StateIndex >= 1) and
  1829.     (Node.StateIndex < StateImages.Count);
  1830.   if MultiSelectCheckBoxNeeded(Node) then result:= True
  1831.   else if ((Node as TfcTreeNode).CheckboxType<>tvctNone) and (Node.StateIndex>=1) then
  1832.      result:= True;
  1833. end;
  1834. function TfcCustomTreeView.GetItemHeight: ShortInt;
  1835. begin
  1836.   result := TreeView_GetItemHeight(Handle);
  1837. end;
  1838. procedure TfcCustomTreeView.SetItemHeight(Value: ShortInt);
  1839. begin
  1840.   TreeView_SetItemHeight(Handle, Value);
  1841. end;
  1842. procedure TfcCustomTreeView.MouseUp(Button: TMouseButton; Shift: TShiftState;
  1843.   X, Y: Integer);
  1844. var Node: TfcTreeNode;
  1845. begin
  1846.    inherited MouseUp(Button, Shift, X, Y);
  1847.    Node:= nil; { Make compiler happy}
  1848.    if Assigned(FOnMouseUp) or (tvoAutoURL in Options) then
  1849.       Node:= GetNodeAt(X, Y) as TfcTreeNode;
  1850.    if Assigned(FOnMouseUp) then
  1851.       FOnMouseUp(self, Node, Button, Shift, X, Y);
  1852.    if tvoAutoURL in Options then
  1853.    begin
  1854.       if (Button = mbLeft) and (Node<>nil) and (Node.StringData<>'') and
  1855.          (fchtOnItem in GetHitTestInfoAt(x,y)) then
  1856.       begin
  1857.          Screen.Cursor:= crHourGlass;
  1858.          ShellExecute(Handle, 'OPEN', PChar(Node.StringData), nil, nil, sw_shownormal);
  1859.          Screen.Cursor:= crDefault; { 10/30/98 - Restore to default cursor }
  1860.       end
  1861.    end
  1862. end;
  1863. procedure TfcCustomTreeView.MouseDown(Button: TMouseButton; Shift: TShiftState;
  1864.   X, Y: Integer);
  1865. var Node, CurNode, LastNode: TfcTreeNode;
  1866.     hitTest: TfcHitTests;
  1867.    Function SameLevelShiftSelect: boolean;
  1868.    begin
  1869. {       if FMultiSelectAttributes.MultiSelectLevel=-1 then begin
  1870.           result:= True;
  1871.           exit;
  1872.        end;}
  1873.        result:=
  1874.          (ssShift in Shift) and
  1875.          (LastSelectedNode<>Nil) and (LastSelectedNode.parent=Node.parent);
  1876.    end;
  1877. begin
  1878.    inherited MouseDown(Button, Shift, X, Y);
  1879.    Node:= GetNodeAt(X, Y) as TfcTreeNode;
  1880.    if Assigned(FOnMouseDown) then FOnMouseDown(self, Node, Button, Shift, X, Y);
  1881.    if (ssDouble in Shift) and Assigned(FOnDblClick) then FOnDblClick(self, Node, Button, Shift, X, Y);
  1882.    if Node=Nil then exit;
  1883.    hitTest:= GetHitTestInfoAt(X, Y);
  1884.    if (ssDouble in Shift) and (Button = mbLeft) then
  1885.    begin
  1886.       Exit;
  1887.    end;
  1888.    { 11/12/99 - If right mouse button, then exit so multi-selected records are not unselected }
  1889.    if (not RightClickSelects) and (ssRight in Shift) then exit;
  1890.    if Node=BeforeMouseDownNode then { Bring up edit control }
  1891.    begin
  1892.       if (fchtOnLabel in hittest) and (tvoEditText in Options) then
  1893.       begin
  1894.          FreeHintWindow;
  1895.          LastHintNode:= nil;
  1896.          If (Node<>Nil) and (Images<>Nil) and
  1897.             (Node.imageindex=-2) then
  1898.             SendMessage(Handle, TVM_EDITLABEL, 0, integer(node.ItemID));
  1899.       end;
  1900.    end;
  1901.    if (Node=ClickedNode) and // 1/21/01 - Selection changes so skip toggle checkbox
  1902.       (fchtOnStateIcon in hitTest) and CheckboxNeeded(Node) and (not ReadOnly) then
  1903.    begin
  1904.       if Node.IsRadioGroup then begin
  1905.          Node.Grayed:= False;
  1906.          Node.checked:= True;
  1907.       end
  1908.       else begin
  1909.          if (tvo3StateCheckbox in Options) and
  1910.             Node.checked and (not Node.Grayed) then Node.Grayed:= True
  1911.          else begin
  1912.             Node.checked:= not Node.checked;
  1913.             if (tvo3StateCheckbox in Options) or
  1914.                not (csDesigning in ComponentState) then
  1915.                Node.Grayed:= False;
  1916.          end;
  1917.       end;
  1918.       exit;
  1919.    end;
  1920.    if FMultiSelectAttributes.Enabled and
  1921.    ((IsRowSelect and (X >= LevelRect(Node).Left + FIndent)) or  // -ksw (Added to make behavior more
  1922.     (not IsRowSelect and (hitTest * [fchtOnItem, fchtOnLabel] <> []))) then    // consistent in non-databound cases)
  1923.    begin
  1924.       if (not (ssCtrl in Shift)) and MultiSelectAttributes.AutoUnselect then
  1925.       begin
  1926.          if SameLevelShiftSelect then UnselectAllNodes(LastSelectedNode)
  1927.          else if ValidMultiSelectLevel(Node.Level) then UnselectAllNodes(nil);
  1928.       end;
  1929.       if (ssShift in Shift) then begin
  1930.          if SamelevelShiftSelect then begin
  1931.             if Node.index>LastSelectedNode.index then begin
  1932.                curNode:= LastSelectedNode;
  1933.                LastNode:= Node;
  1934.                if not MultiSelectAttributes.AutoUnselect and
  1935.                  (LastSelectedNode<>nil) then
  1936.                   MultiSelectNode(LastSelectedNode, True, True);
  1937.                while curNode<>LastNode do begin
  1938.                   curNode:= curNode.GetNextSibling as TfcTreeNode;
  1939.                   if curNode=Nil then break;
  1940.                   MultiSelectNode(curNode, True, True);
  1941.                end;
  1942.             end
  1943.             else begin
  1944.                curNode:= Node;
  1945.                LastNode:= LastSelectedNode;
  1946.                while curNode<>LastNode do begin
  1947.                   MultiSelectNode(curNode, True, True);
  1948.                   curNode:= curNode.GetNextSibling as TfcTreeNode;
  1949.                   if curNode=Nil then break;
  1950.                end;
  1951.                if not MultiSelectAttributes.AutoUnselect and
  1952.                  (curNode=LastNode) and (curNode<>nil) then
  1953.                   MultiSelectNode(curNode, True, True);
  1954.             end;
  1955.             if not node.selected then node.selected:= True;
  1956.          end
  1957.       end
  1958.       else begin
  1959.           if Node<>nil then
  1960.              with Node as TfcTreeNode do begin
  1961.                 if MultiSelectAttributes.AutoUnselect or (ssCtrl in Shift) then
  1962.                    MultiSelectNode(Node, not FMultiSelected, True);
  1963.                 if not node.selected then node.selected:= True;
  1964.              end;
  1965.       end;
  1966.    end;
  1967.    if MultiSelectAttributes.enabled and  not (ssShift in Shift) then
  1968.       LastSelectedNode:= Node;
  1969. end;
  1970. procedure TfcCustomTreeView.WMDestroy(var Message: TWMDestroy);
  1971. begin
  1972.   if Items<>Nil then Items.Clear;  { Faster to call special clear code than to just destroy }
  1973.   inherited;
  1974. end;
  1975. procedure TfcCustomTreeView.WMLButtonDblClk(var Message: TWMLButtonDblClk);
  1976. begin
  1977.   if not (tvoExpandOnDblClk in FOptions) then
  1978.   begin
  1979.      with Message do begin { 6/23/99 - Fires OnDblClick event }
  1980.         MouseDown(mbLeft, KeysToShiftState(Keys) + [ssDouble], XPos, YPos);
  1981.      end
  1982.   end
  1983.   else inherited;
  1984. end;
  1985. function TfcCustomTreeView.GetScrollTime: Integer;
  1986. begin
  1987.   result := TreeView_GetScrollTime(Handle);
  1988. end;
  1989. procedure TfcCustomTreeView.SetScrollTime(Value: Integer);
  1990. begin
  1991.   TreeView_SetScrollTime(Handle, Value);
  1992. end;
  1993. { Prevents flicker when changing selected node }
  1994. procedure TfcCustomTreeView.WMPaint(var Message: TWMPaint);
  1995. var
  1996.     DC, MemDC: HDC;
  1997.     MemBitmap, OldBitmap: HBITMAP;
  1998.     PS: TPaintStruct;
  1999.     PaintCliprect: TRect;
  2000.     UpdateRect: TRect;
  2001.     OldSkipErase: boolean;
  2002. begin
  2003.   // 1/31/2002-Call inherited if nodes are in beginupdate/endupdate.
  2004.   if items.fupdatecount>0 then
  2005.   begin
  2006.      inherited;
  2007.      exit;
  2008.   end;
  2009.   windows.GetUpdateRect(Handle, UpdateRect, false);
  2010. {  if not UseAlternateBuffering then
  2011.   begin
  2012.      inherited;
  2013.      exit;
  2014.   end;
  2015. }
  2016.   PaintClipRect:= FCanvas.ClipRect;
  2017.   if PaintClipRect.Right>ClientRect.Right then
  2018.      PaintClipRect.Right:= ClientRect.Right;
  2019.   if UpdateRect.Bottom>ClientRect.Bottom then
  2020.      UpdateRect.Bottom:= (inherited GetClientRect).Bottom;
  2021.   if (UpdateRect.Top=0) and (UpdateRect.Bottom=0) and
  2022.      (UpdateRect.Left=0) and (UpdateRect.Right=0) then
  2023.      UpdateRect:= PaintClipRect;
  2024.   OldSkipErase:= SkipErase;
  2025.   if Items.Count>0 then SkipErase:= True;
  2026.   if (Message.DC <> 0) then
  2027.   begin
  2028.     if not (csCustomPaint in ControlState) and (ControlCount = 0) then
  2029.       inherited
  2030.     else
  2031.       PaintHandler(Message);
  2032.   end
  2033.   else begin
  2034.     DC := GetDC(0);
  2035.     MemBitmap := CreateCompatibleBitmap(DC, ClientRect.Right, inherited GetClientRect.Bottom);
  2036.     ReleaseDC(0, DC);
  2037.     MemDC := CreateCompatibleDC(0);
  2038.     OldBitmap := SelectObject(MemDC, MemBitmap);
  2039.     try
  2040.       DC := BeginPaint(Handle, PS);
  2041.       Message.DC := MemDC;
  2042.       if not (csCustomPaint in ControlState) and (ControlCount = 0) then
  2043.         inherited
  2044.       else
  2045.         PaintHandler(Message);
  2046.       Message.DC := 0;
  2047. //      BitBlt(DC, 0, 0, ClientRect.Right, ClientRect.Bottom, MemDC, 0, 0, SRCCOPY);
  2048.       BitBlt(DC, UpdateRect.Left, UpdateRect.Top, UpdateRect.Right, UpdateRect.Bottom, MemDC, UpdateRect.Left, UpdateRect.top, SRCCOPY);
  2049.       EndPaint(Handle, PS);
  2050.     finally
  2051.       SelectObject(MemDC, OldBitmap);
  2052.       DeleteDC(MemDC);
  2053.       DeleteObject(MemBitmap);
  2054.     end;
  2055.   end;
  2056.   SkipErase:= OldSkipErase;
  2057. end;
  2058. {
  2059. procedure TfcCustomTreeView.WMPaint(var Message: TWMPaint);
  2060. var OldSkipErase: boolean;
  2061. begin
  2062.    OldSkipErase:= SkipErase;
  2063.    if UsePaintBuffering and (Items.Count>0) then SkipErase:= True;
  2064.    inherited;
  2065.    SkipErase:= OldSkipErase;
  2066. end;
  2067. }
  2068. function TfcTreeNodes.FindNodeInfo(SearchText: string; VisibleOnly: Boolean;
  2069.     StoreDataUsing: TwwStoreData = sdStoreText): TfcTreeNode;
  2070. var Node: TfcTreeNode;
  2071.     tempText: string;
  2072. begin
  2073.   result := nil;
  2074.   Node := GetFirstNode;
  2075.   while Node <> nil do
  2076.   begin
  2077.     case StoreDataUsing of
  2078.        sdStoreText: tempText:= Node.Text;
  2079.        sdStoreData1: tempText:= Node.StringData;
  2080.        sdStoreData2: tempText:= Node.StringData2;
  2081.     end;
  2082.     if UpperCase(tempText) = UpperCase(SearchText) then
  2083.     begin
  2084.       result := Node;
  2085.       Exit;
  2086.     end;
  2087.     if VisibleOnly then Node := Node.GetNextVisible
  2088.     else Node := Node.GetNext;
  2089.   end;
  2090. end;
  2091. function TfcTreeNodes.FindNode(SearchText: string; VisibleOnly: Boolean): TfcTreeNode;
  2092. var Node: TfcTreeNode;
  2093. begin
  2094.   result := nil;
  2095.   Node := GetFirstNode;
  2096.   while Node <> nil do
  2097.   begin
  2098.     if UpperCase(Node.Text) = UpperCase(SearchText) then
  2099.     begin
  2100.       result := Node;
  2101.       Exit;
  2102.     end;
  2103.     if VisibleOnly then Node := Node.GetNextVisible
  2104.     else Node := Node.GetNext;    
  2105.   end;
  2106. end;
  2107. procedure TfcCustomTreeView.WMEraseBkgnd(var Message: TWmEraseBkgnd);
  2108. begin
  2109.   if SkipErase then begin
  2110.      Message.result:= 1;
  2111.      exit;
  2112.   end
  2113.   else inherited;
  2114. end;
  2115. function TfcCustomTreeView.GetMultiSelectItem(Index: integer): TfcTreeNode;
  2116. begin
  2117.    result:= TfcTreeNode(FMultiSelectList[Index]);
  2118. end;
  2119. function TfcCustomTreeView.GetMultiSelectListCount: integer;
  2120. begin
  2121.   result:= FMultiSelectList.Count;
  2122. end;
  2123. procedure TfcCustomTreeView.ClearStateImageIndexes;
  2124. var CurNode: TfcTreeNode;
  2125. begin
  2126.    { Clear image index for all siblings of MultiSelectLevel}
  2127.    CurNode := FTreeNodes.GetFirstNode;
  2128.    while curNode<>Nil do begin
  2129.       if (MultiSelectAttributes.MultiSelectCheckbox=False) then begin
  2130.          if ((curNode.Level = MultiSelectAttributes.MultiSelectLevel) or
  2131.            (MultiSelectAttributes.MultiSelectLevel=-1)) then
  2132.          begin
  2133.             curNode.checked:= False;
  2134.             curNode.StateIndex:= -1;
  2135.          end
  2136.       end
  2137.       else begin
  2138.          if ((curNode.Level <> MultiSelectAttributes.MultiSelectLevel) and
  2139.            (MultiSelectAttributes.MultiSelectLevel<>-1)) then
  2140.          begin
  2141.             curNode.checked:= False;
  2142.             curNode.StateIndex:= -1;
  2143.          end
  2144.       end;
  2145.       curNode:= TfcTreeNode(curNode.GetNext);
  2146.    end;
  2147. end;
  2148. procedure TfcCustomTreeView.KeyDown(var Key: Word; Shift: TShiftState);
  2149. begin
  2150.    inherited;
  2151.    if (Key=32) and (Selected<>nil) and (EditNode=nil) then begin
  2152.       with MultiSelectAttributes do begin
  2153.          if Enabled and (not MultiSelectCheckbox) and
  2154.             (ValidMultiSelectLevel(Selected.Level)) then
  2155.          begin
  2156.             if (ssCtrl in Shift) then
  2157.                Selected.MultiSelected:= not Selected.MultiSelected;
  2158.             exit;
  2159.          end
  2160.       end;
  2161.       if (not ReadOnly) then
  2162.       begin
  2163.          if Selected.IsRadioGroup then begin
  2164.             if not (csDesigning in ComponentState) then Selected.Grayed:= False;
  2165.             Selected.checked:= True;
  2166.          end
  2167.          else begin
  2168.             if (tvo3StateCheckbox in Options) and
  2169.                Selected.checked and (not Selected.Grayed) then Selected.Grayed:= True
  2170.             else begin
  2171.                Selected.checked:= not Selected.checked;
  2172.                if (tvo3StateCheckbox in Options) or
  2173.                   not (csDesigning in ComponentState) then
  2174.                   Selected.Grayed:= False;
  2175.             end
  2176.          end;
  2177.       end;
  2178.       Key:= 0;
  2179.    end;
  2180. end;
  2181. procedure TfcCustomTreeview.KeyPress(var Key: Char);
  2182. begin
  2183.    inherited;
  2184. end;
  2185. procedure TfcTreeNode.Invalidate;
  2186. var r: TRect;
  2187. begin
  2188.   r := DisplayRect(False);
  2189.   InvalidateRect(TreeView.Handle, @r, False);
  2190. end;
  2191. procedure TfcCustomTreeView.MouseLoop(X, Y: Integer);
  2192. var ACursor: TPoint;
  2193.     Msg: TMsg;
  2194.    Function InButton(ACursorPos: TPoint): boolean;
  2195.    var sp: TPoint;
  2196.        MouseRect: TRect;
  2197.    begin
  2198.       sp:= ScreenToClient(ACursorPos);
  2199.       MouseRect:= MouseNode.DisplayRect(False);
  2200.       Result:= (fchtOnButton in GetHitTestInfoAt(sp.x, sp.y)) and { 2/2/99 }
  2201.            (sp.y>=MouseRect.Top) and (sp.y<=MouseRect.Bottom);
  2202.    end;
  2203.    procedure MouseLoop_MouseMove(X, Y: Integer; ACursorPos: TPoint);
  2204.    begin
  2205.      Down:= InButton(ACursorPos);
  2206.      if not Down then
  2207.      begin
  2208.         Down:= InButton(ACursorPos);
  2209.         InvalidateNode(MouseNode)
  2210.      end
  2211.      else
  2212.         InvalidateNode(MouseNode)
  2213.    end;
  2214.    procedure MouseLoop_MouseUp(X, Y: Integer; ACursorPos: TPoint);
  2215.    var IsMouseInControl: Boolean;
  2216.    begin
  2217.      IsMouseInControl:= InButton(ACursorPos);
  2218.      Down:= False;
  2219.      InvalidateNode(MouseNode);
  2220.      if IsMouseInControl then
  2221.      begin
  2222.         if MouseNode.expanded then begin
  2223.            if AutoExpand then Selected:= MouseNode; { 4/13/99 - Otherwise it expands again after auto-collapsing }
  2224.            MouseNode.Collapse(False);
  2225.         end
  2226.         else MouseNode.Expand(False);
  2227.      end
  2228.    end;
  2229. begin
  2230.   Down:= True;
  2231.   InvalidateNode(MouseNode); { Invalidate button icon }
  2232.   SetCapture(Handle);
  2233.   try
  2234.     while GetCapture = Handle do
  2235.     begin
  2236.       GetCursorPos(ACursor);
  2237.       case Integer(GetMessage(Msg, 0, 0, 0)) of
  2238.         -1: Break;
  2239.         0: begin
  2240.           PostQuitMessage(Msg.WParam);
  2241.           Break;
  2242.         end;
  2243.       end;
  2244.       case Msg.Message of
  2245.         WM_LBUTTONDOWN, WM_LBUTTONDBLCLK: ;
  2246.         WM_MOUSEMOVE: MouseLoop_MouseMove(X, Y, ACursor);
  2247.         WM_LBUTTONUP: begin
  2248.           MouseLoop_MouseUp(X, Y, ACursor);
  2249.           TranslateMessage(Msg);   // So OnMouseUp fires
  2250.           DispatchMessage(Msg);
  2251.           if GetCapture = Handle then ReleaseCapture;
  2252.         end;
  2253.         else begin
  2254.           TranslateMessage(Msg);   // So OnMouseUp fires
  2255.           DispatchMessage(Msg);
  2256.         end;
  2257.       end;
  2258.     end;
  2259.   finally
  2260.     if GetCapture = Handle then ReleaseCapture;
  2261.   end;
  2262. end;
  2263. procedure TfcCustomTreeView.SetLineColor(Value: TColor);
  2264. begin
  2265.   if FLineColor <> Value then
  2266.   begin
  2267.     FLineColor:= Value;
  2268.     Invalidate;
  2269.   end;
  2270. end;
  2271. procedure TfcCustomTreeView.SetInactiveFocusColor(Value: TColor);
  2272. begin
  2273.   if FInactiveFocusColor <> Value then
  2274.   begin
  2275.     FInactiveFocusColor:= Value;
  2276.     Invalidate;
  2277.   end;
  2278. end;
  2279. procedure TfcCustomTreeView.CMExit(var Message: TMessage);
  2280. var firstNode: TfcTreeNode;
  2281. begin
  2282.    inherited;
  2283.    { If exactly one node then invalidate }
  2284.    firstNode:= Items.GetFirstNode;
  2285.    if (firstNode<>nil) and (firstNode.GetNextSibling=nil) then InvalidateNode(firstNode);
  2286. end;
  2287. procedure TfcCustomTreeView.MouseMove(Shift: TShiftState; X, Y: Integer);
  2288. var Node: TfcTreeNode;
  2289.     HitTest: TfcHitTests;
  2290.    Function GetEffectiveWidth: integer;
  2291.    begin
  2292.       Result:= ClientRect.Right - ClientRect.Left - 4;
  2293.    end;
  2294.    procedure ProcessToolTips;
  2295.    var SP: TPoint;
  2296.        R, DisplayRect: TRect;
  2297.    begin
  2298.       if ((EditNode<>Node) or (EditNode=Nil)) and
  2299.          (LastHintNode<>Node) and (Node<>nil) then
  2300.       begin
  2301.          FreeHintWindow;
  2302.          HintTimer.Free;
  2303.          HintTimer:= nil;
  2304.          sp:= self.ClientToScreen(Point(0, 0));
  2305.          DisplayRect:= Node.DisplayRect(True);
  2306.          R.Left:= DisplayRect.Left + sp.x - 1;
  2307.          R.Top:= DisplayRect.Top + sp.y - 2;
  2308.          R.Right:= R.Left + Canvas.TextWidth(Node.Text) + 6;
  2309.          R.Bottom:= R.Top + Canvas.TextHeight(Node.Text) + 2;
  2310.          if DisplayRect.Left+Canvas.TextWidth(Node.Text)>
  2311.             GetEffectiveWidth then
  2312.          begin
  2313.             HintTimer:= TTimer.create(self);
  2314.             HintTimer.OnTimer:=HintTimerEvent;
  2315.             HintTimer.Interval:=250;
  2316.             HintTimer.Enabled:= True;
  2317.             LastHintNode:= Node;
  2318.             HintWindow:= CreateHintWindow(Node);
  2319.             with HintWindow do begin
  2320.                if (Node.StringData<>'') and (tvoAutoURL in Options) then
  2321.                begin
  2322.                   Canvas.Font.Color:= GetSysColor(clBlue);
  2323.                   Canvas.Font.Style:=
  2324.                   Canvas.Font.Style + [fsUnderline];
  2325.                end;
  2326.                ActivateHint(R, Node.Text);
  2327.             end
  2328.          end
  2329.          else LastHintNode:= nil;
  2330.       end
  2331.    end;
  2332. begin
  2333.    inherited MouseMove(Shift, X, Y);
  2334.    if Assigned(FOnMouseUp) or (tvoAutoURL in Options) or (tvoToolTips in Options) or
  2335.       fcUseThemes(self) then
  2336.       Node:= GetNodeAt(X, Y) as TfcTreeNode;
  2337.    if Assigned(FOnMouseMove) then
  2338.       FOnMouseMove(self, Node, Shift, X, Y);
  2339.    if fcUseThemes(self) then
  2340.    begin
  2341.      HitTest:= GetHitTestInfoAt(x,y);
  2342.      if (Node<>LastMouseMoveNode) or (LastMouseHitTest<>HitTest) then
  2343.      begin
  2344.         if (Node<>nil) and (Node.checkboxtype<>tvctNone) then
  2345.            Node.invalidate;
  2346.         if (LastMouseMoveNode<>nil) then // 6/30/03
  2347.            LastMouseMoveNode.invalidate;
  2348.      end;
  2349.      LastMouseMoveNode:= Node;
  2350.      LastMouseHitTest:= HitTest;
  2351.    end;
  2352.    if tvoAutoURL in Options then
  2353.    begin
  2354.       if (Node<>Nil) and (Node.StringData<>'') and
  2355.           (fchtOnItem in GetHitTestInfoAt(x,y)) then
  2356.          Cursor:= crHandPoint
  2357.       else
  2358.          Cursor:= crDefault;
  2359.    end;
  2360.    if tvoToolTips in Options then ProcessToolTips;
  2361. end;
  2362. type
  2363.   TfcTreeHintWindow=class(THintWindow)
  2364.   protected
  2365.      procedure Paint; override;
  2366.   public
  2367.      Node: TfcTreeNode;
  2368.   end;
  2369. procedure TfcTreeHintWindow.Paint;
  2370. var
  2371.   R: TRect;
  2372. begin
  2373.   R := ClientRect;
  2374.   Inc(R.Left, 2);
  2375.   Inc(R.Top, 2);
  2376.   if (Node.StringData<>'') and (tvoAutoURL in Node.TreeView.Options) then
  2377.   begin
  2378.       Canvas.Font.Color:= clBlue;
  2379.       Canvas.Font.Style:=
  2380.               Canvas.Font.Style + [fsUnderline];
  2381.   end
  2382.   else begin
  2383.       Canvas.Font.Color := clInfoText;
  2384.   end;
  2385.   SetBkMode(Canvas.Handle, TRANSPARENT);
  2386.   DrawText(Canvas.Handle, PChar(Caption), -1, R, DT_LEFT or DT_NOPREFIX or
  2387.     DT_WORDBREAK);
  2388. end;
  2389. Function TfcCustomTreeView.CreateHintWindow(Node: TfcTreeNode): THintWindow;
  2390. begin
  2391.    HintWindow:= TfcTreeHintWindow.create(self);
  2392.    TfcTreeHintWindow(HintWindow).Node:= Node;
  2393.    HintWindow.Color:= GetSysColor(COLOR_INFOBK);
  2394.    HintWindow.Canvas.Brush.Color:= GetSysColor(COLOR_INFOBK);
  2395.    HintWindow.Canvas.Font:= self.Font;
  2396.    HintWindow.Canvas.Font.Color:= GetSysColor(COLOR_INFOTEXT);
  2397.    HintWindow.Canvas.Pen.Color:= clBlack;
  2398.    result:= HintWindow;
  2399. end;
  2400. procedure TfcCustomTreeView.FreeHintWindow;
  2401. begin
  2402.    HintTimerCount:= 0;
  2403.    HintWindow.Free;
  2404.    HintWindow:= nil;
  2405.    if HintTimer<>nil then
  2406.       HintTimer.enabled:= False;
  2407. end;
  2408. procedure TfcCustomTreeView.HintTimerEvent(Sender: TObject);
  2409. var
  2410.     sp, cp: TPoint;
  2411.     OutsideClient: boolean;
  2412. begin
  2413.    if not (HintWindow<>nil) then exit;
  2414.    GetCursorPos(cp);
  2415.    sp:= self.ClientToScreen(Point(0, 0));
  2416.    if (cp.x<sp.x) or (cp.x>sp.x+ClientRect.Right-ClientRect.Left) or
  2417.       (cp.y<sp.y) or (cp.y>sp.y+ClientRect.Bottom-ClientRect.Top) then
  2418.    begin
  2419.       OutsideClient:= True;
  2420.    end
  2421.    else OutsideClient:= False;
  2422.    { Process Hint Timer clean-up}
  2423.    if OutsideClient then
  2424.    begin
  2425.       FreeHintWindow;
  2426.       LastHintNode:= nil;
  2427.    end
  2428.    else begin
  2429.       inc(HintTimerCount);
  2430.       if HintTimerCount>16 then
  2431.       begin
  2432.          FreeHintWindow;
  2433.       end
  2434.    end
  2435. end;
  2436. procedure TfcCustomTreeView.CMDesignHitTest(var Message: TCMDesignHitTest);
  2437. //var HitTest: TfcHitTests;
  2438. begin
  2439. {   HitTest:= GetHitTestInfoAt(Message.xPos, Message.yPos);
  2440.    if fchtToRight in HitTest then begin
  2441.       Message.Result:= 1;
  2442.    end
  2443.    else }inherited;
  2444. end;
  2445. procedure TfcCustomTreeView.InvalidateNoErase;
  2446. begin
  2447.    InvalidateRect(Handle, nil, False);
  2448. end;
  2449. procedure TfcCustomTreeView.WMNCHitTest(var Message: TWMNCHitTest);
  2450. begin
  2451.   DefaultHandler(Message);
  2452. end;
  2453. function TfcCustomTreeView.GetPaintCanvas: TfcCanvas;
  2454. begin
  2455. //   if UsePaintBuffering then
  2456. //      result:= FPaintCanvas
  2457. //   else
  2458.      result:= FCanvas;
  2459. end;
  2460. end.