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

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************************}
  2. {                                                                   }
  3. {       Almediadev Visual Component Library                         }
  4. {       BusinessSkinForm                                            }
  5. {       Version 1.98                                                }
  6. {                                                                   }
  7. {       Copyright (c) 2000-2003 Almediadev                          }
  8. {       ALL RIGHTS RESERVED                                         }
  9. {                                                                   }
  10. {       Home:  http://www.almdev.com                                }
  11. {       Support: support@almdev.com                                 }
  12. {                                                                   }
  13. {*******************************************************************}
  14. unit bsDBGrids;
  15. {$R-}
  16. {$WARNINGS OFF}
  17. {$HINTS OFF}
  18. interface
  19. uses Windows, SysUtils, Messages, Classes, Controls, Forms, StdCtrls,
  20.   Graphics, bsSkinGrids, DBCtrls, Db, Menus, ImgList, bsSkinCtrls, bsUtils,
  21.   bsSkinBoxCtrls, bsMessages;
  22. type
  23.   TbsColumnValue = (cvColor, cvWidth, cvFont, cvAlignment, cvReadOnly, cvTitleColor,
  24.     cvTitleCaption, cvTitleAlignment, cvTitleFont, cvImeMode, cvImeName);
  25.   TbsColumnValues = set of TbsColumnValue;
  26. const
  27.   ColumnTitleValues = [cvTitleColor..cvTitleFont];
  28.   cm_DeferLayout = WM_USER + 100;
  29. type
  30.   TbsColumn = class;
  31.   TbsSkinCustomDBGrid = class;
  32.   TbsColumnTitle = class(TPersistent)
  33.   private
  34.     FColumn: TbsColumn;
  35.     FCaption: string;
  36.     FFont: TFont;
  37.     FColor: TColor;
  38.     FAlignment: TAlignment;
  39.     procedure FontChanged(Sender: TObject);
  40.     function GetAlignment: TAlignment;
  41.     function GetColor: TColor;
  42.     function GetCaption: string;
  43.     function GetFont: TFont;
  44.     function IsAlignmentStored: Boolean;
  45.     function IsColorStored: Boolean;
  46.     function IsFontStored: Boolean;
  47.     function IsCaptionStored: Boolean;
  48.     procedure SetAlignment(Value: TAlignment);
  49.     procedure SetColor(Value: TColor);
  50.     procedure SetFont(Value: TFont);
  51.     procedure SetCaption(const Value: string); virtual;
  52.   protected
  53.     procedure RefreshDefaultFont;
  54.   public
  55.     constructor Create(Column: TbsColumn);
  56.     destructor Destroy; override;
  57.     procedure Assign(Source: TPersistent); override;
  58.     function DefaultAlignment: TAlignment;
  59.     function DefaultColor: TColor;
  60.     function DefaultFont: TFont;
  61.     function DefaultCaption: string;
  62.     procedure RestoreDefaults; virtual;
  63.     property Column: TbsColumn read FColumn;
  64.   published
  65.     property Alignment: TAlignment read GetAlignment write SetAlignment
  66.       stored IsAlignmentStored;
  67.     property Caption: string read GetCaption write SetCaption stored IsCaptionStored;
  68.     property Color: TColor read GetColor write SetColor stored IsColorStored;
  69.     property Font: TFont read GetFont write SetFont stored IsFontStored;
  70.   end;
  71.   TbsColumnButtonStyle = (cbsAuto, cbsEllipsis, cbsNone);
  72.   TbsColumn = class(TCollectionItem)
  73.   private
  74.     FField: TField;
  75.     FFieldName: string;
  76.     FColor: TColor;
  77.     FWidth: Integer;
  78.     FTitle: TbsColumnTitle;
  79.     FFont: TFont;
  80.     FImeMode: TImeMode;
  81.     FImeName: TImeName;
  82.     FPickList: TStrings;
  83.     FPopupMenu: TPopupMenu;
  84.     FDropDownRows: Cardinal;
  85.     FButtonStyle: TbsColumnButtonStyle;
  86.     FAlignment: TAlignment;
  87.     FReadonly: Boolean;
  88.     FAssignedValues: TbsColumnValues;
  89.     FVisible: Boolean;
  90.     FExpanded: Boolean;
  91.     FStored: Boolean;
  92.     procedure FontChanged(Sender: TObject);
  93.     function  GetAlignment: TAlignment;
  94.     function  GetColor: TColor;
  95.     function  GetExpanded: Boolean;
  96.     function  GetField: TField;
  97.     function  GetFont: TFont;
  98.     function  GetImeMode: TImeMode;
  99.     function  GetImeName: TImeName;
  100.     function  GetParentColumn: TbsColumn;
  101.     function  GetPickList: TStrings;
  102.     function  GetReadOnly: Boolean;
  103.     function  GetShowing: Boolean;
  104.     function  GetWidth: Integer;
  105.     function  GetVisible: Boolean;
  106.     function  IsAlignmentStored: Boolean;
  107.     function  IsColorStored: Boolean;
  108.     function  IsFontStored: Boolean;
  109.     function  IsImeModeStored: Boolean;
  110.     function  IsImeNameStored: Boolean;
  111.     function  IsReadOnlyStored: Boolean;
  112.     function  IsWidthStored: Boolean;
  113.     procedure SetAlignment(Value: TAlignment); virtual;
  114.     procedure SetButtonStyle(Value: TbsColumnButtonStyle);
  115.     procedure SetColor(Value: TColor);
  116.     procedure SetExpanded(Value: Boolean);
  117.     procedure SetField(Value: TField); virtual;
  118.     procedure SetFieldName(const Value: String);
  119.     procedure SetFont(Value: TFont);
  120.     procedure SetImeMode(Value: TImeMode); virtual;
  121.     procedure SetImeName(Value: TImeName); virtual;
  122.     procedure SetPickList(Value: TStrings);
  123.     procedure SetPopupMenu(Value: TPopupMenu);
  124.     procedure SetReadOnly(Value: Boolean); virtual;
  125.     procedure SetTitle(Value: TbsColumnTitle);
  126.     procedure SetWidth(Value: Integer); virtual;
  127.     procedure SetVisible(Value: Boolean);
  128.     function GetExpandable: Boolean;
  129.   protected
  130.     function  CreateTitle: TbsColumnTitle; virtual;
  131.     function  GetGrid: TbsSkinCustomDBGrid;
  132.     function GetDisplayName: string; override;
  133.     procedure RefreshDefaultFont;
  134.     procedure SetIndex(Value: Integer); override;
  135.     property IsStored: Boolean read FStored write FStored default True;
  136.   public
  137.     constructor Create(Collection: TCollection); override;
  138.     destructor Destroy; override;
  139.     procedure Assign(Source: TPersistent); override;
  140.     function  DefaultAlignment: TAlignment;
  141.     function  DefaultColor: TColor;
  142.     function  DefaultFont: TFont;
  143.     function  DefaultImeMode: TImeMode;
  144.     function  DefaultImeName: TImeName;
  145.     function  DefaultReadOnly: Boolean;
  146.     function  DefaultWidth: Integer;
  147.     function  Depth: Integer;
  148.     procedure RestoreDefaults; virtual;
  149.     property  Grid: TbsSkinCustomDBGrid read GetGrid;
  150.     property  AssignedValues: TbsColumnValues read FAssignedValues;
  151.     property  Expandable: Boolean read GetExpandable;
  152.     property  Field: TField read GetField write SetField;
  153.     property  ParentColumn: TbsColumn read GetParentColumn;
  154.     property  Showing: Boolean read GetShowing;
  155.   published
  156.     property  Alignment: TAlignment read GetAlignment write SetAlignment
  157.       stored IsAlignmentStored;
  158.     property  ButtonStyle: TbsColumnButtonStyle read FButtonStyle write SetButtonStyle
  159.       default cbsAuto;
  160.     property  Color: TColor read GetColor write SetColor stored IsColorStored;
  161.     property  DropDownRows: Cardinal read FDropDownRows write FDropDownRows default 7;
  162.     property  Expanded: Boolean read GetExpanded write SetExpanded default True;
  163.     property  FieldName: String read FFieldName write SetFieldName;
  164.     property  Font: TFont read GetFont write SetFont stored IsFontStored;
  165.     property  ImeMode: TImeMode read GetImeMode write SetImeMode stored IsImeModeStored;
  166.     property  ImeName: TImeName read GetImeName write SetImeName stored IsImeNameStored;
  167.     property  PickList: TStrings read GetPickList write SetPickList;
  168.     property  PopupMenu: TPopupMenu read FPopupMenu write SetPopupMenu;
  169.     property  ReadOnly: Boolean read GetReadOnly write SetReadOnly
  170.       stored IsReadOnlyStored;
  171.     property  Title: TbsColumnTitle read FTitle write SetTitle;
  172.     property  Width: Integer read GetWidth write SetWidth stored IsWidthStored;
  173.     property  Visible: Boolean read GetVisible write SetVisible;
  174.   end;
  175.   TbsColumnClass = class of TbsColumn;
  176.   TbsDBGridColumnsState = (csDefault, csCustomized);
  177.   TbsDBGridColumns = class(TCollection)
  178.   private
  179.     FGrid: TbsSkinCustomDBGrid;
  180.     function GetColumn(Index: Integer): TbsColumn;
  181.     function InternalAdd: TbsColumn;
  182.     procedure SetColumn(Index: Integer; Value: TbsColumn);
  183.     procedure SetState(NewState: TbsDBGridColumnsState);
  184.     function GetState: TbsDBGridColumnsState;
  185.   protected
  186.     function GetOwner: TPersistent; override;
  187.     procedure Update(Item: TCollectionItem); override;
  188.   public
  189.     constructor Create(Grid: TbsSkinCustomDBGrid; ColumnClass: TbsColumnClass);
  190.     function  Add: TbsColumn;
  191.     procedure LoadFromFile(const Filename: string);
  192.     procedure LoadFromStream(S: TStream);
  193.     procedure RestoreDefaults;
  194.     procedure RebuildColumns;
  195.     procedure SaveToFile(const Filename: string);
  196.     procedure SaveToStream(S: TStream);
  197.     property State: TbsDBGridColumnsState read GetState write SetState;
  198.     property Grid: TbsSkinCustomDBGrid read FGrid;
  199.     property Items[Index: Integer]: TbsColumn read GetColumn write SetColumn; default;
  200.   end;
  201.   TbsGridDataLink = class(TDataLink)
  202.   private
  203.     FGrid: TbsSkinCustomDBGrid;
  204.     FFieldCount: Integer;
  205.     FFieldMap: array of Integer;
  206.     FModified: Boolean;
  207.     FInUpdateData: Boolean;
  208.     FSparseMap: Boolean;
  209.     function GetDefaultFields: Boolean;
  210.     function GetFields(I: Integer): TField;
  211.   protected
  212.     procedure ActiveChanged; override;
  213.     procedure BuildAggMap;
  214.     procedure DataSetChanged; override;
  215.     procedure DataSetScrolled(Distance: Integer); override;
  216.     procedure FocusControl(Field: TFieldRef); override;
  217.     procedure EditingChanged; override;
  218.     function IsAggRow(Value: Integer): Boolean; virtual;
  219.     procedure LayoutChanged; override;
  220.     procedure RecordChanged(Field: TField); override;
  221.     procedure UpdateData; override;
  222.     function  GetMappedIndex(ColIndex: Integer): Integer;
  223.   public
  224.     constructor Create(AGrid: TbsSkinCustomDBGrid);
  225.     destructor Destroy; override;
  226.     function AddMapping(const FieldName: string): Boolean;
  227.     procedure ClearMapping;
  228.     procedure Modified;
  229.     procedure Reset;
  230.     property DefaultFields: Boolean read GetDefaultFields;
  231.     property FieldCount: Integer read FFieldCount;
  232.     property Fields[I: Integer]: TField read GetFields;
  233.     property SparseMap: Boolean read FSparseMap write FSparseMap;
  234.   end;
  235.   TbsBookmarkList = class
  236.   private
  237.     FList: TStringList;
  238.     FGrid: TbsSkinCustomDBGrid;
  239.     FCache: TBookmarkStr;
  240.     FCacheIndex: Integer;
  241.     FCacheFind: Boolean;
  242.     FLinkActive: Boolean;
  243.     function GetCount: Integer;
  244.     function GetCurrentRowSelected: Boolean;
  245.     function GetItem(Index: Integer): TBookmarkStr;
  246.     procedure SetCurrentRowSelected(Value: Boolean);
  247.     procedure StringsChanged(Sender: TObject);
  248.   protected
  249.     function CurrentRow: TBookmarkStr;
  250.     function Compare(const Item1, Item2: TBookmarkStr): Integer;
  251.     procedure LinkActive(Value: Boolean);
  252.   public
  253.     constructor Create(AGrid: TbsSkinCustomDBGrid);
  254.     destructor Destroy; override;
  255.     procedure Clear;           // free all bookmarks
  256.     procedure Delete;          // delete all selected rows from dataset
  257.     function  Find(const Item: TBookmarkStr; var Index: Integer): Boolean;
  258.     function  IndexOf(const Item: TBookmarkStr): Integer;
  259.     function  Refresh: Boolean;// drop orphaned bookmarks; True = orphans found
  260.     property Count: Integer read GetCount;
  261.     property CurrentRowSelected: Boolean read GetCurrentRowSelected
  262.       write SetCurrentRowSelected;
  263.     property Items[Index: Integer]: TBookmarkStr read GetItem; default;
  264.   end;
  265.   TbsDBGridOption = (dgEditing, dgAlwaysShowEditor, dgTitles, dgIndicator,
  266.     dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect,
  267.     dgAlwaysShowSelection, dgConfirmDelete, dgCancelOnExit, dgMultiSelect);
  268.   TbsDBGridOptions = set of TbsDBGridOption;
  269.   TDrawDataCellEvent = procedure (Sender: TObject; const Rect: TRect; Field: TField;
  270.     State: TGridDrawState) of object;
  271.   TDrawColumnCellEvent = procedure (Sender: TObject; const Rect: TRect;
  272.     DataCol: Integer; Column: TbsColumn; State: TGridDrawState) of object;
  273.   TDBGridClickEvent = procedure (Column: TbsColumn) of object;
  274.   TbsSkinCustomDBGrid = class(TbsSkinCustomGrid)
  275.   private
  276.     FMouseWheelSupport: Boolean;
  277.     FSkinMessage: TbsSkinMessage;
  278.     FPickListBoxSkinDataName: String;
  279.     FPickListBoxCaptionMode: Boolean;
  280.     FIndicators: TImageList;
  281.     FTitleFont: TFont;
  282.     FReadOnly: Boolean;
  283.     FOriginalImeName: TImeName;
  284.     FOriginalImeMode: TImeMode;
  285.     FUserChange: Boolean;
  286.     FIsESCKey: Boolean;
  287.     FLayoutFromDataset: Boolean;
  288.     FOptions: TbsDBGridOptions;
  289.     FTitleOffset, FIndicatorOffset: Byte;
  290.     FUpdateLock: Byte;
  291.     FLayoutLock: Byte;
  292.     FInColExit: Boolean;
  293.     FDefaultDrawing: Boolean;
  294.     FSelfChangingTitleFont: Boolean;
  295.     FSelecting: Boolean;
  296.     FSelRow: Integer;
  297.     FDataLink: TbsGridDataLink;
  298.     FOnColEnter: TNotifyEvent;
  299.     FOnColExit: TNotifyEvent;
  300.     FOnDrawDataCell: TDrawDataCellEvent;
  301.     FOnDrawColumnCell: TDrawColumnCellEvent;
  302.     FEditText: string;
  303.     FColumns: TbsDBGridColumns;
  304.     FVisibleColumns: TList;
  305.     FBookmarks: TbsBookmarkList;
  306.     FSelectionAnchor: TBookmarkStr;
  307.     FOnEditButtonClick: TNotifyEvent;
  308.     FOnColumnMoved: TMovedEvent;
  309.     FOnCellClick: TDBGridClickEvent;
  310.     FOnTitleClick: TDBGridClickEvent;
  311.     FDragCol: TbsColumn;
  312.     function AcquireFocus: Boolean;
  313.     procedure DataChanged;
  314.     procedure EditingChanged;
  315.     function GetDataSource: TDataSource;
  316.     function GetFieldCount: Integer;
  317.     function GetFields(FieldIndex: Integer): TField;
  318.     function GetSelectedField: TField;
  319.     function GetSelectedIndex: Integer;
  320.     procedure InternalLayout;
  321.     procedure MoveCol(RawCol, Direction: Integer);
  322.     function PtInExpandButton(X,Y: Integer; var MasterCol: TbsColumn): Boolean;
  323.     procedure ReadColumns(Reader: TReader);
  324.     procedure RecordChanged(Field: TField);
  325.     procedure SetIme;
  326.     procedure SetColumns(Value: TbsDBGridColumns);
  327.     procedure SetDataSource(Value: TDataSource);
  328.     procedure SetOptions(Value: TbsDBGridOptions);
  329.     procedure SetSelectedField(Value: TField);
  330.     procedure SetSelectedIndex(Value: Integer);
  331.     procedure SetTitleFont(Value: TFont);
  332.     procedure TitleFontChanged(Sender: TObject);
  333.     procedure UpdateData;
  334.     procedure UpdateActive;
  335.     procedure UpdateIme;
  336.     procedure UpdateScrollBar;
  337.     procedure UpdateRowCount;
  338.     procedure WriteColumns(Writer: TWriter);
  339.     procedure CMBiDiModeChanged(var Message: TMessage); message CM_BIDIMODECHANGED;
  340.     procedure CMExit(var Message: TMessage); message CM_EXIT;
  341.     procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  342.     procedure CMParentFontChanged(var Message: TMessage); message CM_PARENTFONTCHANGED;
  343.     procedure CMDeferLayout(var Message); message cm_DeferLayout;
  344.     procedure CMDesignHitTest(var Msg: TCMDesignHitTest); message CM_DESIGNHITTEST;
  345.     procedure WMSetCursor(var Msg: TWMSetCursor); message WM_SETCURSOR;
  346.     procedure WMSize(var Message: TWMSize); message WM_SIZE;
  347.     procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  348.     procedure WMIMEStartComp(var Message: TMessage); message WM_IME_STARTCOMPOSITION;
  349.     procedure WMSetFocus(var Message: TWMSetFocus); message WM_SetFOCUS;
  350.     procedure WMKillFocus(var Message: TMessage); message WM_KillFocus;
  351.   protected
  352.     FUpdateFields: Boolean;
  353.     FAcquireFocus: Boolean;
  354.     procedure PickListBoxOnCheckButtonClick(Sender: TObject);
  355.     procedure SetHScrollBar(Value: TbsSkinScrollBar); override;
  356.     procedure UpdateScrollPos(UpDateVert: Boolean); override;
  357.     procedure UpdateScrollRange(UpDateVert: Boolean); override;
  358.     function  RawToDataColumn(ACol: Integer): Integer;
  359.     function  DataToRawColumn(ACol: Integer): Integer;
  360.     function  AcquireLayoutLock: Boolean;
  361.     procedure BeginLayout;
  362.     procedure BeginUpdate;
  363.     procedure CalcSizingState(X, Y: Integer; var State: TbsGridState;
  364.       var Index: Longint; var SizingPos, SizingOfs: Integer;
  365.       var FixedInfo: TbsGridDrawInfo); override;
  366.     procedure CancelLayout;
  367.     function  CanEditAcceptKey(Key: Char): Boolean; override;
  368.     function  CanEditModify: Boolean; override;
  369.     function  CanEditShow: Boolean; override;
  370.     procedure CellClick(Column: TbsColumn); dynamic;
  371.     procedure ColumnMoved(FromIndex, ToIndex: Longint); override;
  372.     function CalcTitleRect(Col: TbsColumn; ARow: Integer;
  373.       var MasterCol: TbsColumn): TRect;
  374.     function ColumnAtDepth(Col: TbsColumn; ADepth: Integer): TbsColumn;
  375.     procedure ColEnter; dynamic;
  376.     procedure ColExit; dynamic;
  377.     procedure ColWidthsChanged; override;
  378.     function  CreateColumns: TbsDBGridColumns; dynamic;
  379.     function  CreateEditor: TbsSkinInplaceEdit; override;
  380.     procedure CreateWnd; override;
  381.     procedure DeferLayout;
  382.     procedure DefineFieldMap; virtual;
  383.     procedure DefineProperties(Filer: TFiler); override;
  384.     procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
  385.     procedure DrawSkinCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
  386.     procedure DrawDefaultCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
  387.     procedure DrawDataCell(const Rect: TRect; Field: TField;
  388.       State: TGridDrawState); dynamic; { obsolete }
  389.     procedure DrawColumnCell(const Rect: TRect; DataCol: Integer;
  390.       Column: TbsColumn; State: TGridDrawState); dynamic;
  391.     procedure EditButtonClick; dynamic;
  392.     procedure EndLayout;
  393.     procedure EndUpdate;
  394.     function  GetColField(DataCol: Integer): TField;
  395.     function  GetEditLimit: Integer; override;
  396.     function  GetEditMask(ACol, ARow: Longint): string; override;
  397.     function  GetEditText(ACol, ARow: Longint): string; override;
  398.     function  GetFieldValue(ACol: Integer): string;
  399.     function  HighlightCell(DataCol, DataRow: Integer; const Value: string;
  400.       AState: TGridDrawState): Boolean; virtual;
  401.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  402.     procedure KeyPress(var Key: Char); override;
  403.     procedure InvalidateTitles;
  404.     procedure LayoutChanged; virtual;
  405.     procedure LinkActive(Value: Boolean); virtual;
  406.     procedure Loaded; override;
  407.     procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;
  408.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  409.       X, Y: Integer); override;
  410.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
  411.       X, Y: Integer); override;
  412.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  413.     procedure Scroll(Distance: Integer); virtual;
  414.     procedure SetColumnAttributes; virtual;
  415.     procedure SetEditText(ACol, ARow: Longint; const Value: string); override;
  416.     function  StoreColumns: Boolean;
  417.     procedure TimedScroll(Direction: TGridScrollDirection); override;
  418.     procedure TitleClick(Column: TbsColumn); dynamic;
  419.     procedure TopLeftChanged; override;
  420.     function UseRightToLeftAlignmentForField(const AField: TField;
  421.       Alignment: TAlignment): Boolean;
  422.     function BeginColumnDrag(var Origin, Destination: Integer;
  423.       const MousePt: TPoint): Boolean; override;
  424.     function CheckColumnDrag(var Origin, Destination: Integer;
  425.       const MousePt: TPoint): Boolean; override;
  426.     function EndColumnDrag(var Origin, Destination: Integer;
  427.       const MousePt: TPoint): Boolean; override;
  428.     property Columns: TbsDBGridColumns read FColumns write SetColumns;
  429.     property DefaultDrawing: Boolean read FDefaultDrawing write FDefaultDrawing default True;
  430.     property DataLink: TbsGridDataLink read FDataLink;
  431.     property IndicatorOffset: Byte read FIndicatorOffset;
  432.     property LayoutLock: Byte read FLayoutLock;
  433.     property Options: TbsDBGridOptions read FOptions write SetOptions
  434.       default [dgEditing, dgTitles, dgIndicator, dgColumnResize, dgColLines,
  435.       dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit];
  436.     property ParentColor default False;
  437.     property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
  438.     property SelectedRows: TbsBookmarkList read FBookmarks;
  439.     property TitleFont: TFont read FTitleFont write SetTitleFont;
  440.     property UpdateLock: Byte read FUpdateLock;
  441.     property OnColEnter: TNotifyEvent read FOnColEnter write FOnColEnter;
  442.     property OnColExit: TNotifyEvent read FOnColExit write FOnColExit;
  443.     property OnDrawDataCell: TDrawDataCellEvent read FOnDrawDataCell
  444.       write FOnDrawDataCell; { obsolete }
  445.     property OnDrawColumnCell: TDrawColumnCellEvent read FOnDrawColumnCell
  446.       write FOnDrawColumnCell;
  447.     property OnEditButtonClick: TNotifyEvent read FOnEditButtonClick
  448.       write FOnEditButtonClick;
  449.     property OnColumnMoved: TMovedEvent read FOnColumnMoved write FOnColumnMoved;
  450.     property OnCellClick: TDBGridClickEvent read FOnCellClick write FOnCellClick;
  451.     property OnTitleClick: TDBGridClickEvent read FOnTitleClick write FOnTitleClick;
  452.   public
  453.     constructor Create(AOwner: TComponent); override;
  454.     destructor Destroy; override;
  455.     procedure ChangeSkinData; override;
  456.     procedure DefaultDrawDataCell(const Rect: TRect; Field: TField;
  457.       State: TGridDrawState); { obsolete }
  458.     procedure DefaultDrawColumnCell(const Rect: TRect; DataCol: Integer;
  459.       Column: TbsColumn; State: TGridDrawState);
  460.     procedure DefaultHandler(var Msg); override;
  461.     function ExecuteAction(Action: TBasicAction): Boolean; override;
  462.     procedure ShowPopupEditor(Column: TbsColumn; X: Integer = Low(Integer);
  463.       Y: Integer = Low(Integer)); dynamic;
  464.     function UpdateAction(Action: TBasicAction): Boolean; override;
  465.     function ValidFieldIndex(FieldIndex: Integer): Boolean;
  466.     property SkinMessage: TbsSkinMessage read FSkinMessage write FSkinMessage;
  467.     property EditorMode;
  468.     property FieldCount: Integer read GetFieldCount;
  469.     property Fields[FieldIndex: Integer]: TField read GetFields;
  470.     property SelectedField: TField read GetSelectedField write SetSelectedField;
  471.     property SelectedIndex: Integer read GetSelectedIndex write SetSelectedIndex;
  472.     property DataSource: TDataSource read GetDataSource write SetDataSource;
  473.     property PickListBoxSkinDataName: String read FPickListBoxSkinDataName
  474.                                          write FPickListBoxSkinDataName;
  475.     property PickListBoxCaptionMode: Boolean read FPickListBoxCaptionMode
  476.                                          write FPickListBoxCaptionMode;
  477.     property MouseWheelSupport: Boolean
  478.       read FMouseWheelSupport write FMouseWheelSupport;
  479.   end;
  480.   TbsSkinDBGrid = class(TbsSkinCustomDBGrid)
  481.   public
  482.     property Canvas;
  483.     property SelectedRows;
  484.   published
  485.     property MouseWheelSupport;
  486.     property SkinMessage;
  487.     property PickListBoxSkinDataName;
  488.     property PickListBoxCaptionMode;
  489.     property Align;
  490.     property Anchors;
  491.     property BiDiMode;
  492.     property BorderStyle;
  493.     property Color;
  494.     property Columns stored False; 
  495.     property Constraints;
  496.     property Ctl3D;
  497.     property DataSource;
  498.     property DefaultDrawing;
  499.     property DragCursor;
  500.     property DragKind;
  501.     property DragMode;
  502.     property Enabled;
  503.     property FixedColor;
  504.     property Font;
  505.     property ImeMode;
  506.     property ImeName;
  507.     property Options;
  508.     property ParentBiDiMode;
  509.     property ParentColor;
  510.     property ParentCtl3D;
  511.     property ParentFont;
  512.     property ParentShowHint;
  513.     property PopupMenu;
  514.     property ReadOnly;
  515.     property ShowHint;
  516.     property TabOrder;
  517.     property TabStop;
  518.     property TitleFont;
  519.     property Visible;
  520.     property OnCellClick;
  521.     property OnColEnter;
  522.     property OnColExit;
  523.     property OnColumnMoved;
  524.     property OnDrawDataCell;  { obsolete }
  525.     property OnDrawColumnCell;
  526.     property OnDblClick;
  527.     property OnDragDrop;
  528.     property OnDragOver;
  529.     property OnEditButtonClick;
  530.     property OnEndDock;
  531.     property OnEndDrag;
  532.     property OnEnter;
  533.     property OnExit;
  534.     property OnKeyDown;
  535.     property OnKeyPress;
  536.     property OnKeyUp;
  537.     property OnMouseDown;
  538.     property OnMouseMove;
  539.     property OnMouseUp;
  540.     property OnStartDock;
  541.     property OnStartDrag;
  542.     property OnTitleClick;
  543.   end;
  544. const
  545.   IndicatorWidth = 11;
  546. implementation
  547. uses Math, DBConsts, Dialogs {$IFDEF  VER140}, Variants{$ENDIF}
  548.      {$IFDEF  VER150}, Variants{$ENDIF};
  549. {$R BSDBGRIDS.RES}
  550. const
  551.   bmArrow = 'BSDBGARROW';
  552.   bmEdit = 'BSDBEDIT';
  553.   bmInsert = 'BSDBINSERT';
  554.   bmMultiDot = 'BSDBMULTIDOT';
  555.   bmMultiArrow = 'BSDBMULTIARROW';
  556.   MaxMapSize = (MaxInt div 2) div SizeOf(Integer);  { 250 million }
  557. { Error reporting }
  558. procedure RaiseGridError(const S: string);
  559. begin
  560. //  raise EInvalidGridOperation.Create(S);
  561. end;
  562. procedure KillMessage(Wnd: HWnd; Msg: Integer);
  563. var
  564.   M: TMsg;
  565. begin
  566.   M.Message := 0;
  567.   if PeekMessage(M, Wnd, Msg, Msg, pm_Remove) and (M.Message = WM_QUIT) then
  568.     PostQuitMessage(M.wparam);
  569. end;
  570. { TDBGridInplaceEdit }
  571. type
  572.   TEditStyle = (esSimple, esEllipsis, esPickList, esDataList);
  573.   TbsDBPopupListbox = class;
  574.   TDBGridInplaceEdit = class(TbsSkinInplaceEdit)
  575.   private
  576.     FButtonWidth: Integer;
  577.     FDataList: TDBLookupListBox;
  578.     FPickList: TbsDBPopupListbox;
  579.     FActiveList: TWinControl;
  580.     FLookupSource: TDatasource;
  581.     FEditStyle: TEditStyle;
  582.     FListVisible: Boolean;
  583.     FTracking: Boolean;
  584.     FPressed: Boolean;
  585.     procedure ListMouseUp(Sender: TObject; Button: TMouseButton;
  586.       Shift: TShiftState; X, Y: Integer);
  587.     procedure SetEditStyle(Value: TEditStyle);
  588.     procedure StopTracking;
  589.     procedure TrackButton(X,Y: Integer);
  590.     procedure CMCancelMode(var Message: TCMCancelMode); message CM_CancelMode;
  591.     procedure WMCancelMode(var Message: TMessage); message WM_CancelMode;
  592.     procedure WMKillFocus(var Message: TMessage); message WM_KillFocus;
  593.     procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message wm_LButtonDblClk;
  594.     procedure WMPaint(var Message: TWMPaint); message wm_Paint;
  595.     procedure WMSetCursor(var Message: TWMSetCursor); message WM_SetCursor;
  596.     function OverButton(const P: TPoint): Boolean;
  597.     function ButtonRect: TRect;
  598.   protected
  599.     procedure BoundsChanged; override;
  600.     procedure DoDropDownKeys(var Key: Word; Shift: TShiftState);
  601.     procedure DropDown;
  602.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  603.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  604.       X, Y: Integer); override;
  605.     procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  606.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
  607.       X, Y: Integer); override;
  608.     procedure PaintWindow(DC: HDC); override;
  609.     procedure UpdateContents; override;
  610.     procedure WndProc(var Message: TMessage); override;
  611.     property  EditStyle: TEditStyle read FEditStyle write SetEditStyle;
  612.     property  ActiveList: TWinControl read FActiveList write FActiveList;
  613.     property  DataList: TDBLookupListBox read FDataList;
  614.     property  PickList: TbsDBPopupListbox read FPickList;
  615.   public
  616.     procedure CloseUp(Accept: Boolean);
  617.     constructor Create(Owner: TComponent); override;
  618.   end;
  619. { TbsDBPopupListbox }
  620.   TbsDBPopupListbox = class(TbsPopupListBox)
  621.   protected
  622.     FListBoxWindowProc: TWndMethod;
  623.     procedure ListBoxWindowProcHook(var Message: TMessage);
  624.     procedure ListBoxMouseMove(Sender: TObject; Shift: TShiftState;
  625.               X, Y: Integer);
  626.   public
  627.     constructor Create(Owner: TComponent); override;
  628.     destructor Destroy; override;
  629.   end;
  630. constructor TbsDBPopupListbox.Create(Owner: TComponent);
  631. begin
  632.   inherited;
  633.   FListBoxWindowProc := ListBox.WindowProc;
  634.   ListBox.WindowProc := ListBoxWindowProcHook;
  635.   ListBox.OnMouseMove := ListBoxMouseMove;
  636. end;
  637. destructor TbsDBPopupListbox.Destroy;
  638. begin
  639.   inherited;
  640. end;
  641. procedure TbsDBPopupListbox.ListBoxWindowProcHook(var Message: TMessage);
  642. var
  643.   FOld: Boolean;
  644. begin
  645.   FOld := True;
  646.   case Message.Msg of
  647.      WM_LBUTTONUP:
  648.        begin
  649.          TDBGridInPlaceEdit(Owner).CloseUp(True);
  650.        end;
  651.      WM_RBUTTONDOWN, WM_RBUTTONUP,
  652.      WM_MBUTTONDOWN, WM_MBUTTONUP,
  653.      WM_LBUTTONDOWN:
  654.        begin
  655.          FOLd := False;
  656.        end;
  657.      WM_MOUSEACTIVATE:
  658.       begin
  659.         Message.Result := MA_NOACTIVATE;
  660.       end;
  661.   end;
  662.   if FOld then FListBoxWindowProc(Message);
  663. end;
  664. procedure TbsDBPopupListbox.ListBoxMouseMove(Sender: TObject; Shift: TShiftState;
  665.                            X, Y: Integer);
  666. var
  667.   Index: Integer;
  668. begin
  669.   Index := ListBox.ItemAtPos(Point (X, Y), True);
  670.   if (Index >= 0) and (Index < Items.Count)
  671.   then
  672.     ItemIndex := Index;
  673. end;
  674. constructor TDBGridInplaceEdit.Create(Owner: TComponent);
  675. begin
  676.   inherited Create(Owner);
  677.   FLookupSource := TDataSource.Create(Self);
  678.   FButtonWidth := GetSystemMetrics(SM_CXVSCROLL);
  679.   FEditStyle := esSimple;
  680. end;
  681. procedure TDBGridInplaceEdit.BoundsChanged;
  682. var
  683.   R: TRect;
  684. begin
  685.   Windows.SetRect(R, 2, 2, Width - 2, Height);
  686.   if FEditStyle <> esSimple then
  687.     if not TbsSkinCustomDBGrid(Owner).UseRightToLeftAlignment then
  688.       Dec(R.Right, FButtonWidth)
  689.     else
  690.       Inc(R.Left, FButtonWidth - 2);
  691.   SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@R));
  692.   SendMessage(Handle, EM_SCROLLCARET, 0, 0);
  693.   if SysLocale.FarEast then
  694.     SetImeCompositionWindow(Font, R.Left, R.Top);
  695. end;
  696. procedure TDBGridInplaceEdit.CloseUp(Accept: Boolean);
  697. var
  698.   MasterField: TField;
  699.   ListValue: Variant;
  700. begin
  701.   if FListVisible then
  702.   begin
  703.     if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
  704.     if FActiveList = FDataList then
  705.       ListValue := FDataList.KeyValue
  706.     else
  707.       if FPickList.ItemIndex <> -1 then
  708.         ListValue := FPickList.Items[FPicklist.ItemIndex];
  709.     TbsDBPopupListBox(FActiveList).Hide;
  710.     FActiveList.Visible := False;
  711.     FListVisible := False;
  712.     if Assigned(FDataList) then
  713.       FDataList.ListSource := nil;
  714.     FLookupSource.Dataset := nil;
  715.     Invalidate;
  716.     if Accept then
  717.       if FActiveList = FDataList then
  718.         with TbsSkinCustomDBGrid(Grid), Columns[SelectedIndex].Field do
  719.         begin
  720.           MasterField := DataSet.FieldByName(KeyFields);
  721.           if MasterField.CanModify and FDataLink.Edit then
  722.             MasterField.Value := ListValue;
  723.         end
  724.       else
  725.         if (not VarIsNull(ListValue)) and EditCanModify then
  726.           with TbsSkinCustomDBGrid(Grid), Columns[SelectedIndex].Field do
  727.             Text := ListValue;
  728.   end;
  729. end;
  730. procedure TDBGridInplaceEdit.DoDropDownKeys(var Key: Word; Shift: TShiftState);
  731. begin
  732.   case Key of
  733.     VK_UP, VK_DOWN:
  734.       if ssAlt in Shift then
  735.       begin
  736.         if FListVisible then CloseUp(True) else DropDown;
  737.         Key := 0;
  738.       end;
  739.     VK_RETURN, VK_ESCAPE:
  740.       if FListVisible and not (ssAlt in Shift) then
  741.       begin
  742.         CloseUp(Key = VK_RETURN);
  743.         Key := 0;
  744.       end;
  745.   end;
  746. end;
  747. procedure TDBGridInplaceEdit.DropDown;
  748. var
  749.   P: TPoint;
  750.   I,J,Y: Integer;
  751.   Column: TbsColumn;
  752. begin
  753.   if not FListVisible and Assigned(FActiveList) then
  754.   begin
  755.     FActiveList.Width := Width;
  756.     with TbsSkinCustomDBGrid(Grid) do
  757.       Column := Columns[SelectedIndex];
  758.     if FActiveList = FDataList then
  759.     with Column.Field do
  760.     begin
  761.       FDataList.Color := Color;
  762.       FDataList.Font := Font;
  763.       FDataList.RowCount := Column.DropDownRows;
  764.       FLookupSource.DataSet := LookupDataSet;
  765.       FDataList.KeyField := LookupKeyFields;
  766.       FDataList.ListField := LookupResultField;
  767.       FDataList.ListSource := FLookupSource;
  768.       FDataList.KeyValue := DataSet.FieldByName(KeyFields).Value;
  769.    end
  770.     else
  771.     begin
  772.       //
  773.       with FPickList do
  774.       begin
  775.         SkinData := Grid.SkinData;
  776.         SkinDataName := TbsSkinCustomDbGrid(Grid).PickListBoxSkinDataName;
  777.         CaptionMode := TbsSkinCustomDbGrid(Grid).PickListBoxCaptionMode;
  778.         if CaptionMode
  779.         then
  780.         begin
  781.           Caption := Column.Title.Caption;
  782.           OnCheckButtonClick := TbsSkinCustomDbGrid(Grid).PickListBoxOnCheckButtonClick;
  783.         end;
  784.       end;  
  785.       FPickList.ChangeSkinData;
  786.       //
  787.       if FPickList.FIndex = -1
  788.       then
  789.         begin
  790.           FPickList.Font := Font;
  791.         end;
  792.       FPickList.Items := Column.Picklist;
  793.       if FPickList.Items.Count >= Integer(Column.DropDownRows) then
  794.         FPickList.Height := Integer(Column.DropDownRows) * FPickList.ListBox.ItemHeight + 4
  795.       else
  796.         FPickList.Height := FPickList.Items.Count * FPickList.ListBox.ItemHeight + 4;
  797.       if Column.Field.IsNull then
  798.         FPickList.ItemIndex := -1
  799.       else
  800.         FPickList.ItemIndex := FPickList.Items.IndexOf(Column.Field.Text);
  801.       J := FPickList.ClientWidth;
  802.       for I := 0 to FPickList.Items.Count - 1 do
  803.       begin
  804.         Y := FPickList.Canvas.TextWidth(FPickList.Items[I]);
  805.         if Y > J then J := Y;
  806.       end;
  807.       FPickList.ClientWidth := J;
  808.     end;
  809.     P := Parent.ClientToScreen(Point(Left, Top));
  810.     Y := P.Y + Height;
  811.     if Y + FActiveList.Height > Screen.Height then Y := P.Y - FActiveList.Height;
  812.     TbsDBPopupListBox(FActiveList).Show(Point(P.X, Y));
  813.     FListVisible := True;
  814.     Invalidate;
  815.     Windows.SetFocus(Handle);
  816.   end;
  817. end;
  818. type
  819.   TWinControlCracker = class(TWinControl) end;
  820. procedure TDBGridInplaceEdit.KeyDown(var Key: Word; Shift: TShiftState);
  821. begin
  822.   if (EditStyle = esEllipsis) and (Key = VK_RETURN) and (Shift = [ssCtrl]) then
  823.   begin
  824.     TbsSkinCustomDBGrid(Grid).EditButtonClick;
  825.     KillMessage(Handle, WM_CHAR);
  826.   end
  827.   else
  828.     inherited KeyDown(Key, Shift);
  829. end;
  830. procedure TDBGridInplaceEdit.ListMouseUp(Sender: TObject; Button: TMouseButton;
  831.   Shift: TShiftState; X, Y: Integer);
  832. begin
  833.   if Button = mbLeft then
  834.     CloseUp(PtInRect(FActiveList.ClientRect, Point(X, Y)));
  835. end;
  836. procedure TDBGridInplaceEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
  837.   X, Y: Integer);
  838. begin
  839.   if (Button = mbLeft) and (FEditStyle <> esSimple) and
  840.     OverButton(Point(X,Y)) then
  841.   begin
  842.     if FListVisible then
  843.       CloseUp(False)
  844.     else
  845.     begin
  846.       MouseCapture := True;
  847.       FTracking := True;
  848.       TrackButton(X, Y);
  849.       if Assigned(FActiveList) then
  850.         DropDown;
  851.     end;
  852.   end;
  853.   inherited MouseDown(Button, Shift, X, Y);
  854. end;
  855. procedure TDBGridInplaceEdit.MouseMove(Shift: TShiftState; X, Y: Integer);
  856. var
  857.   ListPos: TPoint;
  858.   MousePos: TSmallPoint;
  859. begin
  860.   if FTracking then
  861.   begin
  862.     TrackButton(X, Y);
  863.     if FListVisible then
  864.     begin
  865.       ListPos := FActiveList.ScreenToClient(ClientToScreen(Point(X, Y)));
  866.       if PtInRect(FActiveList.ClientRect, ListPos) then
  867.       begin
  868.         StopTracking;
  869.         MousePos := PointToSmallPoint(ListPos);
  870.         SendMessage(FActiveList.Handle, WM_LBUTTONDOWN, 0, Integer(MousePos));
  871.         Exit;
  872.       end;
  873.     end;
  874.   end;
  875.   inherited MouseMove(Shift, X, Y);
  876. end;
  877. procedure TDBGridInplaceEdit.MouseUp(Button: TMouseButton; Shift: TShiftState;
  878.   X, Y: Integer);
  879. var
  880.   WasPressed: Boolean;
  881. begin
  882.   WasPressed := FPressed;
  883.   StopTracking;
  884.   if (Button = mbLeft) and (FEditStyle = esEllipsis) and WasPressed then
  885.     TbsSkinCustomDBGrid(Grid).EditButtonClick;
  886.   inherited MouseUp(Button, Shift, X, Y);
  887. end;
  888. procedure TDBGridInplaceEdit.PaintWindow(DC: HDC);
  889. procedure DrawButton(R: TRect);
  890. var
  891.   SaveIndex: Integer;
  892.   C: TCanvas;
  893. begin
  894.   SaveIndex := SaveDC(DC);
  895.   C := TCanvas.Create;
  896.   C.Handle := DC;
  897.   with C do
  898.   begin
  899.     Brush.Color := Color;
  900.     FillRect(R);
  901.     DrawArrowImage(C, R, Font.Color, 4);
  902.   end;
  903.   C.Handle := 0;
  904.   C.Free;
  905.   RestoreDC(DC, SaveIndex);
  906. end;
  907. procedure DrawButton2(R: TRect);
  908. var
  909.   SaveIndex: Integer;
  910.   C: TCanvas;
  911.   W, X, Y: Integer;
  912. begin
  913.   SaveIndex := SaveDC(DC);
  914.   C := TCanvas.Create;
  915.   C.Handle := DC;
  916.   with C do
  917.   begin
  918.     Brush.Color := Color;
  919.     FillRect(R);
  920.     X := R.Left + ((R.Right - R.Left) shr 1) - 1;
  921.     Y := R.Top + ((R.Bottom - R.Top) shr 1) - 1;
  922.     W := FButtonWidth shr 3;
  923.     if W = 0 then W := 1;
  924.     Pen.Color := Font.Color;
  925.     Rectangle(X, Y, X + W, Y + W);
  926.     Rectangle(X - (W * 2), Y, X - (W * 2) + W, Y + W);
  927.     Rectangle(X + (W * 2), Y, X + (W * 2) + W, Y + W);
  928.   end;
  929.   C.Handle := 0;
  930.   C.Free;
  931.   RestoreDC(DC, SaveIndex);
  932. end;
  933. var
  934.   R: TRect;
  935. begin
  936.   if FEditStyle <> esSimple
  937.   then
  938.     begin
  939.       R := ButtonRect;
  940.       if FEditStyle in [esDataList, esPickList]
  941.       then
  942.         DrawButton(R)
  943.       else
  944.         DrawButton2(R);
  945.     ExcludeClipRect(DC, R.Left, R.Top, R.Right, R.Bottom);
  946.   end;
  947.   inherited PaintWindow(DC);
  948. end;
  949. procedure TDBGridInplaceEdit.SetEditStyle(Value: TEditStyle);
  950. begin
  951.   if Value = FEditStyle then Exit;
  952.   FEditStyle := Value;
  953.   case Value of
  954.     esPickList:
  955.       begin
  956.         if FPickList = nil then
  957.         begin
  958.           FPickList := TbsDBPopupListbox.Create(Self);
  959.           FPickList.Visible := False;
  960.           FPickList.Parent := Self;
  961.           FPickList.OnMouseUp := ListMouseUp;
  962.         end;
  963.         FActiveList := FPickList;
  964.       end;
  965.     esDataList:
  966.       begin
  967.         if FDataList = nil then
  968.         begin
  969.           FDataList := TPopupDataList.Create(Self);
  970.           FDataList.Visible := False;
  971.           FDataList.Parent := Self;
  972.           FDataList.OnMouseUp := ListMouseUp;
  973.         end;
  974.         FActiveList := FDataList;
  975.       end;
  976.   else  { cbsNone, cbsEllipsis, or read only field }
  977.     FActiveList := nil;
  978.   end;
  979.   with TbsSkinCustomDBGrid(Grid) do
  980.     Self.ReadOnly := Columns[SelectedIndex].ReadOnly;
  981.   Repaint;
  982. end;
  983. procedure TDBGridInplaceEdit.StopTracking;
  984. begin
  985.   if FTracking then
  986.   begin
  987.     TrackButton(-1, -1);
  988.     FTracking := False;
  989.     MouseCapture := False;
  990.   end;
  991. end;
  992. procedure TDBGridInplaceEdit.TrackButton(X,Y: Integer);
  993. var
  994.   NewState: Boolean;
  995.   R: TRect;
  996. begin
  997.   R := ButtonRect;
  998.   NewState := PtInRect(R, Point(X, Y));
  999.   if FPressed <> NewState then
  1000.   begin
  1001.     FPressed := NewState;
  1002.     InvalidateRect(Handle, @R, False);
  1003.   end;
  1004. end;
  1005. procedure TDBGridInplaceEdit.UpdateContents;
  1006. var
  1007.   Column: TbsColumn;
  1008.   NewStyle: TEditStyle;
  1009.   MasterField: TField;
  1010. begin
  1011.   with TbsSkinCustomDBGrid(Grid) do
  1012.     Column := Columns[SelectedIndex];
  1013.   NewStyle := esSimple;
  1014.   case Column.ButtonStyle of
  1015.    cbsEllipsis: NewStyle := esEllipsis;
  1016.    cbsAuto:
  1017.      if Assigned(Column.Field) then
  1018.      with Column.Field do
  1019.      begin
  1020.        { Show the dropdown button only if the field is editable }
  1021.        if FieldKind = fkLookup then
  1022.        begin
  1023.          MasterField := Dataset.FieldByName(KeyFields);
  1024.          { Column.DefaultReadonly will always be True for a lookup field.
  1025.            Test if Column.ReadOnly has been assigned a value of True }
  1026.          if Assigned(MasterField) and MasterField.CanModify and
  1027.            not ((cvReadOnly in Column.AssignedValues) and Column.ReadOnly) then
  1028.            with TbsSkinCustomDBGrid(Grid) do
  1029.              if not ReadOnly and DataLink.Active and not Datalink.ReadOnly then
  1030.                NewStyle := esDataList
  1031.        end
  1032.        else
  1033.        if Assigned(Column.Picklist) and (Column.PickList.Count > 0) and
  1034.          not Column.Readonly then
  1035.          NewStyle := esPickList
  1036.        else if DataType in [ftDataset, ftReference] then
  1037.          NewStyle := esEllipsis;
  1038.      end;
  1039.   end;
  1040.   EditStyle := NewStyle;
  1041.   inherited UpdateContents;
  1042.   Font.Assign(Column.Font);
  1043.   ImeMode := Column.ImeMode;
  1044.   ImeName := Column.ImeName;
  1045. end;
  1046. procedure TDBGridInplaceEdit.CMCancelMode(var Message: TCMCancelMode);
  1047. function NotActiveListHandle: Boolean;
  1048. begin
  1049.   if FActiveList <> nil
  1050.   then
  1051.     with TbsDBPopupListbox(FActiveList) do
  1052.     begin
  1053.       Result := (Message.Sender <> FPickList) and
  1054.                 (Message.Sender <> FPickList.ListBox);
  1055.       if FPickList.ScrollBar <> nil
  1056.       then
  1057.         Result := Result and (Message.Sender <> FPickList.ScrollBar);
  1058.     end
  1059.   else
  1060.     Result := False;
  1061. end;
  1062. begin
  1063.   if (Message.Sender <> Self) and (Message.Sender <> FActiveList) and
  1064.      NotActiveListHandle
  1065.   then
  1066.     CloseUp(False);
  1067. end;
  1068. procedure TDBGridInplaceEdit.WMCancelMode(var Message: TMessage);
  1069. begin
  1070.   StopTracking;
  1071.   inherited;
  1072. end;
  1073. type
  1074.   TParentGrid = class(TbsSkinDBGrid);
  1075. procedure TDBGridInplaceEdit.WMKillFocus(var Message: TMessage);
  1076. begin
  1077.   if not SysLocale.FarEast then inherited
  1078.   else
  1079.   begin
  1080.     ImeName := Screen.DefaultIme;
  1081.     ImeMode := imDontCare;
  1082.     inherited;
  1083.     if HWND(Message.WParam) <> TbsSkinCustomDBGrid(Grid).Handle then
  1084.       ActivateKeyboardLayout(Screen.DefaultKbLayout, KLF_ACTIVATE);
  1085.   end;
  1086.   CloseUp(False);
  1087.   with TParentGrid(Grid) do
  1088.    if FIndex = -1 then InvalidateCell(Col, Row);
  1089. end;
  1090. function TDBGridInplaceEdit.ButtonRect: TRect;
  1091. begin
  1092.   if not TbsSkinCustomDBGrid(Owner).UseRightToLeftAlignment then
  1093.     Result := Rect(Width - FButtonWidth, 0, Width, Height)
  1094.   else
  1095.     Result := Rect(0, 0, FButtonWidth, Height);
  1096. end;
  1097. function TDBGridInplaceEdit.OverButton(const P: TPoint): Boolean;
  1098. begin
  1099.   Result := PtInRect(ButtonRect, P);
  1100. end;
  1101. procedure TDBGridInplaceEdit.WMLButtonDblClk(var Message: TWMLButtonDblClk);
  1102. begin
  1103.   with Message do
  1104.   if (FEditStyle <> esSimple) and OverButton(Point(XPos, YPos)) then
  1105.     Exit;
  1106.   inherited;
  1107. end;
  1108. procedure TDBGridInplaceEdit.WMPaint(var Message: TWMPaint);
  1109. begin
  1110.   PaintHandler(Message);
  1111. end;
  1112. procedure TDBGridInplaceEdit.WMSetCursor(var Message: TWMSetCursor);
  1113. var
  1114.   P: TPoint;
  1115. begin
  1116.   GetCursorPos(P);
  1117.   P := ScreenToClient(P);
  1118.   if (FEditStyle <> esSimple) and OverButton(P) then
  1119.     Windows.SetCursor(LoadCursor(0, idc_Arrow))
  1120.   else
  1121.     inherited;
  1122. end;
  1123. procedure TDBGridInplaceEdit.WndProc(var Message: TMessage);
  1124. begin
  1125.   case Message.Msg of
  1126.     wm_KeyDown, wm_SysKeyDown, wm_Char:
  1127.       if EditStyle in [esPickList, esDataList] then
  1128.       with TWMKey(Message) do
  1129.       begin
  1130.         DoDropDownKeys(CharCode, KeyDataToShiftState(KeyData));
  1131.         if (CharCode <> 0) and FListVisible then
  1132.         begin
  1133.           with TMessage(Message) do
  1134.             SendMessage(TbsDBPopupListbox(FActiveList).ListBox.Handle, Msg, WParam, LParam);
  1135.           Exit;
  1136.         end;
  1137.       end
  1138.   end;
  1139.   inherited;
  1140. end;
  1141. { TbsGridDataLink }
  1142. type
  1143.   TIntArray = array[0..MaxMapSize] of Integer;
  1144.   PIntArray = ^TIntArray;
  1145. constructor TbsGridDataLink.Create(AGrid: TbsSkinCustomDBGrid);
  1146. begin
  1147.   inherited Create;
  1148.   FGrid := AGrid;
  1149.   VisualControl := True;
  1150. end;
  1151. destructor TbsGridDataLink.Destroy;
  1152. begin
  1153.   ClearMapping;
  1154.   inherited Destroy;
  1155. end;
  1156. function TbsGridDataLink.GetDefaultFields: Boolean;
  1157. var
  1158.   I: Integer;
  1159. begin
  1160.   Result := True;
  1161.   if DataSet <> nil then Result := DataSet.DefaultFields;
  1162.   if Result and SparseMap then
  1163.   for I := 0 to FFieldCount-1 do
  1164.     if FFieldMap[I] < 0 then
  1165.     begin
  1166.       Result := False;
  1167.       Exit;
  1168.     end;
  1169. end;
  1170. function TbsGridDataLink.GetFields(I: Integer): TField;
  1171. begin
  1172.   if (0 <= I) and (I < FFieldCount) and (FFieldMap[I] >= 0) then
  1173.     Result := DataSet.FieldList[FFieldMap[I]]
  1174.   else
  1175.     Result := nil;
  1176. end;
  1177. function TbsGridDataLink.AddMapping(const FieldName: string): Boolean;
  1178. var
  1179.   Field: TField;
  1180.   NewSize: Integer;
  1181. begin
  1182.   Result := True;
  1183.   if FFieldCount >= MaxMapSize then RaiseGridError(STooManyColumns);
  1184.   if SparseMap then
  1185.     Field := DataSet.FindField(FieldName)
  1186.   else
  1187.     Field := DataSet.FieldByName(FieldName);
  1188.   if FFieldCount = Length(FFieldMap) then
  1189.   begin
  1190.     NewSize := Length(FFieldMap);
  1191.     if NewSize = 0 then
  1192.       NewSize := 8
  1193.     else
  1194.       Inc(NewSize, NewSize);
  1195.     if (NewSize < FFieldCount) then
  1196.       NewSize := FFieldCount + 1;
  1197.     if (NewSize > MaxMapSize) then
  1198.       NewSize := MaxMapSize;
  1199.     SetLength(FFieldMap, NewSize);
  1200.   end;
  1201.   if Assigned(Field) then
  1202.   begin
  1203.     FFieldMap[FFieldCount] := Dataset.FieldList.IndexOfObject(Field);
  1204.     Field.FreeNotification(FGrid);
  1205.   end
  1206.   else
  1207.     FFieldMap[FFieldCount] := -1;
  1208.   Inc(FFieldCount);
  1209. end;
  1210. procedure TbsGridDataLink.ActiveChanged;
  1211. begin
  1212.   FGrid.LinkActive(Active);
  1213.   FModified := False;
  1214. end;
  1215. procedure TbsGridDataLink.ClearMapping;
  1216. begin
  1217.   FFieldMap := nil;
  1218.   FFieldCount := 0;
  1219. end;
  1220. procedure TbsGridDataLink.Modified;
  1221. begin
  1222.   FModified := True;
  1223. end;
  1224. procedure TbsGridDataLink.DataSetChanged;
  1225. begin
  1226.   FGrid.DataChanged;
  1227.   FModified := False;
  1228. end;
  1229. procedure TbsGridDataLink.DataSetScrolled(Distance: Integer);
  1230. begin
  1231.   FGrid.Scroll(Distance);
  1232. end;
  1233. procedure TbsGridDataLink.LayoutChanged;
  1234. var
  1235.   SaveState: Boolean;
  1236. begin
  1237.   { FLayoutFromDataset determines whether default column width is forced to
  1238.     be at least wide enough for the column title.  }
  1239.   SaveState := FGrid.FLayoutFromDataset;
  1240.   FGrid.FLayoutFromDataset := True;
  1241.   try
  1242.     FGrid.LayoutChanged;
  1243.   finally
  1244.     FGrid.FLayoutFromDataset := SaveState;
  1245.   end;
  1246.   inherited LayoutChanged;
  1247. end;
  1248. procedure TbsGridDataLink.FocusControl(Field: TFieldRef);
  1249. begin
  1250.   if Assigned(Field) and Assigned(Field^) then
  1251.   begin
  1252.     FGrid.SelectedField := Field^;
  1253.     if (FGrid.SelectedField = Field^) and FGrid.AcquireFocus then
  1254.     begin
  1255.       Field^ := nil;
  1256.       FGrid.ShowEditor;
  1257.     end;
  1258.   end;
  1259. end;
  1260. procedure TbsGridDataLink.EditingChanged;
  1261. begin
  1262.   FGrid.EditingChanged;
  1263. end;
  1264. procedure TbsGridDataLink.RecordChanged(Field: TField);
  1265. begin
  1266.   FGrid.RecordChanged(Field);
  1267.   FModified := False;
  1268. end;
  1269. procedure TbsGridDataLink.UpdateData;
  1270. begin
  1271.   FInUpdateData := True;
  1272.   try
  1273.     if FModified then FGrid.UpdateData;
  1274.     FModified := False;
  1275.   finally
  1276.     FInUpdateData := False;
  1277.   end;
  1278. end;
  1279. function TbsGridDataLink.GetMappedIndex(ColIndex: Integer): Integer;
  1280. begin
  1281.   if (0 <= ColIndex) and (ColIndex < FFieldCount) then
  1282.     Result := FFieldMap[ColIndex]
  1283.   else
  1284.     Result := -1;
  1285. end;
  1286. procedure TbsGridDataLink.Reset;
  1287. begin
  1288.   if FModified then RecordChanged(nil) else Dataset.Cancel;
  1289. end;
  1290. function TbsGridDataLink.IsAggRow(Value: Integer): Boolean;
  1291. begin
  1292.   Result := False;
  1293. end;
  1294. procedure TbsGridDataLink.BuildAggMap;
  1295. begin
  1296. end;
  1297. { TbsColumnTitle }
  1298. constructor TbsColumnTitle.Create(Column: TbsColumn);
  1299. begin
  1300.   inherited Create;
  1301.   FColumn := Column;
  1302.   FFont := TFont.Create;
  1303.   FFont.Assign(DefaultFont);
  1304.   FFont.OnChange := FontChanged;
  1305. end;
  1306. destructor TbsColumnTitle.Destroy;
  1307. begin
  1308.   FFont.Free;
  1309.   inherited Destroy;
  1310. end;
  1311. procedure TbsColumnTitle.Assign(Source: TPersistent);
  1312. begin
  1313.   if Source is TbsColumnTitle then
  1314.   begin
  1315.     if cvTitleAlignment in TbsColumnTitle(Source).FColumn.FAssignedValues then
  1316.       Alignment := TbsColumnTitle(Source).Alignment;
  1317.     if cvTitleColor in TbsColumnTitle(Source).FColumn.FAssignedValues then
  1318.       Color := TbsColumnTitle(Source).Color;
  1319.     if cvTitleCaption in TbsColumnTitle(Source).FColumn.FAssignedValues then
  1320.       Caption := TbsColumnTitle(Source).Caption;
  1321.     if cvTitleFont in TbsColumnTitle(Source).FColumn.FAssignedValues then
  1322.       Font := TbsColumnTitle(Source).Font;
  1323.   end
  1324.   else
  1325.     inherited Assign(Source);
  1326. end;
  1327. function TbsColumnTitle.DefaultAlignment: TAlignment;
  1328. begin
  1329.   Result := taLeftJustify;
  1330. end;
  1331. function TbsColumnTitle.DefaultColor: TColor;
  1332. var
  1333.   Grid: TbsSkinCustomDBGrid;
  1334. begin
  1335.   Grid := FColumn.GetGrid;
  1336.   if Assigned(Grid) then
  1337.     Result := Grid.FixedColor
  1338.   else
  1339.     Result := clBtnFace;
  1340. end;
  1341. function TbsColumnTitle.DefaultFont: TFont;
  1342. var
  1343.   Grid: TbsSkinCustomDBGrid;
  1344. begin
  1345.   Grid := FColumn.GetGrid;
  1346.   if Assigned(Grid) then
  1347.     Result := Grid.TitleFont
  1348.   else
  1349.     Result := FColumn.Font;
  1350. end;
  1351. function TbsColumnTitle.DefaultCaption: string;
  1352. var
  1353.   Field: TField;
  1354. begin
  1355.   Field := FColumn.Field;
  1356.   if Assigned(Field) then
  1357.     Result := Field.DisplayName
  1358.   else
  1359.     Result := FColumn.FieldName;
  1360. end;
  1361. procedure TbsColumnTitle.FontChanged(Sender: TObject);
  1362. begin
  1363.   Include(FColumn.FAssignedValues, cvTitleFont);
  1364.   FColumn.Changed(True);
  1365. end;
  1366. function TbsColumnTitle.GetAlignment: TAlignment;
  1367. begin
  1368.   if cvTitleAlignment in FColumn.FAssignedValues then
  1369.     Result := FAlignment
  1370.   else
  1371.     Result := DefaultAlignment;
  1372. end;
  1373. function TbsColumnTitle.GetColor: TColor;
  1374. begin
  1375.   if cvTitleColor in FColumn.FAssignedValues then
  1376.     Result := FColor
  1377.   else
  1378.     Result := DefaultColor;
  1379. end;
  1380. function TbsColumnTitle.GetCaption: string;
  1381. begin
  1382.   if cvTitleCaption in FColumn.FAssignedValues then
  1383.     Result := FCaption
  1384.   else
  1385.     Result := DefaultCaption;
  1386. end;
  1387. function TbsColumnTitle.GetFont: TFont;
  1388. var
  1389.   Save: TNotifyEvent;
  1390.   Def: TFont;
  1391. begin
  1392.   if not (cvTitleFont in FColumn.FAssignedValues) then
  1393.   begin
  1394.     Def := DefaultFont;
  1395.     if (FFont.Handle <> Def.Handle) or (FFont.Color <> Def.Color) then
  1396.     begin
  1397.       Save := FFont.OnChange;
  1398.       FFont.OnChange := nil;
  1399.       FFont.Assign(DefaultFont);
  1400.       FFont.OnChange := Save;
  1401.     end;
  1402.   end;
  1403.   Result := FFont;
  1404. end;
  1405. function TbsColumnTitle.IsAlignmentStored: Boolean;
  1406. begin
  1407.   Result := (cvTitleAlignment in FColumn.FAssignedValues) and
  1408.     (FAlignment <> DefaultAlignment);
  1409. end;
  1410. function TbsColumnTitle.IsColorStored: Boolean;
  1411. begin
  1412.   Result := (cvTitleColor in FColumn.FAssignedValues) and
  1413.     (FColor <> DefaultColor);
  1414. end;
  1415. function TbsColumnTitle.IsFontStored: Boolean;
  1416. begin
  1417.   Result := (cvTitleFont in FColumn.FAssignedValues);
  1418. end;
  1419. function TbsColumnTitle.IsCaptionStored: Boolean;
  1420. begin
  1421.   Result := (cvTitleCaption in FColumn.FAssignedValues) and
  1422.     (FCaption <> DefaultCaption);
  1423. end;
  1424. procedure TbsColumnTitle.RefreshDefaultFont;
  1425. var
  1426.   Save: TNotifyEvent;
  1427. begin
  1428.   if (cvTitleFont in FColumn.FAssignedValues) then Exit;
  1429.   Save := FFont.OnChange;
  1430.   FFont.OnChange := nil;
  1431.   try
  1432.     FFont.Assign(DefaultFont);
  1433.   finally
  1434.     FFont.OnChange := Save;
  1435.   end;
  1436. end;
  1437. procedure TbsColumnTitle.RestoreDefaults;
  1438. var
  1439.   FontAssigned: Boolean;
  1440. begin
  1441.   FontAssigned := cvTitleFont in FColumn.FAssignedValues;
  1442.   FColumn.FAssignedValues := FColumn.FAssignedValues - ColumnTitleValues;
  1443.   FCaption := '';
  1444.   RefreshDefaultFont;
  1445.   { If font was assigned, changing it back to default may affect grid title
  1446.     height, and title height changes require layout and redraw of the grid. }
  1447.   FColumn.Changed(FontAssigned);
  1448. end;
  1449. procedure TbsColumnTitle.SetAlignment(Value: TAlignment);
  1450. begin
  1451.   if (cvTitleAlignment in FColumn.FAssignedValues) and (Value = FAlignment) then Exit;
  1452.   FAlignment := Value;
  1453.   Include(FColumn.FAssignedValues, cvTitleAlignment);
  1454.   FColumn.Changed(False);
  1455. end;
  1456. procedure TbsColumnTitle.SetColor(Value: TColor);
  1457. begin
  1458.   if (cvTitleColor in FColumn.FAssignedValues) and (Value = FColor) then Exit;
  1459.   FColor := Value;
  1460.   Include(FColumn.FAssignedValues, cvTitleColor);
  1461.   FColumn.Changed(False);
  1462. end;
  1463. procedure TbsColumnTitle.SetFont(Value: TFont);
  1464. begin
  1465.   FFont.Assign(Value);
  1466. end;
  1467. procedure TbsColumnTitle.SetCaption(const Value: string);
  1468. var
  1469.   Grid: TbsSkinCustomDBGrid;
  1470. begin
  1471.   if Column.IsStored then
  1472.   begin
  1473.     if (cvTitleCaption in FColumn.FAssignedValues) and (Value = FCaption) then Exit;
  1474.     FCaption := Value;
  1475.     Include(Column.FAssignedValues, cvTitleCaption);
  1476.     Column.Changed(False);
  1477.   end
  1478.   else
  1479.   begin
  1480.     Grid := Column.GetGrid;
  1481.     if Assigned(Grid) and (Grid.Datalink.Active) and Assigned(Column.Field) then
  1482.       Column.Field.DisplayLabel := Value;
  1483.   end;
  1484. end;
  1485. { TbsColumn }
  1486. constructor TbsColumn.Create(Collection: TCollection);
  1487. var
  1488.   Grid: TbsSkinCustomDBGrid;
  1489. begin
  1490.   Grid := nil;
  1491.   if Assigned(Collection) and (Collection is TbsDBGridColumns) then
  1492.     Grid := TbsDBGridColumns(Collection).Grid;
  1493.   if Assigned(Grid) then Grid.BeginLayout;
  1494.   try
  1495.     inherited Create(Collection);
  1496.     FDropDownRows := 7;
  1497.     FButtonStyle := cbsAuto;
  1498.     FFont := TFont.Create;
  1499.     FFont.Assign(DefaultFont);
  1500.     FFont.OnChange := FontChanged;
  1501.     FImeMode := imDontCare;
  1502.     FImeName := Screen.DefaultIme;
  1503.     FTitle := CreateTitle;
  1504.     FVisible := True;
  1505.     FExpanded := True;
  1506.     FStored := True;
  1507.   finally
  1508.     if Assigned(Grid) then Grid.EndLayout;
  1509.   end;
  1510. end;
  1511. destructor TbsColumn.Destroy;
  1512. begin
  1513.   FTitle.Free;
  1514.   FFont.Free;
  1515.   FPickList.Free;
  1516.   inherited Destroy;
  1517. end;
  1518. procedure TbsColumn.Assign(Source: TPersistent);
  1519. begin
  1520.   if Source is TbsColumn then
  1521.   begin
  1522.     if Assigned(Collection) then Collection.BeginUpdate;
  1523.     try
  1524.       RestoreDefaults;
  1525.       FieldName := TbsColumn(Source).FieldName;
  1526.       if cvColor in TbsColumn(Source).AssignedValues then
  1527.         Color := TbsColumn(Source).Color;
  1528.       if cvWidth in TbsColumn(Source).AssignedValues then
  1529.         Width := TbsColumn(Source).Width;
  1530.       if cvFont in TbsColumn(Source).AssignedValues then
  1531.         Font := TbsColumn(Source).Font;
  1532.       if cvImeMode in TbsColumn(Source).AssignedValues then
  1533.         ImeMode := TbsColumn(Source).ImeMode;
  1534.       if cvImeName in TbsColumn(Source).AssignedValues then
  1535.         ImeName := TbsColumn(Source).ImeName;
  1536.       if cvAlignment in TbsColumn(Source).AssignedValues then
  1537.         Alignment := TbsColumn(Source).Alignment;
  1538.       if cvReadOnly in TbsColumn(Source).AssignedValues then
  1539.         ReadOnly := TbsColumn(Source).ReadOnly;
  1540.       Title := TbsColumn(Source).Title;
  1541.       DropDownRows := TbsColumn(Source).DropDownRows;
  1542.       ButtonStyle := TbsColumn(Source).ButtonStyle;
  1543.       PickList := TbsColumn(Source).PickList;
  1544.       PopupMenu := TbsColumn(Source).PopupMenu;
  1545.       FVisible := TbsColumn(Source).FVisible;
  1546.       FExpanded := TbsColumn(Source).FExpanded;
  1547.     finally
  1548.       if Assigned(Collection) then Collection.EndUpdate;
  1549.     end;
  1550.   end
  1551.   else
  1552.     inherited Assign(Source);
  1553. end;
  1554. function TbsColumn.CreateTitle: TbsColumnTitle;
  1555. begin
  1556.   Result := TbsColumnTitle.Create(Self);
  1557. end;
  1558. function TbsColumn.DefaultAlignment: TAlignment;
  1559. begin
  1560.   if Assigned(Field) then
  1561.     Result := FField.Alignment
  1562.   else
  1563.     Result := taLeftJustify;
  1564. end;
  1565. function TbsColumn.DefaultColor: TColor;
  1566. var
  1567.   Grid: TbsSkinCustomDBGrid;
  1568. begin
  1569.   Grid := GetGrid;
  1570.   if Assigned(Grid) then
  1571.     Result := Grid.Color
  1572.   else
  1573.     Result := clWindow;
  1574. end;
  1575. function TbsColumn.DefaultFont: TFont;
  1576. var
  1577.   Grid: TbsSkinCustomDBGrid;
  1578. begin
  1579.   Grid := GetGrid;
  1580.   if Assigned(Grid) then
  1581.     Result := Grid.Font
  1582.   else
  1583.     Result := FFont;
  1584. end;
  1585. function TbsColumn.DefaultImeMode: TImeMode;
  1586. var
  1587.   Grid: TbsSkinCustomDBGrid;
  1588. begin
  1589.   Grid := GetGrid;
  1590.   if Assigned(Grid) then
  1591.     Result := Grid.ImeMode
  1592.   else
  1593.     Result := FImeMode;
  1594. end;
  1595. function TbsColumn.DefaultImeName: TImeName;
  1596. var
  1597.   Grid: TbsSkinCustomDBGrid;
  1598. begin
  1599.   Grid := GetGrid;
  1600.   if Assigned(Grid) then
  1601.     Result := Grid.ImeName
  1602.   else
  1603.     Result := FImeName;
  1604. end;
  1605. function TbsColumn.DefaultReadOnly: Boolean;
  1606. var
  1607.   Grid: TbsSkinCustomDBGrid;
  1608. begin
  1609.   Grid := GetGrid;
  1610.   Result := (Assigned(Grid) and Grid.ReadOnly) or
  1611.     (Assigned(Field) and FField.ReadOnly);
  1612. end;
  1613. function TbsColumn.DefaultWidth: Integer;
  1614. var
  1615.   W: Integer;
  1616.   RestoreCanvas: Boolean;
  1617.   TM: TTextMetric;
  1618. begin
  1619.   if GetGrid = nil then
  1620.   begin
  1621.     Result := 64;
  1622.     Exit;
  1623.   end;
  1624.   with GetGrid do
  1625.   begin
  1626.     if Assigned(Field) then
  1627.     begin
  1628.       RestoreCanvas := not HandleAllocated;
  1629.       if RestoreCanvas then
  1630.         Canvas.Handle := GetDC(0);
  1631.       try
  1632.         Canvas.Font := Self.Font;
  1633.         GetTextMetrics(Canvas.Handle, TM);
  1634.         Result := Field.DisplayWidth * (Canvas.TextWidth('0') - TM.tmOverhang)
  1635.           + TM.tmOverhang + 4;
  1636.         if dgTitles in Options then
  1637.         begin
  1638.           Canvas.Font := Title.Font;
  1639.           W := Canvas.TextWidth(Title.Caption) + 4;
  1640.           if Result < W then
  1641.             Result := W;
  1642.         end;
  1643.       finally
  1644.         if RestoreCanvas then
  1645.         begin
  1646.           ReleaseDC(0,Canvas.Handle);
  1647.           Canvas.Handle := 0;
  1648.         end;
  1649.       end;
  1650.     end
  1651.     else
  1652.       Result := DefaultColWidth;
  1653.   end;
  1654. end;
  1655. procedure TbsColumn.FontChanged;
  1656. begin
  1657.   Include(FAssignedValues, cvFont);
  1658.   Title.RefreshDefaultFont;
  1659.   Changed(False);
  1660. end;
  1661. function TbsColumn.GetAlignment: TAlignment;
  1662. begin
  1663.   if cvAlignment in FAssignedValues then
  1664.     Result := FAlignment
  1665.   else
  1666.     Result := DefaultAlignment;
  1667. end;
  1668. function TbsColumn.GetColor: TColor;
  1669. begin
  1670.   if cvColor in FAssignedValues then
  1671.     Result := FColor
  1672.   else
  1673.     Result := DefaultColor;
  1674. end;
  1675. function TbsColumn.GetExpanded: Boolean;
  1676. begin
  1677.   Result := FExpanded and Expandable;
  1678. end;
  1679. function TbsColumn.GetField: TField;
  1680. var
  1681.   Grid: TbsSkinCustomDBGrid;
  1682. begin    { Returns Nil if FieldName can't be found in dataset }
  1683.   Grid := GetGrid;
  1684.   if (FField = nil) and (Length(FFieldName) > 0) and Assigned(Grid) and
  1685.     Assigned(Grid.DataLink.DataSet) then
  1686.   with Grid.Datalink.Dataset do
  1687.     if Active or (not DefaultFields) then
  1688.       SetField(FindField(FieldName));
  1689.   Result := FField;
  1690. end;
  1691. function TbsColumn.GetFont: TFont;
  1692. var
  1693.   Save: TNotifyEvent;
  1694. begin
  1695.   if not (cvFont in FAssignedValues) and (FFont.Handle <> DefaultFont.Handle) then
  1696.   begin
  1697.     Save := FFont.OnChange;
  1698.     FFont.OnChange := nil;
  1699.     FFont.Assign(DefaultFont);
  1700.     FFont.OnChange := Save;
  1701.   end;
  1702.   Result := FFont;
  1703. end;
  1704. function TbsColumn.GetGrid: TbsSkinCustomDBGrid;
  1705. begin
  1706.   if Assigned(Collection) and (Collection is TbsDBGridColumns) then
  1707.     Result := TbsDBGridColumns(Collection).Grid
  1708.   else
  1709.     Result := nil;
  1710. end;
  1711. function TbsColumn.GetDisplayName: string;
  1712. begin
  1713.   Result := FFieldName;
  1714.   if Result = '' then Result := inherited GetDisplayName;
  1715. end;
  1716. function TbsColumn.GetImeMode: TImeMode;
  1717. begin
  1718.   if cvImeMode in FAssignedValues then
  1719.     Result := FImeMode
  1720.   else
  1721.     Result := DefaultImeMode;
  1722. end;
  1723. function TbsColumn.GetImeName: TImeName;
  1724. begin
  1725.   if cvImeName in FAssignedValues then
  1726.     Result := FImeName
  1727.   else
  1728.     Result := DefaultImeName;
  1729. end;
  1730. function TbsColumn.GetParentColumn: TbsColumn;
  1731. var
  1732.   Col: TbsColumn;
  1733.   Fld: TField;
  1734.   I: Integer;
  1735. begin
  1736.   Result := nil;
  1737.   Fld := Field;
  1738.   if (Fld <> nil) and (Fld.ParentField <> nil) and (Collection <> nil) then
  1739.     for I := Index - 1 downto 0 do
  1740.     begin
  1741.       Col := TbsColumn(Collection.Items[I]);
  1742.       if Fld.ParentField = Col.Field then
  1743.       begin
  1744.         Result := Col;
  1745.         Exit;
  1746.       end;
  1747.     end;
  1748. end;
  1749. function TbsColumn.GetPickList: TStrings;
  1750. begin
  1751.   if FPickList = nil then FPickList := TStringList.Create;
  1752.   Result := FPickList;
  1753. end;
  1754. function TbsColumn.GetReadOnly: Boolean;
  1755. begin
  1756.   if cvReadOnly in FAssignedValues then
  1757.     Result := FReadOnly
  1758.   else
  1759.     Result := DefaultReadOnly;
  1760. end;
  1761. function TbsColumn.GetShowing: Boolean;
  1762. var
  1763.   Col: TbsColumn;
  1764. begin
  1765.   Result := not Expanded and Visible;
  1766.   if Result then
  1767.   begin
  1768.     Col := Self;
  1769.     repeat
  1770.       Col := Col.ParentColumn;
  1771.     until (Col = nil) or not Col.Expanded;
  1772.     Result := Col = nil;
  1773.   end;
  1774. end;
  1775. function TbsColumn.GetVisible: Boolean;
  1776. var
  1777.   Col: TbsColumn;
  1778. begin
  1779.   Result := FVisible;
  1780.   if Result then
  1781.   begin
  1782.     Col := ParentColumn;
  1783.     Result := Result and ((Col = nil) or Col.Visible);
  1784.   end;
  1785. end;
  1786. function TbsColumn.GetWidth: Integer;
  1787. begin
  1788.   if not Showing then
  1789.     Result := -1
  1790.   else if cvWidth in FAssignedValues then
  1791.     Result := FWidth
  1792.   else
  1793.     Result := DefaultWidth;
  1794. end;
  1795. function TbsColumn.IsAlignmentStored: Boolean;
  1796. begin
  1797.   Result := (cvAlignment in FAssignedValues) and (FAlignment <> DefaultAlignment);
  1798. end;
  1799. function TbsColumn.IsColorStored: Boolean;
  1800. begin
  1801.   Result := (cvColor in FAssignedValues) and (FColor <> DefaultColor);
  1802. end;
  1803. function TbsColumn.IsFontStored: Boolean;
  1804. begin
  1805.   Result := (cvFont in FAssignedValues);
  1806. end;
  1807. function TbsColumn.IsImeModeStored: Boolean;
  1808. begin
  1809.   Result := (cvImeMode in FAssignedValues) and (FImeMode <> DefaultImeMode);
  1810. end;
  1811. function TbsColumn.IsImeNameStored: Boolean;
  1812. begin
  1813.   Result := (cvImeName in FAssignedValues) and (FImeName <> DefaultImeName);
  1814. end;
  1815. function TbsColumn.IsReadOnlyStored: Boolean;
  1816. begin
  1817.   Result := (cvReadOnly in FAssignedValues) and (FReadOnly <> DefaultReadOnly);
  1818. end;
  1819. function TbsColumn.IsWidthStored: Boolean;
  1820. begin
  1821.   Result := (cvWidth in FAssignedValues) and (FWidth <> DefaultWidth);
  1822. end;
  1823. procedure TbsColumn.RefreshDefaultFont;
  1824. var
  1825.   Save: TNotifyEvent;
  1826. begin
  1827.   if cvFont in FAssignedValues then Exit;
  1828.   Save := FFont.OnChange;
  1829.   FFont.OnChange := nil;
  1830.   try
  1831.     FFont.Assign(DefaultFont);
  1832.   finally
  1833.     FFont.OnChange := Save;
  1834.   end;
  1835. end;
  1836. procedure TbsColumn.RestoreDefaults;
  1837. var
  1838.   FontAssigned: Boolean;
  1839. begin
  1840.   FontAssigned := cvFont in FAssignedValues;
  1841.   FTitle.RestoreDefaults;
  1842.   FAssignedValues := [];
  1843.   RefreshDefaultFont;
  1844.   FPickList.Free;
  1845.   FPickList := nil;
  1846.   ButtonStyle := cbsAuto;
  1847.   Changed(FontAssigned);
  1848. end;
  1849. procedure TbsColumn.SetAlignment(Value: TAlignment);
  1850. var
  1851.   Grid: TbsSkinCustomDBGrid;
  1852. begin
  1853.   if IsStored then
  1854.   begin
  1855.     if (cvAlignment in FAssignedValues) and (Value = FAlignment) then Exit;
  1856.     FAlignment := Value;
  1857.     Include(FAssignedValues, cvAlignment);
  1858.     Changed(False);
  1859.   end
  1860.   else
  1861.   begin
  1862.     Grid := GetGrid;
  1863.     if Assigned(Grid) and (Grid.Datalink.Active) and Assigned(Field) then
  1864.       Field.Alignment := Value;
  1865.   end;
  1866. end;
  1867. procedure TbsColumn.SetButtonStyle(Value: TbsColumnButtonStyle);
  1868. begin
  1869.   if Value = FButtonStyle then Exit;
  1870.   FButtonStyle := Value;
  1871.   Changed(False);
  1872. end;
  1873. procedure TbsColumn.SetColor(Value: TColor);
  1874. begin
  1875.   if (cvColor in FAssignedValues) and (Value = FColor) then Exit;
  1876.   FColor := Value;
  1877.   Include(FAssignedValues, cvColor);
  1878.   Changed(False);
  1879. end;
  1880. procedure TbsColumn.SetField(Value: TField);
  1881. begin
  1882.   if FField = Value then Exit;
  1883.   if Assigned(FField) and
  1884.      (GetGrid <> nil) then
  1885.     FField.RemoveFreeNotification(GetGrid);
  1886.   FField := Value;
  1887.   if Assigned(Value) then
  1888.   begin
  1889.     if GetGrid <> nil then
  1890.       FField.FreeNotification(GetGrid);
  1891.     FFieldName := Value.FullName;
  1892.   end;
  1893.   if not IsStored then
  1894.   begin
  1895.     if Value = nil then
  1896.       FFieldName := '';
  1897.     RestoreDefaults;
  1898.   end;
  1899.   Changed(False);
  1900. end;
  1901. procedure TbsColumn.SetFieldName(const Value: String);
  1902. var
  1903.   AField: TField;
  1904.   Grid: TbsSkinCustomDBGrid;
  1905. begin
  1906.   AField := nil;
  1907.   Grid := GetGrid;
  1908.   if Assigned(Grid) and Assigned(Grid.DataLink.DataSet) and
  1909.     not (csLoading in Grid.ComponentState) and (Length(Value) > 0) then
  1910.       AField := Grid.DataLink.DataSet.FindField(Value); { no exceptions }
  1911.   FFieldName := Value;
  1912.   SetField(AField);
  1913.   Changed(False);
  1914. end;
  1915. procedure TbsColumn.SetFont(Value: TFont);
  1916. begin
  1917.   FFont.Assign(Value);
  1918.   Include(FAssignedValues, cvFont);
  1919.   Changed(False);
  1920. end;
  1921. procedure TbsColumn.SetImeMode(Value: TImeMode);
  1922. begin
  1923.   if (cvImeMode in FAssignedValues) or (Value <> DefaultImeMode) then
  1924.   begin
  1925.     FImeMode := Value;
  1926.     Include(FAssignedValues, cvImeMode);
  1927.   end;
  1928.   Changed(False);
  1929. end;
  1930. procedure TbsColumn.SetImeName(Value: TImeName);
  1931. begin
  1932.   if (cvImeName in FAssignedValues) or (Value <> DefaultImeName) then
  1933.   begin
  1934.     FImeName := Value;
  1935.     Include(FAssignedValues, cvImeName);
  1936.   end;
  1937.   Changed(False);
  1938. end;
  1939. procedure TbsColumn.SetIndex(Value: Integer);
  1940. var
  1941.   Grid: TbsSkinCustomDBGrid;
  1942.   Fld: TField;
  1943.   I, OldIndex: Integer;
  1944.   Col: TbsColumn;
  1945. begin
  1946.   OldIndex := Index;
  1947.   Grid := GetGrid;
  1948.   if IsStored then
  1949.   begin
  1950.     Grid.BeginLayout;
  1951.     try
  1952.       I := OldIndex + 1;  // move child columns along with parent
  1953.       while (I < Collection.Count) and (TbsColumn(Collection.Items[I]).ParentColumn = Self) do
  1954.         Inc(I);
  1955.       Dec(I);
  1956.       if OldIndex > Value then   // column moving left
  1957.       begin
  1958.         while I > OldIndex do
  1959.         begin
  1960.           Collection.Items[I].Index := Value;
  1961.           Inc(OldIndex);
  1962.         end;
  1963.         inherited SetIndex(Value);
  1964.       end
  1965.       else
  1966.       begin
  1967.         inherited SetIndex(Value);
  1968.         while I > OldIndex do
  1969.         begin
  1970.           Collection.Items[OldIndex].Index := Value;
  1971.           Dec(I);
  1972.         end;
  1973.       end;
  1974.     finally
  1975.       Grid.EndLayout;
  1976.     end;
  1977.   end
  1978.   else
  1979.   begin
  1980.     if (Grid <> nil) and Grid.Datalink.Active then
  1981.     begin
  1982.       if Grid.AcquireLayoutLock then
  1983.       try
  1984.         Col := Grid.ColumnAtDepth(Grid.Columns[Value], Depth);
  1985.         if (Col <> nil) then
  1986.         begin
  1987.           Fld := Col.Field;
  1988.           if Assigned(Fld) then
  1989.             Field.Index := Fld.Index;
  1990.         end;
  1991.       finally
  1992.         Grid.EndLayout;
  1993.       end;
  1994.     end;
  1995.     inherited SetIndex(Value);
  1996.   end;
  1997. end;
  1998. procedure TbsColumn.SetPickList(Value: TStrings);
  1999. begin
  2000.   if Value = nil then
  2001.   begin
  2002.     FPickList.Free;
  2003.     FPickList := nil;
  2004.     Exit;
  2005.   end;
  2006.   PickList.Assign(Value);
  2007. end;
  2008. procedure TbsColumn.SetPopupMenu(Value: TPopupMenu);
  2009. begin
  2010.   FPopupMenu := Value;
  2011.   if Value <> nil then Value.FreeNotification(GetGrid);
  2012. end;
  2013. procedure TbsColumn.SetReadOnly(Value: Boolean);
  2014. var
  2015.   Grid: TbsSkinCustomDBGrid;
  2016. begin
  2017.   Grid := GetGrid;
  2018.   if not IsStored and Assigned(Grid) and Grid.Datalink.Active and Assigned(Field) then
  2019.     Field.ReadOnly := Value
  2020.   else
  2021.   begin
  2022.     if (cvReadOnly in FAssignedValues) and (Value = FReadOnly) then Exit;
  2023.     FReadOnly := Value;
  2024.     Include(FAssignedValues, cvReadOnly);
  2025.     Changed(False);
  2026.   end;
  2027. end;
  2028. procedure TbsColumn.SetTitle(Value: TbsColumnTitle);
  2029. begin
  2030.   FTitle.Assign(Value);
  2031. end;
  2032. procedure TbsColumn.SetWidth(Value: Integer);
  2033. var
  2034.   Grid: TbsSkinCustomDBGrid;
  2035.   TM: TTextMetric;
  2036.   DoSetWidth: Boolean;
  2037. begin
  2038.   DoSetWidth := IsStored;
  2039.   if not DoSetWidth then
  2040.   begin
  2041.     Grid := GetGrid;
  2042.     if Assigned(Grid) then
  2043.     begin
  2044.       if Grid.HandleAllocated and Assigned(Field) and Grid.FUpdateFields then
  2045.       with Grid do
  2046.       begin
  2047.         Canvas.Font := Self.Font;
  2048.         GetTextMetrics(Canvas.Handle, TM);
  2049.         Field.DisplayWidth := (Value + (TM.tmAveCharWidth div 2) - TM.tmOverhang - 3)
  2050.           div TM.tmAveCharWidth;
  2051.       end;
  2052.       if (not Grid.FLayoutFromDataset) or (cvWidth in FAssignedValues) then
  2053.         DoSetWidth := True;
  2054.     end
  2055.     else
  2056.       DoSetWidth := True;
  2057.   end;
  2058.   if DoSetWidth then
  2059.   begin
  2060.     if ((cvWidth in FAssignedValues) or (Value <> DefaultWidth))
  2061.       and (Value <> -1) then
  2062.     begin
  2063.       FWidth := Value;
  2064.       Include(FAssignedValues, cvWidth);
  2065.     end;
  2066.     Changed(False);
  2067.   end;
  2068. end;
  2069. procedure TbsColumn.SetVisible(Value: Boolean);
  2070. begin
  2071.   if Value <> FVisible then
  2072.   begin
  2073.     FVisible := Value;
  2074.     Changed(True);
  2075.   end;
  2076. end;
  2077. procedure TbsColumn.SetExpanded(Value: Boolean);
  2078. const
  2079.   Direction: array [Boolean] of ShortInt = (-1,1);
  2080. var
  2081.   Grid: TbsSkinCustomDBGrid;
  2082.   WasShowing: Boolean;
  2083. begin
  2084.   if Value <> FExpanded then
  2085.   begin
  2086.     Grid := GetGrid;
  2087.     WasShowing := (Grid <> nil) and Grid.Columns[Grid.SelectedIndex].Showing;
  2088.     FExpanded := Value;
  2089.     Changed(True);
  2090.     if (Grid <> nil) and WasShowing then
  2091.     begin
  2092.       if not Grid.Columns[Grid.SelectedIndex].Showing then
  2093.         // The selected cell was hidden by this expand operation
  2094.         // Select 1st child (next col = 1) when parent is expanded
  2095.         // Select child's parent (prev col = -1) when parent is collapsed
  2096.         Grid.MoveCol(Grid.Col, Direction[FExpanded]);
  2097.     end;
  2098.   end;
  2099. end;
  2100. function TbsColumn.Depth: Integer;
  2101. var
  2102.   Col: TbsColumn;
  2103. begin
  2104.   Result := 0;
  2105.   Col := ParentColumn;
  2106.   if Col <> nil then Result := Col.Depth + 1;
  2107. end;
  2108. function TbsColumn.GetExpandable: Boolean;
  2109. var
  2110.   Fld: TField;
  2111. begin
  2112.   Fld := Field;
  2113.   Result := (Fld <> nil) and (Fld.DataType in [ftADT, ftArray]);
  2114. end;
  2115. { TbsDBGridColumns }
  2116. constructor TbsDBGridColumns.Create(Grid: TbsSkinCustomDBGrid; ColumnClass: TbsColumnClass);
  2117. begin
  2118.   inherited Create(ColumnClass);
  2119.   FGrid := Grid;
  2120. end;
  2121. function TbsDBGridColumns.Add: TbsColumn;
  2122. begin
  2123.   Result := TbsColumn(inherited Add);
  2124. end;
  2125. function TbsDBGridColumns.GetColumn(Index: Integer): TbsColumn;
  2126. begin
  2127.   Result := TbsColumn(inherited Items[Index]);
  2128. end;
  2129. function TbsDBGridColumns.GetOwner: TPersistent;
  2130. begin
  2131.   Result := FGrid;
  2132. end;
  2133. procedure TbsDBGridColumns.LoadFromFile(const Filename: string);
  2134. var
  2135.   S: TFileStream;
  2136. begin
  2137.   S := TFileStream.Create(Filename, fmOpenRead);
  2138.   try
  2139.     LoadFromStream(S);
  2140.   finally
  2141.     S.Free;
  2142.   end;
  2143. end;
  2144. type
  2145.   TbsColumnsWrapper = class(TComponent)
  2146.   private
  2147.     FColumns: TbsDBGridColumns;
  2148.   published
  2149.     property Columns: TbsDBGridColumns read FColumns write FColumns;
  2150.   end;
  2151. procedure TbsDBGridColumns.LoadFromStream(S: TStream);
  2152. var
  2153.   Wrapper: TbsColumnsWrapper;
  2154. begin
  2155.   Wrapper := TbsColumnsWrapper.Create(nil);
  2156.   try
  2157.     Wrapper.Columns := FGrid.CreateColumns;
  2158.     S.ReadComponent(Wrapper);
  2159.     Assign(Wrapper.Columns);
  2160.   finally
  2161.     Wrapper.Columns.Free;
  2162.     Wrapper.Free;
  2163.   end;
  2164. end;
  2165. procedure TbsDBGridColumns.RestoreDefaults;
  2166. var
  2167.   I: Integer;
  2168. begin
  2169.   BeginUpdate;
  2170.   try
  2171.     for I := 0 to Count-1 do
  2172.       Items[I].RestoreDefaults;
  2173.   finally
  2174.     EndUpdate;
  2175.   end;
  2176. end;
  2177. procedure TbsDBGridColumns.RebuildColumns;
  2178.   procedure AddFields(Fields: TFields; Depth: Integer);
  2179.   var
  2180.     I: Integer;
  2181.   begin
  2182.     Inc(Depth);
  2183.     for I := 0 to Fields.Count-1 do
  2184.     begin
  2185.       Add.FieldName := Fields[I].FullName;
  2186.       if Fields[I].DataType in [ftADT, ftArray] then
  2187.         AddFields((Fields[I] as TObjectField).Fields, Depth);
  2188.     end;
  2189.   end;
  2190. begin
  2191.   if Assigned(FGrid) and Assigned(FGrid.DataSource) and
  2192.     Assigned(FGrid.Datasource.Dataset) then
  2193.   begin
  2194.     FGrid.BeginLayout;
  2195.     try
  2196.       Clear;
  2197.       AddFields(FGrid.Datasource.Dataset.Fields, 0);
  2198.     finally
  2199.       FGrid.EndLayout;
  2200.     end
  2201.   end
  2202.   else
  2203.     Clear;
  2204. end;
  2205. procedure TbsDBGridColumns.SaveToFile(const Filename: string);
  2206. var
  2207.   S: TStream;
  2208. begin
  2209.   S := TFileStream.Create(Filename, fmCreate);
  2210.   try
  2211.     SaveToStream(S);
  2212.   finally
  2213.     S.Free;
  2214.   end;
  2215. end;
  2216. procedure TbsDBGridColumns.SaveToStream(S: TStream);
  2217. var
  2218.   Wrapper: TbsColumnsWrapper;
  2219. begin
  2220.   Wrapper := TbsColumnsWrapper.Create(nil);
  2221.   try
  2222.     Wrapper.Columns := Self;
  2223.     S.WriteComponent(Wrapper);
  2224.   finally
  2225.     Wrapper.Free;
  2226.   end;
  2227. end;
  2228. procedure TbsDBGridColumns.SetColumn(Index: Integer; Value: TbsColumn);
  2229. begin
  2230.   Items[Index].Assign(Value);
  2231. end;
  2232. procedure TbsDBGridColumns.SetState(NewState: TbsDBGridColumnsState);
  2233. begin
  2234.   if NewState = State then Exit;
  2235.   if NewState = csDefault then
  2236.     Clear
  2237.   else
  2238.     RebuildColumns;
  2239. end;
  2240. procedure TbsDBGridColumns.Update(Item: TCollectionItem);
  2241. var
  2242.   Raw: Integer;
  2243. begin
  2244.   if (FGrid = nil) or (csLoading in FGrid.ComponentState) then Exit;
  2245.   if Item = nil then
  2246.   begin
  2247.     FGrid.LayoutChanged;
  2248.   end
  2249.   else
  2250.   begin
  2251.     Raw := FGrid.DataToRawColumn(Item.Index);
  2252.     FGrid.InvalidateCol(Raw);
  2253.     FGrid.ColWidths[Raw] := TbsColumn(Item).Width;
  2254.   end;
  2255. end;
  2256. function TbsDBGridColumns.InternalAdd: TbsColumn;
  2257. begin
  2258.   Result := Add;
  2259.   Result.IsStored := False;
  2260. end;
  2261. function TbsDBGridColumns.GetState: TbsDBGridColumnsState;
  2262. begin
  2263.   Result := TbsDBGridColumnsState((Count > 0) and Items[0].IsStored);
  2264. end;
  2265. { TbsBookmarkList }
  2266. constructor TbsBookmarkList.Create(AGrid: TbsSkinCustomDBGrid);
  2267. begin
  2268.   inherited Create;
  2269.   FList := TStringList.Create;
  2270.   FList.OnChange := StringsChanged;
  2271.   FGrid := AGrid;
  2272. end;
  2273. destructor TbsBookmarkList.Destroy;
  2274. begin
  2275.   Clear;
  2276.   FList.Free;
  2277.   inherited Destroy;
  2278. end;
  2279. procedure TbsBookmarkList.Clear;
  2280. begin
  2281.   if FList.Count = 0 then Exit;
  2282.   FList.Clear;
  2283.   FGrid.Invalidate;
  2284. end;
  2285. function TbsBookmarkList.Compare(const Item1, Item2: TBookmarkStr): Integer;
  2286. begin
  2287.   with FGrid.Datalink.Datasource.Dataset do
  2288.     Result := CompareBookmarks(TBookmark(Item1), TBookmark(Item2));
  2289. end;
  2290. function TbsBookmarkList.CurrentRow: TBookmarkStr;
  2291. begin
  2292.   if not FLinkActive then RaiseGridError(sDataSetClosed);
  2293.   Result := FGrid.Datalink.Datasource.Dataset.Bookmark;
  2294. end;
  2295. function TbsBookmarkList.GetCurrentRowSelected: Boolean;
  2296. var
  2297.   Index: Integer;
  2298. begin
  2299.   Result := Find(CurrentRow, Index);
  2300. end;
  2301. function TbsBookmarkList.Find(const Item: TBookmarkStr; var Index: Integer): Boolean;
  2302. var
  2303.   L, H, I, C: Integer;
  2304. begin
  2305.   if (Item = FCache) and (FCacheIndex >= 0) then
  2306.   begin
  2307.     Index := FCacheIndex;
  2308.     Result := FCacheFind;
  2309.     Exit;
  2310.   end;
  2311.   Result := False;
  2312.   L := 0;
  2313.   H := FList.Count - 1;
  2314.   while L <= H do
  2315.   begin
  2316.     I := (L + H) shr 1;
  2317.     C := Compare(FList[I], Item);
  2318.     if C < 0 then L := I + 1 else
  2319.     begin
  2320.       H := I - 1;
  2321.       if C = 0 then
  2322.       begin
  2323.         Result := True;
  2324.         L := I;
  2325.       end;
  2326.     end;
  2327.   end;
  2328.   Index := L;
  2329.   FCache := Item;
  2330.   FCacheIndex := Index;
  2331.   FCacheFind := Result;
  2332. end;
  2333. function TbsBookmarkList.GetCount: Integer;
  2334. begin
  2335.   Result := FList.Count;
  2336. end;
  2337. function TbsBookmarkList.GetItem(Index: Integer): TBookmarkStr;
  2338. begin
  2339.   Result := FList[Index];
  2340. end;
  2341. function TbsBookmarkList.IndexOf(const Item: TBookmarkStr): Integer;
  2342. begin
  2343.   if not Find(Item, Result) then
  2344.     Result := -1;
  2345. end;
  2346. procedure TbsBookmarkList.LinkActive(Value: Boolean);
  2347. begin
  2348.   Clear;
  2349.   FLinkActive := Value;
  2350. end;
  2351. procedure TbsBookmarkList.Delete;
  2352. var
  2353.   I: Integer;