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

Delphi控件源码

开发平台:

Delphi

  1. unit fcdbtreeview;
  2. {
  3. //
  4. // Components : TfcDBTreeView
  5. //
  6. // Copyright (c) 1999 by Woll2Woll Software
  7. //
  8. // 5/25/99 - RSW - Fix BorderStyle=bsNone bug where horizontal scrollbar and
  9. //           buttons drawn in wrong position
  10. // 6/22/99 - RSW - Support way to disable Up/Down tree buttons
  11. // 6/22/99 - RSW - Add Select All method
  12. // 7/4/99 -  Fire OnKeyDown event
  13. // 7/26/99 - RSW - Support Builder notation for datasources
  14. // 9/24/99 - RSW - Fix bug with GetHitTestInfoAtXY for detecting Image hit and activeNode hit
  15. // 11/17/99 - RSW - Added support for Form.Print
  16. // 11/17/99 - PYW - Don't HotTrack if this form is not active.
  17. // 1/17/2000 - If insert state then allow changing to this dataset
  18. // 1/20/2000 - Support dtvoShowVertScrollbar options
  19. // 2/8/99 - Move try block before test for firstbookmark to fix potential leak
  20. // 2/14/2000 - Unselect all before destroying
  21. // 3/21/00 - Check for active in UpdateScrollBar to prevent exception when
  22. //           active goes to false
  23. // 5/20/00 - When freeing bookmarks, don't reference dataset in case its already been destroyed
  24. // 06/17/2000 - PYW - Correct painting bug when dtvoShowRoot is not in options and paintbutton is called on the root node
  25. // 10/19/2001 - PYW - Added dtvoFlatCheckboxes since it was in documentation.
  26. // 7/10/02 - Call FreeLastActiveBookmark when assigning datasources
  27. }
  28. interface
  29. {$i fcIfDef.pas}
  30. uses
  31.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  32.   ComCtrls, db, commctrl, stdctrls, extctrls, fccommon, fccanvas,
  33.   fcdbcommon, buttons, fcscrollbar, fcshapebtn, fcbutton, fcimager, fcchangelink,
  34.   fctreeheader, ImgList;
  35. type
  36.   TfcDBCustomTreeView = class;
  37.   TfcTreeHitTest = (fchtdOnButton, fchtdOnStateIcon, fchtdOnImageIcon, fchtdOnText,
  38.                     fchtdOnActiveNode);
  39.   TfcTreeHitTests = set of TfcTreeHitTest;
  40.   TfcTreeDataLink = class(TDataLink)
  41.   private
  42.     FTree: TfcDBCustomTreeView;
  43.   protected
  44.     procedure DataSetChanged; override;
  45.     procedure DataSetScrolled(Distance: Integer); override;
  46.     procedure ActiveChanged; override;
  47.     procedure RecordChanged(Field: TField); override;
  48.   public
  49.     constructor Create(ATree: TfcDBCustomTreeView);
  50.     destructor Destroy; override;
  51.   end;
  52.   TfcMultiSelectItem = class
  53.      Bookmark: TBookmark;
  54.      DataSet: TDataSet;
  55.   end;
  56.   TfcDBMultiSelectAttributes = class(TPersistent)
  57.   private
  58.      FEnabled: Boolean;
  59.      FAutoUnselect: boolean;
  60.      FMultiSelectLevel: integer;
  61.      FMultiSelectCheckbox: boolean;
  62.      TreeView: TfcDBCustomTreeView;
  63.      procedure SetEnabled(val: boolean);
  64.      procedure SetMultiSelectLevel(val: integer);
  65.      procedure SetMultiSelectCheckBox(val: boolean);
  66.   public
  67.      constructor Create(Owner: TComponent);
  68.      procedure Assign(Source: TPersistent); override;
  69.   published
  70.      property AutoUnselect : boolean read FAutoUnselect write FAutoUnselect default False;
  71.      property Enabled: boolean read FEnabled write SetEnabled default False;
  72.      property MultiSelectLevel: integer read FMultiSelectLevel write SetMultiSelectLevel default 0;
  73.      property MultiSelectCheckbox: boolean read FMultiSelectCheckbox write SetMultiSelectCheckbox default True;
  74.   end;
  75.   TfcDBTreeViewOption = (
  76.      dtvoKeysScrollLevelOnly,
  77.      dtvoAutoExpandOnDSScroll,
  78.      dtvoExpandButtons3D,
  79.      dtvoHideSelection,
  80.      dtvoRowSelect, dtvoShowNodeHint, dtvoShowButtons,
  81.      dtvoShowLines, dtvoShowRoot, dtvoShowHorzScrollBar,
  82.      dtvoShowVertScrollBar, dtvoHotTracking, dtvoFlatCheckboxes);
  83.   TfcDBTreeViewOptions = set of TfcDBTreeViewOption;
  84.   TfcDBTreeNode = class
  85.     protected
  86.      HasPrevSibling: boolean;
  87.      HasNextSibling: boolean;
  88.     public
  89.      ActiveRecord: integer;
  90.      DataLink: TfcTreeDataLink;
  91.      Text: string;
  92.      Level: integer;
  93.      DataSet: TDataSet;
  94.      Field: TField;
  95.      Expanded: boolean;
  96.      HasChildren: boolean;
  97.      Parent: TfcDBTreeNode;
  98.      ImageIndex: integer;
  99.      StateIndex: integer;
  100.      Selected: boolean;
  101.      Hot: boolean;
  102.      MultiSelected: boolean;
  103.      function GetFieldValue(FieldName: string): Variant;
  104.   end;
  105.   TfcDBTreeEvent = procedure(TreeView: TfcDBCustomTreeView;
  106.       Node: TfcDBTreeNode) of object;
  107.   TfcDBTreeSectionEvent = procedure(TreeView: TfcDBCustomTreeView;
  108.       Node: TfcDBTreeNode; Section: TfcTreeHeaderSection;
  109.       var DisplayText: string) of object;
  110.   TfcDBTreeMouseEvent = procedure(TreeView: TfcDBCustomTreeView;
  111.       Node: TfcDBTreeNode;
  112.       Button: TMouseButton; Shift: TShiftState; X, Y: Integer) of object;
  113.   TfcDBTreeMouseMoveEvent = procedure(TreeView: TfcDBCustomTreeView;
  114.       Node: TfcDBTreeNode;
  115.       Shift: TShiftState; X, Y: Integer)of object;
  116.   TfcDBTreeDrawTextEvent = procedure (TreeView: TfcDBCustomTreeview;
  117.     Node: TfcDBTreeNode; ARect: TRect;
  118.     var DefaultDrawing: boolean) of object;
  119.   TfcDBTreeDrawSectionEvent = procedure (TreeView: TfcDBCustomTreeview;
  120.     Node: TfcDBTreeNode; Section: TfcTreeHeaderSection;
  121.     ARect: TRect;
  122.     s: String;
  123.     var DefaultDrawing: boolean) of object;
  124.   TfcDBCustomTreeView = class(TWinControl)
  125.   private
  126.     FOptions: TfcDBTreeViewOptions;
  127.     FDisplayFields: TStrings;
  128.     FBorderStyle: TBorderStyle;
  129.     FOnCalcNodeAttributes: TfcDBTreeEvent;
  130.     FOnCalcSectionAttributes: TfcDBTreeSectionEvent;
  131.     FOnDrawSection: TfcDBTreeDrawSectionEvent;
  132.     FOnChange: TfcDBTreeEvent;
  133.     FOnUserExpand: TfcDBTreeEvent;
  134.     FOnUserCollapse: TfcDBTreeEvent;
  135.     FMultiSelectAttributes: TfcDBMultiSelectAttributes;
  136.     FOnMouseDown, FOnMouseUp, FOnDblClick: TfcDBTreeMouseEvent;
  137.     FOnMouseMove: TfcDBTreeMouseMoveEvent;
  138.     FLevelIndent : integer;
  139.     FDataSourcesMiddle: string;
  140.     FImager: TfcCustomImager;
  141.     FMultiSelectList: TList;
  142.     FImages: TCustomImageList;
  143.     FStateImages: TCustomImageList;
  144.     FLineColor: TColor;
  145.     FInactiveFocusColor: TColor;
  146.     FScrollWithinLevel: boolean;
  147.     FDisableThemes: boolean;
  148.     FDataLinks: TList;
  149.     FCanvas: TControlCanvas;
  150.     FPaintCanvas: TfcCanvas;
  151.     FPaintBitmap: TBitmap;
  152. //    InChange, InFetchData, InScroll: boolean;
  153. //    SkipSetTop: boolean;
  154.     SkipErase, SkipReload: boolean;
  155.     FFirstDataLink, FLastDataLink: TfcTreeDataLink;
  156.     FActiveDataSet: TDataSet;
  157.     FLastVisibleDataset: TDataSet;
  158.     ActiveDataSetChanged: boolean;
  159.     ActiveNodeIndex: integer;
  160.     HintTimer: TTimer;
  161.     HintTimerCount: integer;
  162.     LastHintRow: integer;
  163.     Nodes: TList;
  164.     RowHeight : integer;
  165.     FixedOffset : integer;
  166.     CacheSize : integer;
  167.     FActiveNode: TfcDBTreeNode;
  168.     MaxTextWidth: integer;
  169.     ResetScroll: boolean;
  170.     Down: boolean;     { Used by MouseLoop}
  171.     MouseRow: integer; { Used by MouseLoop}
  172.     PaintingRow: integer;
  173.     FOnDrawText: TfcDBTreeDrawTextEvent;
  174.     SaveCursor: TCursor;
  175.     CheckMaxWidth: boolean; { Set to True to force wmpaint to check horzscrollbar }
  176.     CheckMaxWidthGrow: boolean; { Set to True to force wmpaint to check horzscrollbar }
  177.     InPaint: boolean;
  178.     InComputeHorzWidthOnly : boolean;
  179.     FChangeLink: TfcChangeLink;
  180.     NodesCleared: boolean;
  181.     HaveBadLink: boolean; { True if DataSources property is referencing external form that has not been created yet }
  182.     {$ifdef fcDelphi4Up}
  183.     FHideUpDownButtons: boolean;
  184.     FHeader: TfcTreeHeader;
  185.     procedure SetHideUpDownButtons(val: boolean);
  186.     {$endif}
  187.     function GetDataSource: TDataSource;
  188.     procedure SetDataSource(Value: TDataSource);
  189.     function GetLastDataSource: TDataSource;
  190.     procedure SetLastDataSource(Value: TDataSource);
  191.     procedure SetDataSources(Value: String);
  192.     procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
  193.     procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  194.     procedure CMExit(var Message: TMessage); message CM_EXIT;
  195.     procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  196.     procedure WMEraseBkgnd(var Message: TWmEraseBkgnd); message WM_ERASEBkgnd;
  197. //    procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  198. //    procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL;
  199.     procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  200.     procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
  201. //    procedure CMDesignHitTest(var Message: TCMDesignHitTest); message CM_DESIGNHITTEST;
  202.     procedure WMSize(var Message: TWMSize); message WM_SIZE;
  203.     procedure TreeDownClick(Sender : TObject);
  204.     procedure TreeUpClick(Sender : TObject);
  205.     Function GetParentDataLink(Dataset: TDataset): TfcTreeDataLink;
  206.     Function GetChildDataLink(Dataset: TDataset): TfcTreeDataLink;
  207.     Procedure MouseToRow(X, Y: integer; var Row: integer);
  208.     function RowToNode(Row: integer; var Node: TfcDBTreeNode): boolean;
  209. //    function NodeToIndex(Node: TfcDBTreeNode): integer;
  210.     function NodeToRow(Node: TfcDBTreeNode; var Row: integer): boolean;
  211.     procedure SetBorderStyle(Value: TBorderStyle);
  212.     Function GetCenterPoint(ARect: TRect): TPoint;
  213.     procedure ResetStartOffsets(ActiveDataSet: TDataSet);
  214.     Function GetStartOffset: integer;
  215.     Procedure SetStartOffset(ActiveDataSet: TDataSet; val: integer);
  216.     procedure SetImages(Value: TCustomImageList);
  217.     procedure SetStateImages(Value: TCustomImageList);
  218.     function UseStateImages(Node: TfcDBTreeNode): Boolean;
  219.     function GetMultiSelectItem(Index: integer): TfcMultiSelectItem;
  220.     procedure HintTimerEvent(Sender: TObject);
  221.     function GetMultiSelectListCount: integer;
  222.     Function GetStateImageWidth: integer;
  223.     procedure ScrollRight;
  224.     procedure ScrollLeft;
  225.     procedure SetLineColor(Value: TColor);
  226.     procedure SetInactiveFocusColor(Value: TColor);
  227.     procedure SetOptions(Value: TfcDBTreeViewOptions);
  228.     Function GetStartX(Node: TfcDBTreeNode): integer;
  229.     procedure SetDisplayFields(Value: TStrings);
  230.     procedure UpdateScrollBarPosition;
  231.     procedure SetLevelIndent(val: integer);
  232.     procedure SetImager(Value: TfcCustomImager);
  233.     procedure ImagerChange(Sender: TObject);
  234.     procedure SetHeader(Value: TFcTreeHeader);
  235.   protected
  236.     HintWindow: THintWindow;
  237.     LastActiveBookmark, FirstBookmark: TBookmark;
  238.     ScrollSize: integer;
  239.     HotTrackRow: integer;
  240.     FMouseInControl : boolean;
  241.     SkipFreeNodes: boolean;
  242.     OldNodes: TList;
  243.     HorzScrollBar, VertScrollBar: TfcScrollBar;
  244.     UpTreeButton, DownTreeButton: TfcShapeBtn;
  245.     StartOffsets: Array[0..50] of integer;
  246.     RootDataSetBookmark: TBookmark;
  247.     {$ifdef fcDelphi4Up}
  248.     function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
  249.     function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
  250.     {$endif}
  251.     Function GetNodeText(DisplayFieldLine: string;
  252.        DataSet: TDataSet;
  253.        var Field: TField): string;
  254.     function ComputeHeaderWidth: integer; virtual;
  255.     Function IsRootDataSetMoved: boolean; virtual;
  256.     procedure DrawColumnText(
  257.           Node: TfcDBTreeNode; ARect: TRect); virtual;
  258.     procedure CreateWnd; override;
  259.     procedure UpdateScrollBar; virtual;
  260.     procedure CreateParams(var Params: TCreateParams); override;
  261.     procedure PaintButton(Node: TfcDBTreeNode;
  262.               pt: TPoint; Size: integer; Expanded: Boolean); virtual;
  263.     procedure PaintLines(Node: TfcDBTreeNode); virtual;
  264.     procedure PaintImage(Node: TfcDBTreeNode); virtual;
  265.     procedure DataChanged(DataSet: TDataSet); virtual;
  266.     procedure Scroll(DataSet: TDataSet; Distance: Integer); virtual;
  267.     procedure LinkActive(DataSet: TDataSet; Value: Boolean); virtual;
  268.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  269.     procedure DoCalcNodeAttributes(Node: TfcDBTreeNode); virtual;
  270.     procedure DoCalcSectionAttributes(Node: TfcDBTreeNode;
  271.                 Section: TfcTreeHeaderSection;
  272.                 var DisplayText: string); virtual;
  273.     procedure DoDrawSection(Node: TfcDBTreeNode;
  274.                 Section: TfcTreeHeaderSection;
  275.                 ARect: TRect;
  276.                 s: String;
  277.                 var DoDefault: boolean); virtual;
  278.     Function InMasterChanging(DataSet: TDataSet): boolean; virtual;
  279.     procedure RefreshDataLinks(FirstDS, LastDS: TDataSource); virtual;
  280.     procedure Notification(AComponent: TComponent;
  281.          Operation: TOperation); override;
  282.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  283.       X, Y: Integer); override;
  284.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
  285.       X, Y: Integer); override;
  286.     procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  287.     Function LevelRect(Node: TfcDBTreeNode): TRect;
  288.     Function TextRect(Node: TfcDBTreeNode; Row: integer): TRect;
  289.     Function MultiSelectCheckboxNeeded(Node: TfcDBTreeNode): boolean;
  290.     function ValidMultiSelectLevel(ALevel: Integer): Boolean;
  291.     Function FindCurrentMultiSelectIndex(DataSet: TDataSet): integer; virtual;
  292.     procedure Loaded; override;
  293.     procedure FreeHintWindow; virtual;
  294.     Function CreateHintWindow: THintWindow; virtual;
  295.     procedure Change(FSelected: TfcDBTreeNode); virtual;
  296.     Procedure SaveIfFirstRecordBookmark(DataSet: TDataSet);
  297.     Procedure FreeFirstBookmark;
  298.     Function HaveValidDataLinks: boolean;
  299.     function IsChildDataSetOfActive(DataSet: TDataSet): boolean;
  300.     function IsMasterDataSetOfActive(DataSet: TDataSet): boolean;
  301.     Procedure ToggleMultiSelection(
  302.        RequireControlKey: boolean; Shift: TShiftState);
  303.     procedure MouseLoop(X, Y: Integer); virtual;
  304.     function UpdateDataLinkToActive(Node: TfcDBTreeNode): boolean;
  305.     procedure PriorRow(WithinLevel: boolean); virtual;
  306.     procedure NextRow(WithinLevel: boolean); virtual;
  307.     procedure PriorPage(WithinLevel: boolean); virtual;
  308.     procedure NextPage(WithinLevel: boolean); virtual;
  309.     function GetClientRect: TRect; override;
  310.     procedure VScroll(ScrollCode: integer; Position: integer); virtual;
  311.     procedure HScroll(ScrollCode: integer; Position: integer); virtual;
  312.     function CreateUpTreeButton: TfcShapeBtn; virtual;
  313.     function CreateDownTreeButton: TfcShapeBtn; virtual;
  314.     procedure DoDrawText(TreeView: TfcDBCustomTreeView;
  315.          Node: TfcDBTreeNode; ARect: TRect;
  316.          var DefaultDrawing: boolean); virtual;
  317.     procedure WndProc(var Message: TMessage); override;
  318.     procedure FreeOldNodes;
  319.     procedure DoUserExpand(Node: TfcDBTreeNode); virtual;
  320.     procedure DoUserCollapse(Node: TfcDBTreeNode); virtual;
  321.     procedure SetActiveDataSet(DataSet: TDataSet); virtual;
  322.     procedure SetLastVisibleDataSet(DataSet: TDataSet); virtual;
  323.     Function GetDataLink(Dataset: TDataset): TfcTreeDataLink;
  324.     Function GetDataLinkIndex(Dataset: TDataset): integer;
  325.   public
  326.     Patch: Variant;
  327.     procedure LayoutChanged; virtual;
  328.     Procedure FreeLastActiveBookmark;
  329.     procedure FreeRootBookmark;  // Move to public in case they change index and wish to clear old bookmark
  330.     constructor Create(AOwner: TComponent); override;
  331.     destructor Destroy; override;
  332.     procedure MoveTo(Node: TfcDBTreeNode);
  333.     function GetHitTestInfoAt(X, Y: Integer): TfcTreeHitTests;
  334.     procedure UnselectAll; virtual;
  335.     {$ifdef fcDelphi4Up}
  336.     procedure SelectAll(ADataSet: TDataSet); virtual;
  337.     {$endif}
  338.     procedure SelectRecord; virtual;
  339.     procedure UnselectRecord; virtual;
  340.     Function IsSelectedRecord: boolean;
  341.     procedure InvalidateNode(Node: TfcDBTreeNode);
  342.     procedure InvalidateRow(Row: integer);
  343.     procedure InvalidateClient; virtual;
  344.     procedure Expand(Node: TfcDBTreeNode); virtual;
  345.     procedure Collapse(Node: TfcDBTreeNode); virtual;
  346.     procedure MakeActiveDataSet(DataSet: TDataSet; Collapse: boolean);
  347.     Function GetNodeAt(X,Y: integer): TfcDBTreeNode;
  348.     procedure SortMultiSelectList;
  349.     property ActiveNode: TfcDBTreeNode read FActiveNode;
  350.     property Canvas : TfcCanvas read FPaintCanvas;
  351.     property MultiSelectList[Index: Integer]: TfcMultiSelectItem read GetMultiSelectItem;
  352.     property MultiSelectListCount : integer read GetMultiSelectListCount;
  353.     property ActiveDataSet : TDataSet read FActiveDataSet write SetActiveDataSet;
  354.     property LastVisibleDataSet: TDataSet read FLastVisibleDataSet write SetLastVisibleDataSet;
  355. //  published
  356.     property LevelIndent : integer read FLevelIndent write SetLevelIndent;
  357.     property LineColor: TColor read FLineColor write SetLineColor default clBtnShadow;
  358.     property InactiveFocusColor: TColor read FInactiveFocusColor write SetInactiveFocusColor default clBtnFace;
  359.     property ParentColor default False;
  360.     property BorderStyle : TBorderStyle read FBorderStyle write SetBorderStyle;
  361.     property DataSourceFirst: TDataSource read GetDataSource write SetDataSource;
  362.     property DataSourceLast: TDataSource read GetLastDataSource write SetLastDataSource;
  363.     property DataSources: String read FDataSourcesMiddle write SetDataSources;
  364.     property Options: TfcDBTreeViewOptions read FOptions write SetOptions default
  365.           [dtvoAutoExpandOnDSScroll,
  366.            dtvoShowButtons, dtvoShowNodeHint, dtvoShowLines, dtvoShowRoot, dtvoShowHorzScrollBar,
  367.            dtvoShowVertScrollBar];
  368.     property DisplayFields: TStrings read FDisplayFields write SetDisplayFields;
  369.     property Images: TCustomImageList read FImages write SetImages;
  370.     property Imager: TfcCustomImager read FImager write SetImager;
  371.     property StateImages: TCustomImageList read FStateImages write SetStateImages;
  372.     property MultiSelectAttributes: TfcDBMultiSelectAttributes
  373.         read FMultiSelectAttributes write FMultiSelectAttributes;
  374.     property OnCalcNodeAttributes: TfcDBTreeEvent read FOnCalcNodeAttributes
  375.              write FOnCalcNodeAttributes;
  376.     property OnCalcSectionAttributes: TfcDBTreeSectionEvent read FOnCalcSectionAttributes
  377.              write FOnCalcSectionAttributes;
  378.     property OnDrawSection: TfcDBTreeDrawSectionEvent read FOnDrawSection
  379.              write FOnDrawSection;
  380.     property OnChange: TfcDBTreeEvent read FOnChange write FOnChange;
  381.     property OnUserCollapse: TfcDBTreeEvent read FOnUserCollapse write FOnUserCollapse;
  382.     property OnUserExpand: TfcDBTreeEvent read FOnUserExpand write FOnUserExpand;
  383.     property OnDblClick: TfcDBTreeMouseEvent read FOnDblClick write FOnDblClick;
  384.     property OnMouseDown: TfcDBTreeMouseEvent read FOnMouseDown write FOnMouseDown;
  385.     property OnMouseMove: TfcDBTreeMouseMoveEvent read FOnMouseMove write FOnMouseMove;
  386.     property OnMouseUp: TfcDBTreeMouseEvent read FOnMouseUp write FOnMouseUp;
  387.     property OnDrawText: TfcDBTreeDrawTextEvent read FOnDrawText write FOnDrawText;
  388.     property Header: TfcTreeHeader read FHeader write SetHeader;
  389.     property HideUpDownButtons: boolean read FHideUpDownButtons write SetHideUpDownButtons default False;
  390.     property DisableThemes : boolean read FDisableThemes write FDisableThemes default False;
  391.   end;
  392.   TfcDBTreeView = class(TfcDBCustomTreeView)
  393.   published
  394.     property DisableThemes;
  395.     property Align;
  396.     property BorderStyle;
  397.     property Color;
  398.     property DragCursor;
  399.     property DragMode;
  400.     property Enabled;
  401.     property Font;
  402.     property ParentColor default False;
  403.     property ParentFont;
  404.     property ParentShowHint;
  405.     property TabStop default True;
  406.     property TabOrder;
  407.     property Visible;
  408.     property Header;
  409.     {$ifdef fcDelphi4Up}
  410.     property Anchors;
  411.     property BiDiMode;
  412.     property Constraints;
  413.     property ParentBiDiMode;
  414.     {$endif}
  415.     property DataSourceFirst;
  416.     property DataSourceLast;
  417.     property DataSources;
  418.     property DisplayFields;
  419. //    {$ifdef fcDelphi4Up}
  420. //    property HideUpDownButtons;
  421. //    {$endif}
  422.     property Imager;
  423.     property InactiveFocusColor;
  424.     property LineColor;
  425.     property Options;
  426.     {$ifdef fcDelphi4Up}
  427.     property OnStartDock;
  428.     property OnEndDock;
  429.     {$endif}
  430.     property LevelIndent;
  431.     property Images;
  432.     property StateImages;
  433.     property MultiSelectAttributes;
  434.     property PopupMenu;
  435.     property HideUpDownButtons;
  436.     property OnCalcNodeAttributes;
  437.     property OnCalcSectionAttributes;
  438.     property OnDrawSection;
  439.     property OnChange;
  440.     property OnUserCollapse;
  441.     property OnUserExpand;
  442.     property OnEnter;
  443.     property OnExit;
  444.     property OnKeyDown;
  445.     property OnKeyUp;
  446.     property OnKeyPress;
  447.     property OnDblClick;
  448.     property OnMouseDown;
  449.     property OnMouseMove;
  450.     property OnMouseUp;
  451.     property OnDragDrop;
  452.     property OnDragOver;
  453.     property OnEndDrag;
  454.     property OnStartDrag;
  455.     property OnDrawText;
  456.   end;
  457. implementation
  458. {$ifdef fcdelphi6}
  459. uses
  460.  {$ifdef ThemeManager}
  461.    variants, thememgr, themesrv, uxtheme;
  462.  {$else}
  463.    variants;
  464.  {$endif}
  465. {$endif}
  466. {$ifdef fcdelphi7}
  467. uses
  468. variants, themes;
  469. {$endif}
  470. const
  471.   CmpLess = -1;
  472.   CmpEql = 0;
  473.   CmpGtr = 1;
  474.   CmpKeyEql = 2;
  475. type
  476.   CMPBkMkRslt = Integer; { To resolve CmpBkmkRslt type }
  477. constructor TfcTreeDataLink.Create(ATree: TfcDBCustomTreeView);
  478. begin
  479.   inherited Create;
  480.   FTree := ATree;
  481. end;
  482. procedure TfcTreeDataLink.RecordChanged(Field: TField);
  483. begin
  484.   if (Field<>Nil) and (Dataset.State in [dsEdit, dsInsert]) then
  485.   begin
  486.     FTree.invalidateClient;
  487.   end;
  488. end;
  489. procedure TfcDBCustomTreeView.CreateParams(var Params: TCreateParams);
  490. begin
  491.   inherited CreateParams(Params);
  492.   with Params do
  493.   begin
  494.     if (FBorderStyle = bsSingle) then
  495.     begin
  496.        Style := Style and not WS_BORDER;
  497.        ExStyle := ExStyle or WS_EX_CLIENTEDGE;
  498.     end;
  499.   end;
  500. end;
  501. destructor TfcTreeDataLink.Destroy;
  502. begin
  503.   inherited Destroy;
  504. end;
  505. procedure TfcTreeDataLink.DataSetChanged;
  506. begin
  507.   FTree.DataChanged(self.dataset);
  508. end;
  509. procedure TfcTreeDataLink.DataSetScrolled(Distance: Integer);
  510. begin
  511.   FTree.Scroll(self.dataset, Distance);
  512. end;
  513. type
  514. TfcTreeVertScrollBar = class(TfcScrollBar)
  515.   protected
  516.     procedure Scroll(ScrollCode: integer; Position: integer); override;
  517. end;
  518. TfcTreeHorzScrollBar = class(TfcScrollBar)
  519.   protected
  520.     procedure Scroll(ScrollCode: integer; Position: integer); override;
  521. end;
  522. constructor TfcDBCustomTreeView.Create(AOwner: TComponent);
  523. var i: Integer;
  524. begin
  525.   inherited;
  526.   CacheSize:= 1;
  527.   FDataLinks:= TList.create;
  528.   FFirstDataLink:= TfcTreeDataLink.create(self);
  529.   FLastDataLink:= TfcTreeDataLink.create(self);
  530.   FFirstDataLink.BufferCount := CacheSize;
  531.   FLastDataLink.BufferCount := CacheSize;
  532.   Nodes:= TList.create;
  533.   OldNodes:= TList.create;
  534.   FPaintBitmap:= TBitmap.create;
  535.   FPaintCanvas:= TfcCanvas(FPaintBitmap.Canvas);
  536.   FOptions:=
  537.      [dtvoAutoExpandOnDSScroll, dtvoShowButtons, dtvoShowNodeHint,
  538.       dtvoShowLines, dtvoShowRoot, dtvoShowHorzScrollBar,
  539.       dtvoShowVertScrollBar];
  540.   FBorderStyle:= bsSingle;
  541.   for i:= 0 to 50 do StartOffsets[i]:= 0;
  542.   LevelIndent := 21;
  543.   LevelIndent:= 19; { Seems to be more consistent with TTreeView. Find out when 21
  544.                       is better }
  545.   FixedOffset := 1;
  546.   RowHeight:= 16;
  547.   HintTimer:= TTimer.create(self);
  548.   FMultiSelectAttributes:= TfcDBMultiSelectAttributes.create(self);
  549.   FMultiSelectList:= TList.create;
  550.   HotTrackRow:= -1;
  551.   Width := 121;
  552.   Height := 97;
  553.   Color:= clWindow;
  554.   ParentColor:= False;
  555.   FLineColor:= clBtnShadow;
  556.   FInactiveFocusColor:= clBtnFace;
  557.   FDisplayFields:= TStringList.create;
  558.   VertScrollBar:= TfcTreeVertScrollBar.create(self);
  559.   VertScrollBar.Kind:= sbVertical;
  560.   VertScrollBar.Width:= GetSystemMetrics(SM_CXVSCROLL);
  561.   VertScrollBar.parent:= self;
  562.   HorzScrollBar:= TfcTreeHorzScrollBar.create(self);
  563.   HorzScrollBar.Kind:= sbHorizontal;
  564.   HorzScrollBar.Height:= GetSystemMetrics(SM_CXVSCROLL);
  565.   HorzScrollBar.Max:= 5;
  566.   HorzScrollBar.PageSize:= 10;
  567.   HorzScrollBar.visible:= false;
  568.   HorzScrollBar.parent:= self;
  569.   HorzScrollBar.SmallChange:= 10;
  570.   HorzScrollBar.ContinuousDrag:= True;
  571.   UpTreeButton:= CreateUpTreeButton;
  572.   DownTreeButton:= CreateDownTreeButton;
  573.   FScrollWithinLevel:= True;
  574.   FChangeLink := TfcChangeLink.Create;
  575.   FChangeLink.OnChange := ImagerChange;
  576.   TabStop:= True;
  577. end;
  578. destructor TfcDBCustomTreeView.Destroy;
  579. var i: integer;
  580. begin
  581.    FChangeLink.Free;
  582.    VertScrollBar.Free;
  583.    VertScrollBar:= nil;
  584.    for i:= 0 to Nodes.count-1 do TfcDBTreeNode(Nodes[i]).Free;
  585.    Nodes.Free;
  586.    FreeOldNodes;
  587.    OldNodes.Free;
  588.    for i:= 0 to FDataLinks.count-1 do begin
  589.       if FDataLinks[i]=FFirstDataLink then continue;
  590.       if FDataLinks[i]=FLastDataLink then continue;
  591.       TfcTreeDataLink(FDataLinks[i]).Free;
  592.    end;
  593.    UnselectAll; { 2/14/2000 }
  594.    FMultiSelectList.Free;
  595.    FMultiSelectAttributes.Free;
  596.    FFirstDataLink.Free;
  597.    FLastDataLink.Free;
  598.    FDataLinks.Free;
  599.    HintTimer.Free;
  600.    FreeFirstBookmark;
  601.    FreeLastActiveBookmark;
  602.    FDisplayFields.Free;
  603.    FPaintBitmap.Free;
  604.    FCanvas.Free;
  605.    FreeRootBookmark;
  606.    inherited Destroy;
  607. end;
  608. procedure TfcDBCustomTreeView.FreeRootBookmark;
  609. begin
  610.   {$ifdef fcdelphi4up}
  611.   if RootDataSetBookmark<>Nil then FreeMem(RootDataSetBookmark);
  612.   RootDataSetBookmark:= nil;
  613.   {$else}
  614.   if RootDataSetBookmark<>Nil then StrDispose(RootDataSetBookmark);
  615.   RootDataSetBookmark:= nil;
  616.   {$endif}
  617. end;
  618. procedure TfcTreeDataLink.ActiveChanged;
  619. begin
  620.   if (DataSource=FTree.DataSourceFirst) or (DataSource=FTree.DataSourceLast) then
  621.      FTree.RefreshDataLinks(FTree.DataSourceFirst, FTree.DataSourceLast);
  622.   FTree.LinkActive(Dataset, Active);
  623. end;
  624. function TfcDBCustomTreeView.GetDataSource: TDataSource;
  625. begin
  626.     Result := FFirstDataLink.DataSource
  627. end;
  628. function TfcDBCustomTreeView.GetLastDataSource: TDataSource;
  629. begin
  630.     Result := FLastDataLink.DataSource
  631. end;
  632. {type
  633.   TwwGetWordOption = (wwgwSkipLeadingBlanks, wwgwQuotesAsWords, wwgwStripQuotes,
  634.                       wwgwSpacesInWords);
  635.   TwwGetWordOptions = set of TwwGetWordOption;
  636.   strCharSet = Set of char;
  637. Function wwGetWord(s: string; var APos: integer;
  638.          Options: TwwGetWordOptions; DelimSet: strCharSet): string;
  639. var i: integer;
  640.    Function max(x,y: integer): integer;
  641.    begin
  642.      if x>y then result:= x
  643.      else result:= y;
  644.    end;
  645.    Procedure StripQuotes;
  646.    begin
  647.       if not (wwgwStripQuotes in Options) then exit;
  648.       if (Result[1]='"') or (Result[1]='''') then
  649.          if (Result[length(Result)] = '"') or
  650.             (Result[length(Result)] = '''') then
  651.             Result:= copy(Result, 2, length(Result)-2)
  652.          else
  653.             Result:= copy(Result, 2, length(Result)-1);
  654.    end;
  655. begin
  656.    result:= '';
  657.    if APos<=0 then exit;
  658.    if APos>length(s) then exit;
  659.    i:= APos;
  660.    if (wwgwSkipLeadingBlanks in Options) then
  661.    begin
  662.       while (i<=length(s)) and ((s[i]=' ') or (s[i]=#9)) do inc(i);
  663.       APos:= i;
  664.    end;
  665.    if (wwgwQuotesAsWords in Options) then
  666.    begin
  667.       if s[i]='"' then begin
  668.          inc(i);
  669.          while (i<=length(s)) and (s[i]<>'"') do inc(i);
  670.          if s[i]='"' then begin
  671.             result:= copy(s, APos, i+1-APos);
  672.             APos:= i+1;
  673.          end
  674.          else if (i>length(s)) then begin
  675.             result:= copy(s, APos, length(s));
  676.             APos:= length(s)+1;
  677.          end;
  678.          StripQuotes;
  679.          exit;
  680.       end
  681.    end;
  682.    if wwgwSpacesInWords in Options then
  683.    begin
  684.       while (i<=length(s)) and (s[i] in [#32..#255]) do begin
  685.          if (s[i] in DelimSet) then break
  686.          else inc(i);
  687.       end
  688.    end
  689.    else begin
  690.       while (i<=length(s)) and (s[i] in [#33..#255]) do begin
  691.          if (s[i] in DelimSet) then break
  692.          else inc(i);
  693.       end
  694.    end;
  695.    result:= copy(s, APos, max(i-APos, 1));
  696.    if length(result)>1 then APos:= i
  697.    else APos:= i+1;
  698. end;
  699. }
  700. procedure TfcDBCustomTreeView.RefreshDataLinks(FirstDS, LastDS: TDataSource);
  701. var i:integer;
  702.     FDataLink: TfcTreeDataLink;
  703.     tempDS, curDS: TDataSource;
  704.     index, tempIndex: integer;
  705.     token: string;
  706.     OldLinks: TList;
  707.    { Retrieve TDataSource with the name of the datasource }
  708.    Function DataSourceFromToken(DataSourceName: string): TDataSource;
  709.    var Component: TComponent;
  710.        token1, token2: string;
  711.    begin
  712.       result:= nil;
  713.       if (pos('.', DataSourceName)<=0) and
  714.          (pos('->', DataSourceName)<=0) then
  715.       begin
  716.          Component:= GetParentForm(self).FindComponent(DataSourceName);
  717.       end
  718.       else begin
  719.          token1:= fcGetToken(DataSourceName, '.', 0);
  720.          token2:= fcGetToken(DataSourceName, '.', 1);
  721.          Component:= FindGlobalComponent(token1);
  722.          if Component<>nil then
  723.             Component:= Component.FindComponent(token2);
  724.          if Component=nil then { 7/25/99 - Builder notation }
  725.          begin
  726.            token1:= fcGetToken(DataSourceName, '->', 0);
  727.            token2:= fcGetToken(DataSourceName, '->', 1);
  728.            Component:= FindGlobalComponent(token1);
  729.            if Component<>nil then
  730.               Component:= Component.FindComponent(token2);
  731.          end
  732.       end;
  733.       if Component is TDataSource then
  734.          result:= TDataSource(Component);
  735.    end;
  736.    Function GetDataLink(curDS: TDataSource): TfcTreeDataLink;
  737.    var i: integer;
  738.    begin
  739.       for i:= 0 to OldLinks.count-1 do begin
  740.          if TfcTreeDataLink(OldLinks[i]).DataSource = curDS then
  741.          begin
  742.              result:= OldLinks[i];
  743.              OldLinks.Delete(i);  { Remove from list so that its not destroyed later }
  744.              exit;
  745.          end
  746.       end;
  747.       result:= TfcTreeDataLink.Create(Self);
  748.    end;
  749. begin
  750.    if csDestroying in ComponentState then exit;
  751.    OldLinks:= TList.Create;
  752.    try
  753.      for i:= 0 to FDataLinks.count-1 do begin
  754.         if (FDataLinks[i]<>FFirstDataLink) and
  755.            (FDataLinks[i]<>FLastDataLink) then
  756.          OldLinks.Add(FDataLinks[i]);
  757.      end;
  758.      if ((LastDS=nil) or (LastDS.DataSet=nil)) and (DataSources='') then
  759.      begin
  760.         { Clearing datasource }
  761.         if FDataLinks.Count>0 then invalidate;
  762.         FDataLinks.Clear;
  763.         exit;
  764.      end;
  765.      FDataLinks.Clear;
  766.      if FFirstDataLink.DataSet<>nil then
  767.      begin
  768.         FDataLinks.Add(FFirstDataLink);
  769.         if FFirstDataLink.Active then // 2/23/01 - Make sure active so that recordcount gets set
  770.            FFirstDataLink.BufferCount := CacheSize;
  771.         index:= 1;
  772.      end
  773.      else index:= 0;
  774.      if FFirstDataLink.Dataset<>FLastDataLink.DataSet then
  775.      begin
  776.         FDataLinks.Add(FLastDataLink);
  777.         if FFirstDataLink.Active then  // Make sure active so that recordcount gets set
  778.            FLastDataLink.BufferCount := CacheSize;
  779.      end;
  780.      if (DataSources='') then begin
  781.        { 3/2/99 - Special case of only one level so don't add any more datalinks}
  782.        if FFirstDataLink.Dataset=FLastDataLink.DataSet then exit;
  783.        try
  784.           curDS:= LastDS;
  785.           repeat
  786.              tempDS:= fcGetControlMasterSource(curDS.dataset);
  787.              if tempDS=nil then
  788.                 tempDS:= fcGetControlDataSource(curDS.dataset);
  789.              curDS:= tempDS;
  790.              if (curDS<>FirstDS) and (curDS<>nil) then
  791.              begin
  792.                 FDataLink:= GetDataLink(curDS);
  793.                 FDataLink.BufferCount := CacheSize;
  794.                 FDataLink.DataSource := curDS;
  795.                 FDataLinks.Insert(index, FDataLink);
  796.              end
  797.           until (curDS=FirstDS) or (curDS=nil);
  798.        finally
  799.        end
  800.      end
  801.      else begin
  802.         HaveBadLink:= False;
  803.         tempIndex:= 0;
  804.         repeat
  805.            token:= fcGetToken(DataSources, ';', tempIndex);
  806.            curDS:= DataSourceFromToken(token);
  807.            if (curDS=nil) and
  808.               ((pos('.', token)>0) or (pos('->', token)>0)) then { 7/25/99 - Suppport Builder notation }
  809.                  HaveBadLink:= True;
  810.            if (curDS<>nil) and (curDS<>FirstDS) and (curDS<>LastDS) then
  811.            begin
  812.               FDataLink:= TfcTreeDataLink.Create(Self);
  813.               FDataLink.BufferCount := CacheSize;
  814.               FDataLink.DataSource := curDS;
  815.               FDataLinks.Insert(index, FDataLink);
  816.               inc(index);
  817.            end;
  818.            inc(tempIndex);
  819.         until token = '';
  820.      end;
  821.   finally
  822.      for i:= 0 to OldLinks.Count-1 do TfcTreeDataLink(OldLinks[i]).Free;
  823.      OldLinks.Free;
  824.   end
  825. end;
  826. procedure TfcDBCustomTreeView.SetDataSource(Value: TDataSource);
  827. begin
  828.   FreeLastActiveBookmark;  // Free LastActiveBookmark when datasource changes
  829.   FFirstDataLink.Datasource:= Value;
  830.   RefreshDataLinks(Value, FLastDataLink.DataSource);
  831.   { Initialize other data links }
  832.   if (Value<>nil) and (Value.DataSet<>nil) then
  833.      LinkActive(Value.DataSet, Value.Dataset.Active);
  834. end;
  835. procedure TfcDBCustomTreeView.SetDataSources(Value: String);
  836. begin
  837.   if Value<>FDataSourcesMiddle then
  838.   begin
  839.      FreeLastActiveBookmark;  // Free LastActiveBookmark when datasource changes
  840.      FDataSourcesMiddle:= Value;
  841.      RefreshDataLinks(FFirstDataLink.DataSource, FLastDataLink.DataSource);
  842.      if not HaveValidDataLinks then invalidate;
  843.   end
  844. end;
  845. procedure TfcDBCustomTreeView.SetLastDataSource(Value: TDataSource);
  846. begin
  847.   FreeLastActiveBookmark;  // Free LastActiveBookmark when datasource changes
  848.   FLastDataLink.Datasource:= Value;
  849.   RefreshDataLinks(FFirstDataLink.DataSource, Value);
  850.   { Initialize other data links }
  851.   if (Value<>nil) and (Value.DataSet<>nil) then
  852.      LinkActive(Value.DataSet, Value.Dataset.Active);
  853. end;
  854. procedure TfcDBCustomTreeView.LinkActive(DataSet: TDataSet; Value: Boolean);
  855. begin
  856.   SaveIfFirstRecordBookmark(DataSet);
  857.   if (FDataLinks.count>0) and (Dataset = TfcTreeDataLink(FDataLinks[0]).DataSet) then begin
  858.      FreeRootBookmark;
  859.      IsRootDataSetMoved;
  860.   end;
  861.   LayoutChanged;
  862.   UpdateScrollBar;
  863. end;
  864. { Return true if master node has changed }
  865. { This code is not implemented yet }
  866. Function TfcDBCustomTreeView.InMasterChanging(DataSet: TDataSet): boolean;
  867. begin
  868.     if DataSet = DatasourceFirst.dataset then result:= False
  869.     else result:= True;
  870. end;
  871. Function TfcDBCustomTreeView.IsRootDataSetMoved: boolean;
  872. var TempRootDataSetBookmark: TBookmark;
  873.    res: CmpBkmkRslt;
  874.    RootDataSet: TDataSet;
  875. begin
  876.    result:= False;
  877.    if FDataLinks.count<=0 then exit;
  878.    RootDataSet:= TfcTreeDataLink(FDataLinks[0]).DataSet;
  879.    if (RootDataSet=nil) then exit;
  880.    if RootDataSet.state <> dsBrowse then exit;
  881.    TempRootDataSetBookmark:= RootDataSet.GetBookmark;
  882.    if TempRootDataSetBookmark=nil then exit;
  883.    if RootDataSetBookmark=nil then begin
  884.       RootDataSetBookmark:= TempRootDataSetBookmark;
  885.       exit;
  886.    end;
  887.    res:= RootDataSet.CompareBookmarks(RootDataSetBookmark, TempRootDataSetBookmark);
  888.    if (res=CMPKeyEql) or (res=CMPEql) then begin
  889.       RootDataSet.FreeBookmark(TempRootDataSetBookmark);
  890.       exit;
  891.    end;
  892.    result:= True;
  893.    FreeRootBookmark;
  894.    RootDataSetBookmark:= TempRootDataSetBookmark;
  895. end;
  896. procedure TfcDBCustomTreeView.DataChanged(DataSet: TDataSet);
  897. var ChildDataLink: TfcTreeDataLink;
  898. begin
  899.   if FDataLinks.Count=0 then exit;
  900.   
  901.   if Dataset = TfcTreeDataLink(FDataLinks[0]).DataSet then begin
  902.      SaveIfFirstRecordBookmark(DataSet);
  903.      if IsRootDataSetMoved then
  904.         ActiveDataSet:= DataSet;
  905.      UpdateScrollBar;
  906.   end
  907.   else if FScrollWithinLevel then UpdateScrollBar;
  908.   if dataset.state = dsBrowse then
  909.   begin
  910.      ChildDataLink:= GetChildDataLink(Dataset);
  911.      if ChildDataLink<>nil then ResetStartOffsets(ChildDataLink.DataSet);
  912.   end;
  913. //     ResetStartOffsets(DataSet); { 12/23/98 }
  914.   CheckMaxWidthGrow:= True;
  915.   invalidateClient;
  916. end;
  917. Function TfcDBCustomTreeView.GetDataLinkIndex(Dataset: TDataset): integer;
  918. var i:integer;
  919. begin
  920.    result:= -1;
  921.    if DataSet =nil then exit;
  922.    for i:= 0 to FDataLinks.count-1 do begin
  923.       if TfcTreeDataLink(FDataLinks[i]).dataset = DataSet then
  924.       begin
  925.          result:= i;
  926.          break;
  927.       end
  928.    end;
  929. end;
  930. Function TfcDBCustomTreeView.GetDataLink(Dataset: TDataset): TfcTreeDataLink;
  931. var i:integer;
  932. begin
  933.    result:= nil;
  934.    if DataSet =nil then exit;
  935.    for i:= 0 to FDataLinks.count-1 do begin
  936.       if TfcTreeDataLink(FDataLinks[i]).dataset = DataSet then
  937.          result:= TfcTreeDataLink(FDataLinks[i]);
  938.    end;
  939. end;
  940. Function TfcDBCustomTreeView.GetParentDataLink(Dataset: TDataset): TfcTreeDataLink;
  941. var i:integer;
  942. begin
  943.    result:= nil;
  944.    for i:= 0 to FDataLinks.count-1 do begin
  945.       if TfcTreeDataLink(FDataLinks[i]).dataset = DataSet then
  946.       begin
  947.          if i>0 then
  948.             result:= TfcTreeDataLink(FDataLinks[i-1]);
  949.          break;
  950.       end
  951.    end;
  952. end;
  953. Function TfcDBCustomTreeView.GetChildDataLink(Dataset: TDataset): TfcTreeDataLink;
  954. var i:integer;
  955. begin
  956.    result:= nil;
  957.    for i:= 0 to FDataLinks.count-1 do begin
  958.       if TfcTreeDataLink(FDataLinks[i]).dataset = DataSet then
  959.       begin
  960.          if (i+1)<=FDataLinks.count-1 then
  961.             result:= TfcTreeDataLink(FDataLinks[i+1]);
  962.          break;
  963.       end
  964.    end;
  965. end;
  966. procedure TfcDBCustomTreeView.Scroll(DataSet: TDataSet; Distance: Integer);
  967. var DataLink: TfcTreeDataLink;
  968. begin
  969.   if not HandleAllocated then Exit;
  970.   if FDataLinks.Count>0 then begin
  971.      if Dataset = TfcTreeDataLink(FDataLinks[0]).DataSet then begin
  972.         IsRootDataSetMoved;
  973.         UpdateScrollBar;
  974.      end
  975.      else if FScrollWithinLevel then UpdateScrollBar;
  976.   end;
  977.   if IsChildDataSetOfActive(DataSet) then
  978.   begin
  979.      if (dtvoAutoExpandOnDSScroll in Options) then
  980.         LastVisibleDataSet:= dataset;
  981.   end
  982.   else LastVisibleDataSet:= dataset;
  983.   if ActiveDataSet<>DataSet then
  984.   begin
  985.      if (dtvoAutoExpandOnDSScroll in Options) then
  986.         ActiveDataSet:= DataSet;
  987.      DataLink:= GetChildDataLink(ActiveDataSet);
  988.      if DataLink<>nil then begin
  989.         ResetStartOffsets(DataLink.DataSet);
  990.      end;
  991.   end
  992.   else begin
  993.      DataLink:= GetChildDataLink(ActiveDataSet);
  994.      if DataLink<>nil then ResetStartOffsets(DataLink.DataSet);
  995.   end;
  996.   SaveIfFirstRecordBookmark(DataSet);
  997.   CheckMaxWidthGrow:= True;
  998.   InvalidateClient;
  999. end;
  1000. Procedure TfcDBCustomTreeView.SaveIfFirstRecordBookmark(DataSet: TDataSet);
  1001. begin
  1002.   if DataSet=nil then exit;
  1003.   if FDataLinks.Count>0 then
  1004.   begin
  1005.      if Dataset <> TfcTreeDataLink(FDataLinks[0]).DataSet then exit;
  1006.      if (DataSet.state = dsBrowse) and (DataSet.Bof) and (TfcTreeDataLink(FDataLinks[0]).DataSet = DataSet) then
  1007.      begin
  1008.          FreeFirstBookmark;
  1009.          FirstBookmark:= dataset.GetBookmark;
  1010.      end
  1011.   end;
  1012.   if DataSet.state in [dsEdit, dsInsert] then FreeFirstBookmark;
  1013. end;
  1014. Procedure TfcDBCustomTreeView.FreeFirstBookmark;
  1015. begin
  1016.      {$ifdef fcdelphi4up}
  1017.      if FirstBookmark<>Nil then FreeMem(FirstBookmark);
  1018.      FirstBookmark:= nil;
  1019.      {$else}
  1020.      if FirstBookmark<>Nil then StrDispose(FirstBookmark);
  1021.      FirstBookmark:= nil;
  1022.      {$endif}
  1023. end;
  1024. Procedure TfcDBCustomTreeView.FreeLastActiveBookmark;
  1025. begin
  1026.      {$ifdef fcdelphi4up}
  1027.      if LastActiveBookmark<>Nil then FreeMem(LastActiveBookmark);
  1028.      LastActiveBookmark:= nil;
  1029.      {$else}
  1030.      if LastActiveBookmark<>Nil then StrDispose(LastActiveBookmark);
  1031.      LastActiveBookmark:= nil;
  1032.      {$endif}
  1033. end;
  1034. procedure TfcDBCustomTreeView.DoCalcNodeAttributes(Node: TfcDBTreeNode);
  1035. begin
  1036.    if Assigned(FOnCalcNodeAttributes) then
  1037.       FOnCalcNodeAttributes(Self, Node);
  1038. end;
  1039. procedure TfcDBCustomTreeView.DoCalcSectionAttributes(Node: TfcDBTreeNode;
  1040.   Section: TfcTreeHeaderSection; var DisplayText: string);
  1041. begin
  1042.    if Assigned(FOnCalcSectionAttributes) then
  1043.       FOnCalcSectionAttributes(Self, Node, Section, DisplayText);
  1044. end;
  1045. procedure TfcDBCustomTreeView.DoDrawSection(Node: TfcDBTreeNode;
  1046.   Section: TfcTreeHeaderSection;
  1047.   ARect: TRect;
  1048.   s: string;
  1049.   var DoDefault: boolean);
  1050. begin
  1051.    if Assigned(FOnDrawSection) then
  1052.       FOnDrawSection(Self, Node, Section, ARect, S, DoDefault);
  1053. end;
  1054. Function fcGetWord(s: string; var APos: integer; var IsFieldName: boolean): string;
  1055. var i: integer;
  1056.    Function max(x,y: integer): integer;
  1057.    begin
  1058.      if x>y then result:= x
  1059.      else result:= y;
  1060.    end;
  1061. begin
  1062.    result:= '';
  1063.    IsFieldName:= False;
  1064.    if APos<=0 then exit;
  1065.    if APos>length(s) then exit;
  1066.    i:= APos;
  1067.    if s[i]='"' then begin
  1068.       inc(i);
  1069.       while (i<=length(s)) and (s[i]<>'"') do inc(i);
  1070.       if s[i]='"' then begin
  1071.          result:= copy(s, APos+1, i-APos-1);
  1072.          IsFieldName:= True;
  1073.          APos:= i+1;
  1074.       end
  1075.    end
  1076.    else begin
  1077.       while (i<=length(s)) and (not (s[i] in ['"'])) do inc(i);
  1078.       result:= copy(s, APos, max(i-APos, 1));
  1079.       APos:= i;
  1080.    end;
  1081. end;
  1082. Function TfcDBCustomTreeView.GetNodeText(
  1083.    DisplayFieldLine: string;
  1084.    DataSet: TDataSet;
  1085.    var Field: TField): string;
  1086. var line, word: string;
  1087.     APos: integer;
  1088.     isFieldName: boolean;
  1089.     curField: TField;
  1090.     i: integer;
  1091.     tempStr: string;
  1092. begin
  1093.   line:= '';
  1094.   APos:= 1;
  1095.   if Header<>nil then with Header do begin
  1096.      for i:= 0 to Sections.count-1 do begin
  1097.         curField:= DataSet.FindField(Sections[i].FieldName);
  1098. //        Sections[i].Field:= curField;
  1099.         if curField<>nil then begin
  1100.            if curField.datatype=ftMemo then
  1101.               tempStr:= curField.asString
  1102.            else
  1103.               tempStr:= curField.DisplayText;
  1104.            tempStr:= fcStrRemoveChar(tempStr, #9);
  1105.         end
  1106.         else tempStr:= '';
  1107.         if Field=nil then Field:= curField;
  1108.         if i>0 then line:= line + #9 + tempstr
  1109.         else line:= tempstr;
  1110.      end;
  1111.      result:= line;
  1112.      exit;
  1113.   end;
  1114.   Field:= nil;
  1115.   repeat
  1116.       word:= fcGetWord(DisplayFieldLine, APos, isFieldName);
  1117.       if (word<>'') then begin
  1118.          if IsFieldName then
  1119.          begin
  1120.             curField:= dataset.findfield(word);
  1121.             if curField<>nil then
  1122.             begin
  1123.                if curField.datatype=ftMemo then
  1124.                  line:= line + curField.asString
  1125.                else
  1126.                  line:= line + curField.DisplayText;
  1127.                if Field=nil then Field:= curField;
  1128.             end
  1129.             else
  1130.                line:= line + '<Field not found ' + word + '>'
  1131.          end
  1132.          else begin
  1133.             if pos('"', DisplayFieldLine)>0 then
  1134.                line:= line + word
  1135.             else begin
  1136.                curField:= dataset.findfield(word);
  1137.                if curField=nil then
  1138.                   line:= line + ' <Field not found ' + word + '> '
  1139.                else begin
  1140.                   if curField.datatype=ftMemo then
  1141.                     line:= line + dataset.fieldbyname(word).asString
  1142.                   else line:= line + dataset.fieldbyname(word).DisplayText;
  1143.                   if Field=nil then Field:= curField;
  1144.                end
  1145.             end
  1146.          end
  1147.       end
  1148.   until (word='');
  1149.   result:= line;
  1150. end;
  1151. procedure TfcDBCustomTreeView.WMPaint(var Message: TWMPaint);
  1152. var
  1153.     Node: TfcDBTreeNode;
  1154.     NextDataLink: TfcTreeDataLink;
  1155. //    Flags: integer;
  1156.     FocusRect, R: TRect;
  1157.     i: Integer;
  1158.     ActiveNode: boolean;
  1159.     PrevActiveRecord: integer;
  1160.     sp: TPoint;
  1161.     NewMaxTextWidth : integer;
  1162.     UpdateRect: TRect;
  1163.     FActiveBookmark: TBookmark;
  1164.     res: CmpBkmkRslt;
  1165.     NewNode: boolean;
  1166.     DefaultDrawing: boolean;
  1167.     MousePos: TPoint;
  1168.     OrigCheckMaxWidth: boolean;
  1169.     ParentForm:TCustomForm;
  1170.      procedure ScanDataLink(ParentNode: TfcDBTreeNode; DataLinkIndex: integer);
  1171.      var DataLink: TfcTreeDataLink;
  1172.          RecIndex: integer;
  1173.          PrevActiveRecord: integer;
  1174.          curBookmark: TBookmark;
  1175.          res: CmpBkmkRslt;
  1176.      begin
  1177.         if DataLinkIndex>=FDataLinks.count then exit;
  1178.         DataLink:= TfcTreeDataLink(FDataLinks[DataLinkIndex]);
  1179.         PrevActiveRecord:= DataLink.ActiveRecord;
  1180.         for RecIndex:= 0 to DataLink.RecordCount-1 do
  1181.         begin
  1182.            DataLink.ActiveRecord:= RecIndex;
  1183.            Node:= TfcDBTreeNode.create;
  1184.            if DataLinkIndex<=DisplayFields.count-1 then
  1185.              Node.Text:= GetNodeText(DisplayFields[DataLinkIndex], DataLink.DataSet, Node.Field)
  1186.            else
  1187.              Node.Text:= GetNodeText(DataLink.DataSet.Fields[0].FieldName, DataLink.DataSet, Node.Field);
  1188.            Node.Level:= DataLinkIndex;
  1189.            Node.DataLink:= DataLink;
  1190.            Node.DataSet:= DataLink.DataSet;
  1191.            Node.ActiveRecord:= RecIndex;
  1192.            Node.Expanded:= False;
  1193.            Node.HasChildren:= (DataLinkIndex < FDataLinks.count-1);
  1194.            Node.Parent:= ParentNode;
  1195.            Node.Selected:= False;
  1196.            Node.ImageIndex:= 0;
  1197.            Node.StateIndex:= -1;
  1198.            Node.HasPrevSibling:= (not DataLink.DataSet.Bof) or (DataLink.ActiveRecord>0);
  1199.            if Node.HasPrevSibling and (DataLinkIndex=0) then
  1200.            begin
  1201.               curBookmark:= DataLink.dataset.GetBookmark;
  1202.               try { 2/8/99 - Move try block before test for firstbookmark to fix potential leak }
  1203.                  if (curBookmark<>nil) and (FirstBookmark<>nil) then
  1204.                  begin
  1205.                    res:= DataLink.DataSet.CompareBookmarks(FirstBookmark, curBookmark);
  1206.                    if (res=CMPKeyEql) or (res=CMPEql) then
  1207.                    begin
  1208.                       if DataLink.ActiveRecord=0 then
  1209.                          Node.HasPrevSibling:= False
  1210.                       else
  1211.                          FreeFirstBookmark; { First Record inserted by another application }
  1212.                    end;
  1213.                  end
  1214.               finally
  1215.                  DataLink.dataset.FreeBookmark(curBookmark);
  1216.               end;
  1217.            end;
  1218.            if (not Node.HasPrevSibling) and (dsInsert = DataLink.DataSet.state) then
  1219.               Node.HasPrevSibling:= True;
  1220.            Node.HasNextSibling:=
  1221.              not ((RecIndex = DataLink.RecordCount-1) and
  1222.                   ((DataLink.RecordCount < DataLink.BufferCount) or DataLink.DataSet.eof));
  1223.            if (not Node.HasNextSibling) and (dsInsert = DataLink.DataSet.state) then
  1224.               Node.HasNextSibling:= True;
  1225.            Nodes.Add(Node);
  1226.            if (PrevActiveRecord=RecIndex) then
  1227.            begin
  1228.               if (DataLink.DataSet=ActiveDataSet) then
  1229.               begin
  1230.                  ActiveNodeIndex:= Nodes.count-1;
  1231.                  Node.Selected:= True;
  1232.               end;
  1233.               if (DataLink.DataSet=LastVisibleDataSet) or
  1234.                  (LastVisibleDataSet=nil) and not (csDesigning in ComponentState) then
  1235.               begin
  1236.                  NextDataLink:= GetChildDataLink(DataLink.DataSet);
  1237.                  if (NextDataLink<>nil) and
  1238.                     NextDataLink.DataSet.eof and NextDataLink.DataSet.bof then
  1239.                  begin
  1240.                     Node.HasChildren:= False;
  1241.                  end;
  1242.               end
  1243.               else begin
  1244.                  Node.Expanded:= True;
  1245.                  ScanDataLink(Node, DataLinkIndex + 1);
  1246.               end
  1247.            end
  1248.         end;
  1249.         DataLink.ActiveRecord:= PrevActiveRecord;
  1250.      end;
  1251.    { Fill with background color }
  1252.    Procedure BeginPainting;
  1253.    var PaintRect: TRect;
  1254.    begin
  1255.       InPaint:= True;
  1256.       if VertScrollBar.visible then
  1257.          FPaintBitmap.Width := fcMax(0, Width + HorzScrollBar.position - VertScrollBar.Width)
  1258.       else
  1259.          FPaintBitmap.Width := Width + HorzScrollBar.position;
  1260.       // otherwise horz Scrollbar getting painted over by data when themes enabled
  1261.       if HorzScrollBar.visible and fcUseThemes(self) then
  1262.         FPaintBitmap.Height := HorzScrollBar.Top-1
  1263.       else
  1264.         FPaintBitmap.Height := Height;
  1265.       FPaintCanvas.Brush.Color := Color;
  1266.       if (FImager <> nil){ and (FImager.visible) }then
  1267.       begin
  1268.          if FImager.WorkBitmap.Empty then FImager.UpdateWorkBitmap;
  1269.          if FImager.DrawStyle=dsTile then
  1270.          begin
  1271.             PaintRect:= Rect(0,0,FPaintBitmap.Width, FPaintBitmap.Height);
  1272.             FImager.WorkBitmap.TileDraw(Canvas, PaintRect);
  1273.          end
  1274.          else begin
  1275.             PaintRect:= Rect(horzscrollbar.position + 0, 0, horzscrollbar.position + ClientRect.Right, ClientRect.Bottom);
  1276.             Canvas.StretchDraw(PaintRect, FImager.WorkBitmap);
  1277.          end
  1278.       end
  1279.       else
  1280.         FPaintCanvas.FillRect(Rect(0, 0, FPaintBitmap.Width, FPaintBitmap.Height));
  1281.    end;
  1282.    procedure EndPainting;
  1283.    var r, sourceRect: TRect;
  1284.        scrollpos: integer;
  1285.        PaintClipRect: TRect;
  1286.        MyPrintCanvas: TCanvas;
  1287.    begin
  1288.      r := ClientRect;
  1289.      OffsetRect(r, 1, 0);
  1290.      // 11/17/99 - Support form.print
  1291.      if (csPaintCopy in ControlState) and (Message.DC<>0) then
  1292.      begin
  1293.         MyPrintCanvas:= TCanvas.Create;
  1294.         MyPrintCanvas.Handle:= Message.DC;
  1295.      end
  1296.      else MyPrintCanvas:= FCanvas;
  1297.      MyPrintCanvas.CopyMode:= cmSrcCopy;
  1298.      scrollpos:= HorzScrollBar.position;
  1299.      PaintClipRect:= FPaintCanvas.ClipRect;
  1300.      if PaintClipRect.Right>ClientRect.Right then
  1301.      begin
  1302.         PaintClipRect.Right:= ClientRect.Right;
  1303.      end;
  1304.      if UpdateRect.Bottom>ClientRect.Bottom then
  1305.      begin
  1306.         UpdateRect.Bottom:= ClientRect.Bottom;
  1307.      end;
  1308.      if (UpdateRect.Top=0) and (UpdateRect.Bottom=0) and
  1309.         (UpdateRect.Left=0) and (UpdateRect.Right=0) then
  1310.         UpdateRect:= PaintClipRect;
  1311.      SourceRect:= Rect(Scrollpos, UpdateRect.Top,
  1312.         ScrollPos+PaintClipRect.Right, UpdateRect.Bottom );
  1313.      MyPrintCanvas.CopyRect(
  1314.         Rect(0,UpdateRect.Top, PaintClipRect.Right, UpdateRect.Bottom),
  1315.         FPaintCanvas, SourceRect);
  1316.      if (csPaintCopy in ControlState) and (Message.DC<>0) then
  1317.      begin
  1318.         MyPrintCanvas.Handle:= 0;
  1319.         MyPrintCanvas.Free;
  1320.      end;
  1321.      InPaint:= False;
  1322.    end;
  1323. begin
  1324.    GetUpdateRect(Handle, UpdateRect, False);
  1325.    SkipErase:= True;
  1326.    inherited;
  1327.    SkipErase:= False;
  1328.    if FCanvas = nil then
  1329.    begin
  1330.       FCanvas := TControlCanvas.Create;
  1331.       FCanvas.Control := Self;
  1332.    end;
  1333.    if (HaveBadLink) then begin { Fix data module problem }
  1334.       BeginPainting;
  1335.       EndPainting;
  1336.       RefreshDataLinks(nil, nil);
  1337.       HaveBadLink:= False;  { Just do first for first paint so clear flag }
  1338.       exit;
  1339.    end;
  1340.    if not HaveValidDataLinks then begin
  1341.       BeginPainting;
  1342.       EndPainting;
  1343.       exit;
  1344.    end;
  1345.    if (Images<>nil) and (StateImages<>nil) then
  1346.    begin
  1347.      FixedOffset := 2;
  1348.    end
  1349.    else begin
  1350.      FixedOffset := 2;
  1351.    end;
  1352.    BeginPainting;
  1353.    r:= Rect(0,0,0,0);
  1354.    try
  1355.       if FDataLinks.count<=0 then begin
  1356.          RefreshDataLinks(DataSourceFirst, DataSourceLast);
  1357.          if FDataLinks.count<=0 then begin
  1358.             exit;
  1359.          end;
  1360.       end;
  1361.      if (ActiveDataSet=nil) then
  1362.        ActiveDataSet:= TfcTreeDataLink(FDataLinks[0]).DataSet;
  1363.      if (ActiveDataSet=nil) or (not ActiveDataSet.Active) then exit;
  1364.      if dtvoHotTracking in Options then
  1365.      begin
  1366.         GetCursorPos(MousePos);
  1367.         sp:= ScreenToClient(MousePos);
  1368.         if (sp.x>0) and (sp.x<Width) and (sp.y>0) and (sp.y<height) then
  1369.            MouseToRow(sp.x, sp.y, HotTrackRow)
  1370.         else HotTrackRow:= -1;
  1371.      end;
  1372.      //11/17/99 - Don't HotTrack if this form is not active.
  1373.      ParentForm := GetParentForm(self);
  1374.      if (ParentForm<>nil) and (ParentForm.handle<>GetActiveWindow) then
  1375.         HotTrackRow:= -1;
  1376.      if not SkipReload then
  1377.      begin
  1378.         NodesCleared:= True;
  1379.         for i:= 0 to Nodes.count-1 do begin
  1380.           if SkipFreeNodes then OldNodes.Add(Nodes[i])
  1381.           else TfcDBTreeNode(Nodes[i]).Free;
  1382.         end;
  1383.         Nodes.Clear;
  1384.         ActiveNodeIndex:= TfcTreeDataLink(FDataLinks[0]).ActiveRecord;;
  1385.         ScanDataLink(nil, 0);
  1386. //        if ActiveNodeIndex-GetStartOffset(ActiveDataSet)>=CacheSize then
  1387. //           SetStartOffset(ActiveDataSet, fcmax(ActiveNodeIndex+1-CacheSize, 0));
  1388.      end;
  1389.      if ActiveNodeIndex-GetStartOffset>=CacheSize then
  1390.         SetStartOffset(ActiveDataSet, fcmax(ActiveNodeIndex+1-CacheSize, 0));
  1391.      if (ActiveNodeIndex>=0) and (ActiveNodeIndex<=Nodes.Count-1) then
  1392.      begin
  1393.         FActiveNode:= TfcDBTreeNode(Nodes[ActiveNodeIndex]);
  1394.      end
  1395.      else FActiveNode:= nil;
  1396.      NewMaxTextWidth:= 0;
  1397.      for i:= 0 to Nodes.Count-1 do begin
  1398.          if HorzScrollBar.Visible then
  1399.          begin
  1400.             if (i>=CacheSize+1) then break;
  1401.          end
  1402.          else if i>=CacheSize then break;
  1403.          if (i+GetStartOffset>=Nodes.count) then break;
  1404.          PaintingRow:= i;
  1405.          Node:= TfcDBTreeNode(Nodes[i+GetStartOffset]);
  1406.          FPaintCanvas.Font.Assign(Font); { Restore original font }
  1407.          FPaintCanvas.Pen.Color := FLineColor; //clBtnShadow; { for line drawing }
  1408.          PrevActiveRecord:= Node.DataLink.ActiveRecord;
  1409.          try
  1410.             Node.DataLink.ActiveRecord:= Node.ActiveRecord;
  1411.             Node.MultiSelected:= FindCurrentMultiSelectIndex(Node.DataLink.DataSet)>=0;
  1412.             ActiveNode:= ActiveNodeIndex=i+GetStartOffset;
  1413.             if ActiveNode and not FMultiSelectAttributes.enabled then
  1414.             begin
  1415.                if Focused then begin
  1416.                   FPaintCanvas.Brush.Color:= clHighlight;
  1417.                   FPaintCanvas.Font.Color := clHighlightText;
  1418.                end
  1419.                else begin
  1420.                   if not (dtvoHideSelection in Options) then
  1421.                   begin
  1422.                      if InactiveFocusColor<>Color then
  1423.                         FPaintCanvas.Brush.Color:= InactiveFocusColor
  1424.                      else
  1425.                         FPaintCanvas.Brush.Color:= clGray;
  1426.                   end
  1427.                   else begin
  1428.                      FPaintCanvas.Brush.Color := Color;
  1429.                   end;
  1430.                   FPaintCanvas.Font.Color:= Font.Color;
  1431.                end
  1432.             end
  1433.             else if FMultiSelectAttributes.enabled then
  1434.             begin
  1435.                if Node.MultiSelected then begin
  1436.                   if Focused then begin
  1437.                      FPaintCanvas.Brush.Color:= clHighlight;
  1438.                      FPaintCanvas.Font.Color := clHighlightText;
  1439.                   end
  1440.                   else begin
  1441.                      FPaintCanvas.Brush.Color:= InactiveFocusColor;
  1442.                      FPaintCanvas.Font.Color:= Font.Color;
  1443.                   end
  1444.                end
  1445.                else begin
  1446.                   if (Imager<>nil) and ActiveNode then
  1447.                      FPaintCanvas.Brush.Color := clNone
  1448.                   else
  1449.                      FPaintCanvas.Brush.Color := Color;
  1450.                   FPaintCanvas.Font.Color := Font.Color;
  1451.                end
  1452.             end
  1453.             else begin
  1454.                FPaintCanvas.Brush.Color := Color;
  1455.                FPaintCanvas.Font.Color := Font.Color;
  1456.             end;
  1457.             { 4/5/99 - Set Node.Hot property }
  1458.             if dtvoHotTracking in Options then
  1459.             begin
  1460.               R:= TextRect(Node, i);
  1461.               Node.Hot:= (PaintingRow = HotTrackRow) and (sp.x>r.left) and (sp.x<r.right);
  1462.             end;
  1463.             DoCalcNodeAttributes(Node);
  1464.             NewMaxTextWidth:=
  1465.               fcMax(Canvas.TextWidth(Node.Text) + TextRect(Node, i).Left, NewMaxTextWidth);
  1466.             FocusRect:= TextRect(Node, i);
  1467.             if not odd(fcRectHeight(r) div 2) and (FocusRect.Top>0) then
  1468.             begin
  1469.                FocusRect.top:= FocusRect.Top - 1;
  1470.             end
  1471.             else FocusRect.Bottom:= FocusRect.Bottom + 1;
  1472.             FocusRect.Left:= FocusRect.Left - 1;
  1473.             if FPaintCanvas.Brush.Color <> clNone then
  1474.             begin
  1475.                if dtvoRowSelect in Options then
  1476.                begin
  1477.                  if BorderStyle=bsNone then { 5/25/99 }
  1478.                     FocusRect.Right:= FPaintBitmap.Width
  1479.                  else
  1480.                     FocusRect.Right:= FPaintBitmap.Width-4;
  1481.                  if Images <> nil then dec(FocusRect.Left, TImageList(Images).Width);
  1482.                  if UseStateImages(node) then dec(FocusRect.Left, GetStateImageWidth);
  1483.                  if (Images<>nil) and UseStateImages(node) then dec(FocusRect.Left, 1);
  1484.                  if (Images<>nil) or UseStateImages(node) then dec(FocusRect.Left, 2);
  1485.                end;
  1486.                if (Node.Selected or
  1487.                    (Node.MultiSelected and FMultiSelectAttributes.enabled)) then
  1488.                    FPaintCanvas.FillRect(FocusRect);
  1489.             end;
  1490.             PaintLines(node);
  1491.             PaintImage(node);
  1492.             R:= TextRect(Node, i);
  1493.             SetBkMode(FPaintCanvas.Handle, TRANSPARENT);
  1494.             try
  1495.                DefaultDrawing:= True;
  1496.                if dtvoHotTracking in Options then
  1497.                begin
  1498.                   R:= TextRect(Node, i);
  1499.                   if Node.Hot then begin
  1500.                      if not node.selected then FPaintCanvas.Font.Color:= clBlue;
  1501.                      FPaintCanvas.Font.Style:= [fsUnderline];
  1502.                      if (fsBold in Font.Style) then
  1503.                         FPaintCanvas.Font.Style:= FPaintCanvas.Font.Style + [fsbold];
  1504.                   end
  1505.                   else if (PaintingRow = HotTrackRow) then
  1506.                      HotTrackRow:= -1;
  1507.                end;
  1508.                if odd(fcRectHeight(r) div 2) then R.Top:= R.Top + 1;
  1509.                R.Left:= R.Left + 1;
  1510.                DoDrawText(self, Node, r, DefaultDrawing);
  1511.                if DefaultDrawing then begin
  1512.                   if Assigned(Header) then
  1513.                   begin
  1514.                      r.Left:= r.Left - 2; // Adjust for 1st column
  1515.                      DrawColumnText(Node, r)
  1516.                   end
  1517.                   else begin
  1518.                     FPaintCanvas.DrawText(Node.Text, R, DT_END_ELLIPSIS OR DT_NOPREFIX);
  1519.                     R.Left:= R.Left - 1;
  1520.                     if odd(fcRectHeight(r) div 2) then R.Top:= R.Top - 1;
  1521.                     if ActiveNode and Focused then begin
  1522.                        if (not (dtvoRowSelect in Options)) then
  1523.                        begin
  1524.                           FPaintCanvas.Brush.Color := clBlack;
  1525.                           if FMultiSelectAttributes.enabled and (Imager=nil) then
  1526.                           begin
  1527.                              SetBkColor(FPaintCanvas.Handle, ColorToRGB(Color));
  1528.                              SetTextColor(FPaintCanvas.Handle, ColorToRGB(Font.Color));
  1529.                           end
  1530.                           else begin
  1531.                              SetBkColor(FPaintCanvas.Handle, ColorToRGB(clHighlight));
  1532.                              SetTextColor(FPaintCanvas.Handle, ColorToRGB(clHighlightText));
  1533.                           end;
  1534.                           FPaintCanvas.DrawFocusRect(FocusRect);
  1535.                        end
  1536.                        else begin
  1537.                           if FMultiSelectAttributes.enabled then
  1538.                           begin
  1539.                              FPaintCanvas.DrawFocusRect(FocusRect);
  1540.                           end;
  1541.                        end;
  1542.                     end
  1543.                  end
  1544.                end
  1545.            finally
  1546.               SetBkMode(FPaintCanvas.Handle, OPAQUE);
  1547.            end;
  1548.         finally
  1549.            Node.DataLink.ActiveRecord:= PrevActiveRecord;
  1550.         end;
  1551.      end;
  1552.      // 9/5/01 - Scrolling width should total columns in header
  1553.      if Header<>nil then
  1554.      begin
  1555.         NewMaxTextWidth:= ComputeHeaderWidth;
  1556.      end;
  1557.      UpTreeButton.enabled:=  (FFirstDataLink<>nil) and (ActiveDataSet<>FFirstDataLink.DataSet);
  1558.      DownTreeButton.enabled:=  (FLastDataLink<>nil) and (ActiveDataSet<>FLastDataLink.DataSet) and
  1559.                                (FActiveNode<>nil) and FActiveNode.hasChildren;
  1560.      OrigCheckMaxWidth:= CheckMaxWidth;
  1561.      if (((MaxTextWidth<>NewMaxTextWidth) and (CheckMaxWidth or CheckMaxWidthGrow))
  1562.         or ResetScroll) and
  1563.         (dtvoShowHorzScrollBar in Options) then
  1564.      begin
  1565.         CheckMaxWidth:= False;
  1566.         CheckMaxWidthGrow:= False;
  1567.         if (MaxTextWidth<NewMaxTextWidth) or
  1568.            ((HorzScrollBar.position=0) and OrigCheckMaxWidth) then
  1569.         begin
  1570.            MaxTextWidth:= NewMaxTextWidth;
  1571.            ResetScroll:= False;
  1572.            with HorzScrollBar do begin
  1573.               Min:= 0;
  1574.               Max:= NewMaxTextWidth+3;
  1575.               InComputeHorzWidthOnly := True;
  1576.               PageSize:= self.ClientRect.Right - self.ClientRect.Left;
  1577.               InComputeHorzWidthOnly := False;
  1578.               GetClientRect; // Updates scrollbars if necessary after PageSize is set
  1579.               ScrollSize:= Max;
  1580. //           if PageSize>Max then
  1581. //              LayoutChanged;
  1582.               HorzScrollBar.Invalidate;
  1583.            end
  1584.         end;
  1585.      end
  1586.      else begin
  1587.         CheckMaxWidth:= False;
  1588.         CheckMaxWidthGrow:= False;
  1589.      end;
  1590.      if FActiveNode<>nil then with FActiveNode.DataLink.DataSet do
  1591.      begin
  1592.         FActiveBookmark:= GetBookmark;
  1593.         if ActiveDataSetChanged then
  1594.         begin
  1595.            FreeLastActiveBookmark;
  1596.            LastActiveBookmark:= FActiveBookmark;
  1597.            try { 4/25/99 - Catch exception }
  1598.               Change(FActiveNode);
  1599.            except
  1600.               ActiveDataSetChanged:= False;
  1601.               raise;
  1602.            end;
  1603.            ActiveDataSetChanged:= False;
  1604.            exit;
  1605.         end;
  1606.         if (FActiveBookmark<>nil) then
  1607.         begin
  1608.            if LastActiveBookmark<>nil then
  1609.            begin
  1610.               res:= CompareBookmarks(LastActiveBookmark, FActiveBookmark);
  1611.               NewNode:= (res<>CMPKeyEql) and (res<>CMPEql);
  1612.            end
  1613.            else NewNode:= True;
  1614.            if NewNode then
  1615.            begin
  1616.               FreeLastActiveBookmark;
  1617.               LastActiveBookmark:= FActiveBookmark;
  1618.               Change(FActiveNode);
  1619.               ActiveDataSetChanged:= False;
  1620.            end
  1621.            else FreeBookmark(FActiveBookmark);
  1622.         end
  1623.      end;
  1624.    finally
  1625.      EndPainting;
  1626.      if HotTrackRow>=0 then
  1627.      begin
  1628.         SaveCursor:= Screen.Cursor;
  1629.         Cursor:= crHandPoint;
  1630.      end
  1631.      else if Screen.Cursor<>crArrow then
  1632.         Cursor:= SaveCursor;
  1633.    end;
  1634. end;
  1635. procedure TfcDBCustomTreeView.WMEraseBkgnd(var Message: TWmEraseBkgnd);
  1636. begin
  1637.   if True or SkipErase then begin { 4/31/99 - Always Remove erase to prevent flicker when resizing }
  1638.      Message.result:= 1;
  1639.      exit;
  1640.   end
  1641.   else inherited;
  1642. end;
  1643. Procedure TfcDBCustomTreeView.MouseToRow(X, Y: integer; var Row: integer);
  1644. begin
  1645.    row:= -1;
  1646.    if y<0 then exit;
  1647.    if y>Height then exit;
  1648.    row:= y div RowHeight;
  1649. end;
  1650. function TfcDBCustomTreeView.RowToNode(Row: integer; var Node: TfcDBTreeNode): boolean;
  1651. begin
  1652.    result:= false;
  1653.    Node:= nil;
  1654.    if row<0 then exit;
  1655.    if row> cacheSize-1 then exit;
  1656.    if GetStartOffset+Row<=Nodes.count-1 then
  1657.    begin
  1658.       Node:= Nodes[GetStartOffset + Row];
  1659.       result:= true;
  1660.    end
  1661.    else result:= false;
  1662. end;
  1663. {function TfcDBCustomTreeView.NodeToIndex(Node: TfcDBTreeNode): integer;
  1664. var i: Integer;
  1665. begin
  1666.    result:= -1;
  1667.    for i:= 0 to Nodes.count-1 do begin
  1668.       if nodes[i]=node then begin
  1669.          result:= i;
  1670.          break;
  1671.       end
  1672.    end
  1673. end;
  1674. }
  1675. function TfcDBCustomTreeView.NodeToRow(Node: TfcDBTreeNode; var Row: integer): boolean;
  1676. var i: integer;
  1677. begin
  1678.    result:= false;
  1679.    row:= -1;
  1680.    for i:= 0 to Nodes.count-1 do begin
  1681.       if nodes[i]=node then
  1682.       begin
  1683.          Row:= i - GetStartOffset;
  1684.          if Row>=0 then result:= True;
  1685.          break;
  1686.       end
  1687.    end;
  1688. end;
  1689. procedure TfcDBCustomTreeView.Collapse(Node: TfcDBTreeNode);
  1690. var DataLink: TfcTreeDataLink;
  1691. begin
  1692.    if Node=nil then exit;
  1693.    try
  1694.       SkipFreeNodes:= True;
  1695.       DoUserCollapse(Node);
  1696.       DataLink:= TfcTreeDataLink(FDataLinks[Node.Level]);
  1697.       ActiveDataSet:= DataLink.DataSet;
  1698.       if LastVisibleDataSet<>ActiveDataSet then
  1699.       begin
  1700.          LastVisibleDataSet := ActiveDataSet;
  1701.          CheckMaxWidth:= True;
  1702.       end;
  1703.       InvalidateClient;
  1704.    finally
  1705.       FreeOldNodes;
  1706.       SkipFreeNodes:= False;
  1707.    end;
  1708. end;
  1709. procedure TfcDBCustomTreeView.FreeOldNodes;
  1710. var i: integer;
  1711. begin
  1712.   for i:= 0 to OldNodes.count-1 do TfcDBTreeNode(OldNodes[i]).Free;
  1713.   OldNodes.Clear;
  1714. end;
  1715. procedure TfcDBCustomTreeView.Expand(Node: TfcDBTreeNode);
  1716. var DataLink: TfcTreeDataLink;
  1717. begin
  1718.    if Node=nil then exit;
  1719.    if ActiveNode<>Node then MoveTo(Node); { 3/9/99 }
  1720.    try
  1721.       SkipFreeNodes:= True;
  1722.       if (Header<>nil) and (displayfields.count<=1) then
  1723.       begin
  1724.         displayfields.clear;
  1725.         displayfields.add('');
  1726.         displayfields.add('');
  1727.       end;
  1728.       DoUserExpand(Node);  { !!!Node could be invalid after executing user event }
  1729.       DataLink:= TfcTreeDataLink(FDataLinks[Node.Level]);
  1730.       DataLink:= GetChildDataLink(DataLink.DataSet);
  1731.       if (DataLink<>nil) and (DataLink.RecordCount>0) then begin
  1732.          ActiveDataSet:= DataLink.DataSet;
  1733.          if not IsChildDataSetofActive(LastVisibleDataSet) then
  1734.             LastVisibleDataSet:= ActiveDataSet;
  1735.          CheckMaxWidth:= True;
  1736.          invalidateClient;
  1737.       end;
  1738.    finally
  1739.       FreeOldNodes;
  1740.       SkipFreeNodes:= False;
  1741.    end;
  1742. end;
  1743. Procedure TfcDBCustomTreeView.MoveTo(Node: TfcDBTreeNode);
  1744. begin
  1745.    UpdateDataLinkToActive(Node);
  1746. end;
  1747. function TfcDBCustomTreeView.UpdateDataLinkToActive(Node: TfcDBTreeNode): boolean;
  1748. var TempDataLink, DataLink: TfcTreeDataLink;
  1749. begin
  1750.    result:= False;
  1751.    if (Node=nil) or (Node.DataLink=nil) then exit;
  1752.    DataLink:= Node.DataLink;
  1753.    if not Node.DataLink.Active then exit; { 4/9/99 }
  1754.    if DataLink.ActiveRecord<>Node.ActiveRecord then
  1755.    begin
  1756.       Datalink.Dataset.MoveBy(Node.ActiveRecord - DataLink.ActiveRecord);
  1757.       if ActiveDataSet<>DataLink.DataSet then
  1758.       begin
  1759.          ActiveDataSet:= DataLink.DataSet;
  1760.          TempDataLink:= GetChildDataLink(ActiveDataSet);
  1761.          if TempDataLink<>nil then ResetStartOffsets(TempDataLink.DataSet);
  1762.       end
  1763.       else begin
  1764.          TempDataLink:= GetChildDataLink(ActiveDataSet);
  1765.          if TempDataLink<>nil then ResetStartOffsets(TempDataLink.DataSet);
  1766.       end;
  1767.       invalidateClient;
  1768.       result:= True;
  1769.    end
  1770.    else begin { Same master record }
  1771.       DataLink:= GetDataLink(DataLink.DataSet);
  1772.       if (DataLink<>nil) and (ActiveDataSet<>DataLink.DataSet) then begin
  1773.          ActiveDataSet:= DataLink.DataSet;
  1774.          invalidateClient;
  1775.          result:= True;
  1776.       end
  1777.    end;
  1778. end;
  1779. procedure TfcDBCustomTreeView.MouseUp(Button: TMouseButton; Shift: TShiftState;
  1780.   X, Y: Integer);
  1781. var row: integer;
  1782.     node: TfcDBTreeNode;
  1783. begin
  1784.    inherited;
  1785.    if not HaveValidDataLinks then exit;
  1786.    MouseToRow(X, Y, Row);
  1787.    RowToNode(Row, Node);
  1788.    if Assigned(FOnMouseUp) then FOnMouseUp(self, Node, Button, Shift, X, Y);
  1789. end;
  1790. procedure TfcDBCustomTreeView.MouseDown(Button: TMouseButton; Shift: TShiftState;
  1791.   X, Y: Integer);
  1792. var Node: TfcDBTreeNode;
  1793.     Row: integer;
  1794.     DataLink: TfcTreeDataLink;
  1795.     HitTest: TfcTreeHitTests;
  1796.     ValidNode: Boolean;
  1797.     NodeLevelRect: TRect;
  1798. {   Function SameLevelShiftSelect: boolean;
  1799.    begin
  1800.       ShowMessage('not implemented yet');
  1801.       result:= True;
  1802.    end;
  1803. }
  1804. begin
  1805.    inherited;
  1806.    if not HaveValidDataLinks then exit;
  1807.    SetFocus;
  1808.    MouseToRow(X, Y, Row);
  1809.    ValidNode:= RowToNode(Row, Node);
  1810.    NodesCleared:= False; { If nodes are cleared in Paint then node is invalid }
  1811.    DataLink:= nil;
  1812.    if ValidNode then DataLink:= TfcTreeDataLink(FDataLinks[Node.Level]);
  1813.    if Assigned(FOnMouseDown) then FOnMouseDown(self, Node, Button, Shift, X, Y);
  1814.    if (ssDouble in Shift) and Assigned(FOnDblClick) then FOnDblClick(self, Node, Button, Shift, X, Y);
  1815.    if not ValidNode then exit;
  1816.    hitTest:= GetHitTestInfoAt(X,Y);
  1817.    if hitTest=[] then exit;
  1818.    NodeLevelRect:= LevelRect(Node);
  1819.    if (fchtdOnButton in hitTest) and (ssLeft in Shift) then
  1820.    begin
  1821.       if dtvoExpandButtons3D in Options then
  1822.       begin
  1823.          MouseRow:= Row;
  1824.          MouseLoop(X,Y);
  1825.          MouseRow:= -1;
  1826.          exit;
  1827.       end
  1828.    end;
  1829.    if (not NodesCleared) and (not (ssDouble in Shift)) then
  1830.       UpdateDataLinkToActive(Node);
  1831.    if (fchtdOnButton in hitTest) and (ssLeft in Shift) and (not NodesCleared) then
  1832.    begin
  1833.       if node.expanded then Collapse(Node)
  1834.       else Expand(Node);
  1835.       exit;
  1836.    end;
  1837.    if (not NodesCleared) and (fchtdOnStateIcon in hitTest) and
  1838.      MultiSelectCheckBoxNeeded(Node) then
  1839.    begin
  1840.        if IsSelectedRecord then UnselectRecord
  1841.        else SelectRecord;
  1842.    end
  1843.    else if ([fchtdOnStateIcon, fchtdOnImageIcon, fchtdOnText] * hitTest <> []) and
  1844.       (ssDouble in Shift) and (not NodesCleared) then
  1845.    begin
  1846.       DataLink:= GetChildDataLink(DataLink.DataSet);
  1847.       if (DataLink<>nil) and (DataLink.RecordCount>0) then begin
  1848.          if node.expanded then Collapse(Node)
  1849.          else Expand(Node);
  1850.       end;
  1851.       invalidateClient;
  1852.    end;
  1853.    if FMultiSelectAttributes.Enabled and
  1854.     (((dtvoRowSelect in Options) and (X >= NodeLevelRect.Left)) or  // -ksw (Added to make behavior more
  1855.     (not (dtvoRowSelect in Options) and (hitTest * [fchtdOnText] <> []))) then    // consistent in non-databound cases)
  1856.    begin
  1857.       Update; { 5/5/99 - Updates active node before calling ToggleMultiSelection }
  1858.       { 7/29/99 - Support auto-unselect property }
  1859.       if ([ssCtrl, ssShift] * Shift = []) and
  1860.          (MultiSelectAttributes.AutoUnselect) then UnselectAll;
  1861.       ToggleMultiSelection(not MultiSelectAttributes.AutoUnselect, Shift);
  1862.    end;
  1863. end;
  1864. procedure TfcDBCustomTreeView.MouseLoop(X, Y: Integer);
  1865. var ACursor: TPoint;
  1866.     Msg: TMsg;
  1867.    Function InButton(ACursorPos: TPoint): boolean;
  1868.    var sp: TPoint;
  1869.    begin
  1870.       sp:= ScreenToClient(ACursorPos);
  1871.       Result:= (fchtdOnButton in GetHitTestInfoAt(sp.x, sp.y)) and
  1872.            (sp.y>=MouseRow * RowHeight) and (sp.y<=(MouseRow+1) * RowHeight -1);
  1873.    end;
  1874.    procedure MouseLoop_MouseMove(X, Y: Integer; ACursorPos: TPoint);
  1875.    begin
  1876.      Down:= InButton(ACursorPos);
  1877.      if not Down then
  1878.      begin
  1879.         Down:= InButton(ACursorPos);
  1880.         InvalidateRow(MouseRow)
  1881.      end
  1882.      else
  1883.         InvalidateRow(MouseRow)
  1884.    end;
  1885.    procedure MouseLoop_MouseUp(X, Y: Integer; ACursorPos: TPoint);
  1886.    var
  1887.       IsMouseInControl: Boolean;
  1888.       MouseNode: TfcDBTreeNode;
  1889.    begin
  1890.      IsMouseInControl:= InButton(ACursorPos);
  1891.      Down:= False;
  1892.      InvalidateRow(MouseRow);
  1893.      if IsMouseInControl then
  1894.      begin
  1895.         if RowToNode(MouseRow, MouseNode) then
  1896.         begin
  1897.            FActiveNode:= MouseNode;
  1898.            UpdateDataLinkToActive(MouseNode);
  1899.            if ActiveNode.expanded then
  1900.               Collapse(ActiveNode)
  1901.            else
  1902.               Expand(ActiveNode);
  1903.         end
  1904.      end
  1905.    end;