home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 October / Chip_2001-10_cd1.bin / zkuste / delphi / kolekce / d456 / DCSLIB25.ZIP / DCStringGrid.pas < prev    next >
Pascal/Delphi Source File  |  2001-06-26  |  33KB  |  1,077 lines

  1. {
  2.  BUSINESS CONSULTING
  3.  s a i n t - p e t e r s b u r g
  4.  
  5.          Components Library for Borland Delphi 4.x, 5.x
  6.          Copyright (c) 1998-2000 Alex'EM
  7.  
  8. }
  9. unit DCStringGrid;
  10.  
  11. interface
  12. uses
  13.   Windows, SysUtils, Messages, Classes, Controls, Forms, StdCtrls, Graphics,
  14.   Grids, Menus, CommCtrl,  DCChoice, DCKnots, DCStdCtrls, DCDBGrids,
  15.   DCEditTools, DCConst, DCData;
  16.  
  17. const
  18.   DFLAG_CHECKED = $1;
  19.   DFLAG_CHANGED = $2;
  20.  
  21. type
  22.   PColumnData_tag = ^TColumnData;
  23.   TColumnData = record
  24.     DataIndex: integer;
  25.     Name     : string;
  26.     Caption  : string;
  27.     FieldName: string;
  28.     LinkName : string;
  29.     Comment  : string;
  30.     DisplayFormat: string;
  31.     Alignment: TAlignment;
  32.     DataType : TDetailDataType;
  33.     EditType : TEditType;
  34.     MaxLength: integer;
  35.     MaxValue : integer;
  36.     Precision: integer;
  37.     Digits   : integer;
  38.     Width    : integer;
  39.     ItemIndex: integer;
  40.     KnotOptions : TKnotOptions;
  41.     EditOptions : TEditOptions;
  42.   end;
  43.  
  44.   TDCCustomStringGrid = class;
  45.  
  46.   TErrorCodeEvent  = procedure (ErrorCode: integer; P: Pointer) of object;
  47.   TGetRecordCode   = procedure (Sender: TObject; var Code: string) of object;
  48.   TInitDataItem    = procedure (Sender: TObject; ColumnData: TColumnData;
  49.     var RecordItem: TRecordItemData) of object;
  50.   TGetDataItem     = procedure (Sender: TObject; KnotItem: TKnotItem;
  51.     ColumnData: TColumnData; Control: TObject; var Value: string; var ChangeText: boolean) of object;
  52.   TSetDataItem     = procedure (Sender: TObject; Edit: TDCCustomChoiceEdit;
  53.     KnotItem: TKnotItem; ColumnData: TColumnData; var Value: string) of object;
  54.   TCheckDataEvent  = procedure (Sender: TObject; KnotItem: TKnotItem;
  55.     var DataValid: boolean) of object;
  56.   TDeleteDataEvent = procedure (Sender: TObject; KnotItem: TKnotItem;
  57.     Deleted: TList) of object;
  58.  
  59.   TDCCustomStringGrid = class(TDCCustomTreeGrid)
  60.   private
  61.     FColumnsData: TList;
  62.     FDeleted: TStringList;
  63.     FState: TJournalState;
  64.     FEditColData: TColumnData;
  65.     FOnError: TErrorCodeEvent;
  66.     FOnLoadData: TNotifyEvent;
  67.     FOnSaveData: TNotifyEvent;
  68.     FOnInplaceError: TGetErrorHint;
  69.     FOnInplaceKillFocus: TKillFocusEvent;
  70.     FOnGetRecordCode: TGetRecordCode;
  71.     FOnInitData: TInitDataItem;
  72.     FInplaceEdit: Pointer;
  73.     FParamCount: integer;
  74.     FOnGetDataItem: TGetDataItem;
  75.     FOnSetDataItem: TSetDataItem;
  76.     FGridImages: TImageList;
  77.     FOnCheckData: TCheckDataEvent;
  78.     FOnDeleteData: TDeleteDataEvent;
  79.     FTreeEnabled: boolean;
  80.     function IsUnique(Value: string; ColumnData: TColumnData): boolean;
  81.     function DefaultWidth(AEditType: TEditType; ADataType: TDetailDataType;
  82.       AMaxLength: integer): integer;
  83.     procedure SetState(const Value: TJournalState);
  84.     procedure InplaceKillFocus(Sender: TObject; var StayOnControl: Boolean);
  85.     procedure SaveDataItem(KnotItem: TKnotItem; Sender: TObject);
  86.     procedure SetTreeEnabled(const Value: boolean);
  87.     function  GetTreeEnabled: boolean;
  88.   protected
  89.     procedure UpdateRecordCountInfo;
  90.     procedure ClearColumnsData;
  91.     procedure LoadData; virtual;
  92.     procedure SaveData; virtual;
  93.     procedure PerformGridMessage(Msg: Cardinal); virtual;
  94.     procedure DoInitDataValue(var RecordItem: TRecordItemData; ColumnData: TColumnData); virtual;
  95.     function GetRecordCode: string; virtual;
  96.     procedure DoInsert(KnotItem: TKnotItem; var Apply: boolean); override;
  97.     procedure DoDelete(KnotItem: TKnotItem; var Apply: boolean;
  98.       ComponentState: TComponentState); override;
  99.     procedure DoUpdate(KnotItem: TKnotItem; var Edit: TDCCustomChoiceEdit;
  100.       Column: TKnotColumn); override;
  101.     procedure DoSelectCell(Sender: TObject; ACol, ARow: Longint;
  102.       var CanSelect: Boolean); override;
  103.     procedure DoCreateCellEdit(Column: TKnotColumn;
  104.       var Edit: TDCCustomChoiceEdit; var CanCreate: boolean); override;
  105.     procedure DoDestroyCellEdit; override;
  106.     procedure DoErrorCode(ErrorCode: integer; P: Pointer);
  107.     procedure DoInplaceError(Sender: TObject; ErrorCode: integer;
  108.       var ErrorHint: string); virtual;
  109.     procedure DoCheckCellEdit(Sender: TObject; var isError: boolean;
  110.       ColumnData: TColumnData); virtual;
  111.     procedure DoDrawColumnCell(Canvas: TCanvas; ARect: TRect; ACol: integer;
  112.       AColumn: TKnotColumn; AKnot: TKnotItem; AState: TGridDrawState); override;
  113.     procedure GetBookmarkData(KnotItem: TKnotItem; Data:Pointer); override;
  114.     procedure GSErrorCode(var Message: TMessage); message GS_ERRORCODE;
  115.     property OnError: TErrorCodeEvent read FOnError write FOnError;
  116.     property OnLoadData: TNotifyEvent read FOnLoadData write FOnLoadData;
  117.     property OnSaveData: TNotifyEvent read FOnSaveData write FOnSaveData;
  118.     property OnInplaceError: TGetErrorHint read FOnInplaceError write FOnInplaceError;
  119.     property OnInplaceKillFocus: TKillFocusEvent read FOnInplaceKillFocus write FOnInplaceKillFocus;
  120.     property State: TJournalState read FState write SetState;
  121.     property OnInitData: TInitDataItem read FOnInitData write FOnInitData;
  122.     property OnGetRecordCode: TGetRecordCode read FOnGetRecordCode write FOnGetRecordCode;
  123.     property OnGetDataItem: TGetDataItem read FOnGetDataItem write FOnGetDataItem;
  124.     property OnSetDataItem: TSetDataItem read FOnSetDataItem write FOnSetDataItem;
  125.     property TreeEnabled: boolean read FTreeEnabled write SetTreeEnabled default False;
  126.   public
  127.     constructor Create(AOwner: TComponent); override;
  128.     destructor Destroy; override;
  129.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  130.     procedure KeyPress(var Key: Char); override;
  131.     procedure UpdateGridColumns;
  132.     procedure InsertDataItem(EditorMode: boolean = True);
  133.     procedure DeleteDataItem;
  134.     function GetColumnsData(Name: string; var pColumnData: PColumnData_tag): boolean;
  135.     function GetDataItem(KnotItem: TKnotItem; ColumnData: TColumnData;
  136.       Sender: TObject; AQuoted: boolean = False): string;
  137.     function AddColumn(ADataType: TDetailDataType; AName, ACaption: string): PColumnData_tag;
  138.     procedure SetValue(ADataType: TDetailDataType; AText: string;
  139.       var ARecordItem: TRecordItemData);
  140.     function ValidRecord(KnotItem: TKnotItem): boolean; virtual;
  141.     function ValidEditValue: boolean;
  142.     procedure Load; virtual;
  143.     procedure Save; virtual;
  144.     property Deleted: TStringList read FDeleted;
  145.     property ColumnsData: TList read FColumnsData;
  146.     property ParamCount: integer read FParamCount write FParamCount;
  147.     property OnCheckData: TCheckDataEvent read FOnCheckData write FOnCheckData;
  148.     property OnDeleteData: TDeleteDataEvent read FOnDeleteData write FOnDeleteData;
  149.   end;
  150.  
  151.   TDCStringGrid = class(TDCCustomStringGrid)
  152.   public
  153.     property Canvas;
  154.     property Knots;
  155.     property ScrollBars;
  156.     property SelectedRows;
  157.     property SelectedKnot;
  158.     property SelectedIndex;
  159.     property Col;
  160.     property Row;
  161.     property RowCount;
  162.     property ColCount;
  163.     property FixedRows;
  164.     property FixedCols;
  165.     property State;
  166.     property RowModified;
  167.     property Columns;
  168.     property GroupBox;
  169.   published
  170.     property Align;
  171.     property Anchors;
  172.     property BiDiMode;
  173.     property BorderStyle;
  174.     property Color;
  175.     property Constraints;
  176.     property Ctl3D;
  177.     property DefaultDrawing;
  178.     property DragCursor;
  179.     property DragKind;
  180.     property DragMode;
  181.     property Enabled;
  182.     property FixedColor;
  183.     property Font;
  184.     property Options;
  185.     property OptionsEx;
  186.     property ParentBiDiMode;
  187.     property ParentColor;
  188.     property ParentCtl3D;
  189.     property ParentFont;
  190.     property ParentShowHint;
  191.     property PopupMenu;
  192.     property ShowHint;
  193.     property TabOrder;
  194.     property TabStop;
  195.     property Visible;
  196.     property OnColumnMoved;
  197.     property OnDblClick;
  198.     property OnDragDrop;
  199.     property OnDragOver;
  200.     property OnEndDock;
  201.     property OnEndDrag;
  202.     property OnEnter;
  203.     property OnExit;
  204.     property OnKeyDown;
  205.     property OnKeyPress;
  206.     property OnKeyUp;
  207.     property OnStartDock;
  208.     property OnStartDrag;
  209.     property Images;
  210.     property DefaultRowHeight;
  211.     property OnMouseDown;
  212.     property OnMouseMove;
  213.     property OnMouseUp;
  214.     property OnCellClick;
  215.     property OnTitleClick;
  216.     property OnClipClick;
  217.     property OnDelete;
  218.     property OnInsert;
  219.     property OnDrawColumnCell;
  220.     property OnTreeCellText;
  221.     property TreePathWidth;
  222.     property OnRowMoved;
  223.     property OnSelectCell;
  224.     property OnTopLeftChanged;
  225.     property OnCreateCellEdit;
  226.     property OnDestroyCellEdit;
  227.     property OnClipButtonClick;
  228.     property OnError;
  229.     property OnLoadData;
  230.     property OnSaveData;
  231.     property OnInplaceError;
  232.     property OnInplaceKillFocus;
  233.     property OnInitData;
  234.     property OnGetRecordCode;
  235.     property OnColumnComment;
  236.     property PopupTitle;
  237.     property OnGetDataItem;
  238.     property OnSetDataItem;
  239.     property OnSelectKnot;
  240.     property OnPaintMessage;
  241.     property OnCheckData;
  242.     property OnDeleteData;
  243.     property TreeEnabled;
  244.     property OnExpanded;
  245.     property OnCollapsed;
  246.   end;
  247.  
  248.   TPrivateChoiceEdit = class(TDCCustomChoiceEdit)
  249.   end;
  250.  
  251. implementation
  252.  
  253. { TDCCustomStringGrid }
  254.  
  255. function TDCCustomStringGrid.AddColumn(ADataType: TDetailDataType;
  256.   AName, ACaption: string): PColumnData_tag;
  257.  var
  258.   pColumnData: PColumnData_tag;
  259. begin
  260.   New(pColumnData);
  261.   with pColumnData^ do
  262.   begin
  263.     DataType    := ADataType;
  264.     Name        := AName;
  265.     Caption     := ACaption;
  266.     FieldName   := AName;
  267.     LinkName    := '';
  268.     EditType    := edEdit;
  269.     KnotOptions := [kcShowEdit, kcSizing, kcVisible, kcIndexed];
  270.     EditOptions := [eoCanEmpty];
  271.     MaxLength   := -1;
  272.     Precision   := -1;
  273.     Digits      := -1;
  274.     ItemIndex   := -1;
  275.     Width       := -1;
  276.     DataIndex   := FColumnsData.Count;
  277.     Alignment   := taLeftJustify;
  278.     MaxValue    := -1;
  279.   end;
  280.   FColumnsData.Add(pColumnData);
  281.   Result := pColumnData;
  282. end;
  283.  
  284. procedure TDCCustomStringGrid.ClearColumnsData;
  285.  var
  286.   i: integer;
  287.   pColumnData: PColumnData_tag;
  288. begin
  289.   for i := 0 to FColumnsData.Count-1 do
  290.   begin
  291.     pColumnData := FColumnsData.Items[i];
  292.     with pColumnData^ do
  293.     begin
  294.       SetLength(Name, 0);
  295.       SetLength(Caption, 0);
  296.       SetLength(FieldName, 0);
  297.       SetLength(LinkName, 0);
  298.       SetLength(LinkName, 0);
  299.       SetLength(Comment, 0);
  300.       SetLength(DisplayFormat, 0);
  301.     end;
  302.     FreeMem(pColumnData);
  303.   end;
  304.   FColumnsData.Clear;
  305. end;
  306.  
  307. constructor TDCCustomStringGrid.Create(AOwner: TComponent);
  308. begin
  309.   inherited;
  310.   FTreeEnabled := False;
  311.   FColumnsData := TList.Create;
  312.   FDeleted     := TStringList.Create;
  313.   FGridImages := ETGetSystemImages(DCGIM_SMALLICON);
  314. end;
  315.  
  316. function TDCCustomStringGrid.DefaultWidth(AEditType: TEditType;
  317.   ADataType: TDetailDataType; AMaxLength: integer): integer;
  318.  
  319.  const
  320.    ADefaultWidth: array[TDetailDataType] of integer =
  321.      (8, 0, 12, 8, 20);
  322.    //ddInteger, ddDate, ddFloat, ddCurrency, ddString
  323.  
  324.  var
  325.    BaseWidth, CharCount: integer;
  326.  
  327.  function GS_TextWidth(Value: string): integer;
  328.  begin
  329.    Result := GetTextWidth(Canvas.Handle, Value);
  330.  end;
  331.  
  332. begin
  333.   if AMaxLength = -1 then
  334.     CharCount := ADefaultWidth[ADataType]
  335.   else
  336.     CharCount := AMaxLength;
  337.  
  338.   case ADataType of
  339.     ddInteger : BaseWidth := CharCount * GS_TextWidth('9');
  340.     ddFloat   : BaseWidth := CharCount * GS_TextWidth('9') + GS_TextWidth(DecimalSeparator);
  341.     ddCurrency: BaseWidth := CharCount * GS_TextWidth('9') + GS_TextWidth(DecimalSeparator);
  342.     ddString  : BaseWidth := CharCount * GS_TextWidth('W');
  343.     ddDate    : BaseWidth := GS_TextWidth(ShortDateFormat) + 14;
  344.     else
  345.       BaseWidth := 0;
  346.   end;
  347.  
  348.   Result := BaseWidth;
  349.  
  350.   case AEditType of
  351.     edEdit  :;
  352.     edDate  :;
  353.     edGrid  : Result := Result + 14;
  354.     edChoice: Result := Result + 14;
  355.     edTree  : Result := Result + 14;
  356.     edCombo : Result := Result + 14;
  357.     edCheck : Result := FGridImages.Width + 4;
  358.   end;
  359.   Result := Result + 4;
  360. end;
  361.  
  362. procedure TDCCustomStringGrid.DeleteDataItem;
  363. begin
  364.   if (FState = jsBrowse) and (Knots.Count > 0) then
  365.     DeleteRecords(not(tgConfirmDelete in Options) or False);
  366. end;
  367.  
  368. destructor TDCCustomStringGrid.Destroy;
  369. begin
  370.   ClearColumnsData;
  371.   FColumnsData.Free;
  372.   FDeleted.Free;
  373.   inherited;
  374. end;
  375.  
  376. procedure TDCCustomStringGrid.DoCreateCellEdit(Column: TKnotColumn;
  377.   var Edit: TDCCustomChoiceEdit; var CanCreate: boolean);
  378.  var
  379.   pColumnData: PColumnData_tag;
  380.   AText: string;
  381. begin
  382.   State := jsEdit;
  383.   inherited DoCreateCellEdit(Column, Edit, CanCreate);
  384.  
  385.   if CanCreate and (Edit <> nil) and GetColumnsData(Column.Name, pColumnData) then
  386.     FEditColData := pColumnData^;
  387.  
  388.   if CanCreate and (FState = jsEdit) and not Assigned(Edit) and (Column <> nil) then
  389.   begin
  390.    if GetColumnsData(Column.Name, pColumnData) then
  391.    begin
  392.      {Initialize Data}
  393.      FEditColData := pColumnData^;
  394.      with FEditColData do
  395.      begin
  396.        case EditType of
  397.          edEdit  :
  398.            begin
  399.              case DataType of
  400.                ddDate, ddString:
  401.                  begin
  402.                    Edit := TDCInplaceChoiceEdit.Create(nil);
  403.                    with TDCInplaceChoiceEdit(Edit) do
  404.                    begin
  405.                      Grid   := Self;
  406.                      Visible:= False;
  407.                      Parent := Self;
  408.                      ButtonExist := False;
  409.                    end;
  410.                  end;
  411.                ddFloat, ddCurrency, ddInteger:
  412.                  begin
  413.                    Edit := TDCInplaceFloatEdit.Create(nil);
  414.                    with TDCInplaceFloatEdit(Edit) do
  415.                    begin
  416.                      Grid   := Self;
  417.                      Visible:= False;
  418.                      Parent := Self;
  419.                      case FEditColData.DataType of
  420.                        ddFloat:
  421.                          DataType.Kind := deFloat;
  422.                        ddCurrency:
  423.                          begin
  424.                            DataType.Kind := deCurrency;
  425.                            ButtonExist := False;
  426.                          end;
  427.                        ddInteger:
  428.                          begin
  429.                            DataType.Kind := deInteger;
  430.                            ButtonExist := False;
  431.                          end;
  432.                      end;
  433.                      DataType.Precision := Precision;
  434.                      DataType.Digits := Digits;
  435.                    end;
  436.                  end;
  437.              end;
  438.            end;
  439.          edDate  :
  440.            begin
  441.              Edit := TDCInplaceDateEdit.Create(nil);
  442.              with  TDCInplaceDateEdit(Edit) do
  443.              begin
  444.                Grid   := Self;
  445.                Visible:= False;
  446.                Parent := Self;
  447.              end;  
  448.            end;
  449.          edGrid  : {!!};
  450.          edChoice: {!!};
  451.          edTree  : {!!};
  452.          edCombo : {!!};
  453.          edCheck :
  454.            begin
  455.              if not(kcReadOnly in Column.Options) then
  456.              begin
  457.                AText := GetDataItem(SelectedKnot, FEditColData, nil);
  458.                if IsValidInteger(AText) then
  459.                begin
  460.                  if MaxValue > -1 then
  461.                    AText := IntToStr(StrToInt(AText) + 1 div MaxValue)
  462.                  else
  463.                    AText := IntToStr(StrToInt(AText) + 1);
  464.                  if Assigned(FOnSetDataItem) then
  465.                    FOnSetDataItem(Self, nil, SelectedKnot, FEditColData, AText);
  466.                  Knots.BeginUpdate;
  467.                  SetValue(FEditColData.DataType, AText,
  468.                      PRecordData_tag(SelectedKnot.Data)^.Data[FEditColData.DataIndex]);
  469.                  Knots.EndUpdate;
  470.                end;
  471.              end;
  472.            end;
  473.          edInfo  : { nothing };
  474.        end;
  475.      end;
  476.    end;
  477.   end;
  478.   if Assigned(Edit) then
  479.   begin
  480.     with TPrivateChoiceEdit(Edit) do
  481.     begin
  482.       PerformCloseUp := True;
  483.       CanEmpty       := eoCanEmpty in pColumnData^.EditOptions;
  484.       OnKillFocus    := InplaceKillFocus;
  485.       OnGetErrorHint := DoInplaceError;
  486.       GetDataItem(SelectedKnot, FEditColData, Edit);
  487.     end;
  488.     Edit.MaxLength   := FEditColData.MaxLength;
  489.   end
  490.   else
  491.     State := jsBrowse;
  492.  
  493.   FInplaceEdit := Edit;
  494. end;
  495.  
  496. procedure TDCCustomStringGrid.DoDelete(KnotItem: TKnotItem;
  497.   var Apply: boolean; ComponentState: TComponentState);
  498.  var
  499.   pRecordData: PRecordData_tag;
  500. begin
  501.   if not((csDestroying in ComponentState) or (KnotItem.Owner.State = ksUpdate)) then
  502.     inherited
  503.   else
  504.     Apply := True;
  505.  
  506.   if Apply and (csDestroying in ComponentState)then
  507.   begin
  508.     pRecordData := KnotItem.Data;
  509.     if (pRecordData <> nil) and (pRecordData^.State<>rsInserted) and
  510.        (FState = jsBrowse)  and (KnotItem.Owner.State <> ksUpdate)
  511.     then begin
  512.       if Assigned(FOnDeleteData) then
  513.         FOnDeleteData(Self, KnotItem, Pointer(FDeleted))
  514.       else
  515.         FDeleted.Add(pRecordData^.Code);
  516.     end;
  517.     Perform(GS_ERRORCODE, 0, ERR_EDIT_NONE);
  518.     RDFree(pRecordData);
  519.  
  520.     UpdateRecordCountInfo;
  521.   end;
  522.  
  523. end;
  524.  
  525. procedure TDCCustomStringGrid.DoErrorCode(ErrorCode: integer; P: Pointer);
  526. begin
  527.   if Assigned(FOnError) then FOnError(ErrorCode, P);
  528. end;
  529.  
  530. procedure TDCCustomStringGrid.DoInplaceError(Sender: TObject;
  531.   ErrorCode: integer; var ErrorHint: string);
  532. begin
  533.   case ErrorCode of
  534.     ERR_EDIT_NEEDUNIQ      : ErrorHint := LoadStr(RES_EDIT_ERR_UNIQ);
  535.   end;
  536.   if Assigned(FOnInplaceError) then
  537.     FOnInplaceError(Sender, ErrorCode, ErrorHint);
  538. end;
  539.  
  540. procedure TDCCustomStringGrid.DoInitDataValue( var RecordItem: TRecordItemData;
  541.   ColumnData: TColumnData);
  542. begin
  543.   with RecordItem do
  544.   begin
  545.     DISetFlag(RecordItem, DFLAG_CHECKED, True);
  546.     DISetFlag(RecordItem, DFLAG_CHANGED, True);
  547.     case ColumnData.DataType of
  548.       ddInteger:
  549.         DISetValue(RecordItem, daInteger, '0');
  550.       ddDate, ddFloat, ddCurrency:
  551.         DISetValue(RecordItem, daFloat, '0');
  552.       ddString:
  553.         DISetValue(RecordItem, daString, '');
  554.     end;
  555.     if Assigned(FOnInitData) then FOnInitData(Self, ColumnData, RecordItem);
  556.   end;
  557. end;
  558.  
  559. procedure TDCCustomStringGrid.DoInsert(KnotItem: TKnotItem;
  560.   var Apply: boolean);
  561.  var
  562.   pRecordData: PRecordData_tag;
  563.   pColumnData: PColumnData_tag;
  564.   i: integer;
  565.   ACode: string;
  566. begin
  567.   inherited;
  568.   if not GetTreeEnabled and (KnotItem.Parent.Level <> -1) then Apply := False;
  569.  
  570.   if Apply then
  571.   begin
  572.     pRecordData := RDCreate(ParamCount);
  573.     ACode := GetRecordCode;
  574.     RDSetCode(pRecordData, PChar(ACode), Length(ACode));
  575.  
  576.     case FState of
  577.       jsLoad  : RDSetState(pRecordData, rsNotChanged);
  578.       jsBrowse: RDSetState(pRecordData, rsInserted);
  579.     end;
  580.  
  581.     for i := 0 to FColumnsData.Count-1 do
  582.     begin
  583.       pColumnData := FColumnsData.Items[i];
  584.       DoInitDataValue(pRecordData^.Data[pColumnData.DataIndex],  pColumnData^);
  585.     end;
  586.     KnotItem.Data := pRecordData;
  587.  
  588.     Perform(GS_ERRORCODE, 0, ERR_EDIT_NONE);
  589.     UpdateRecordCountInfo;
  590.   end;
  591. end;
  592.  
  593. procedure TDCCustomStringGrid.DoSelectCell(Sender: TObject; ACol,
  594.   ARow: Integer; var CanSelect: Boolean);
  595. begin
  596.   if (ARow<>Row) and (Knots.State = ksInsert)
  597.   then begin
  598.     if not ValidRecord(SelectedKnot) then
  599.       CanSelect := False
  600.     else
  601.       Perform(GS_ERRORCODE, 0, ERR_EDIT_NONE);
  602.   end;
  603.   inherited;
  604. end;
  605.  
  606. procedure TDCCustomStringGrid.DoUpdate(KnotItem: TKnotItem;
  607.   var Edit: TDCCustomChoiceEdit; Column: TKnotColumn);
  608. begin
  609.   with PRecordData_tag(KnotItem.Data)^ do
  610.     if State = rsNotChanged then State := rsUpdated;
  611.   SaveDataItem(KnotItem, Edit);
  612. end;
  613.  
  614. function TDCCustomStringGrid.GetColumnsData(Name: string;
  615.   var pColumnData: PColumnData_tag): boolean;
  616.  var
  617.   i: integer;
  618. begin
  619.   for i := 0 to FColumnsData.Count-1 do
  620.   begin
  621.     pColumnData := FColumnsData.Items[i];
  622.     if UpperCase(Name) = UpperCase(pColumnData^.Name) then
  623.     begin
  624.       Result := True;
  625.       Exit;
  626.     end;
  627.   end;
  628.   Result := False;
  629. end;
  630.  
  631. function TDCCustomStringGrid.GetDataItem(KnotItem: TKnotItem;
  632.   ColumnData: TColumnData; Sender: TObject; AQuoted: boolean): string;
  633.  var
  634.   pRecordData: PRecordData_tag;
  635.   ChangeText: boolean;
  636. begin
  637.   pRecordData := KnotItem.Data;
  638.   if pRecordData <> nil then
  639.   begin
  640.     with ColumnData, pRecordData^.Data[DataIndex] do
  641.     begin
  642.       case ColumnData.DataType of
  643.         ddInteger :
  644.           if ColumnData.DisplayFormat <> '' then
  645.             Result := Format(ColumnData.DisplayFormat,[Value])
  646.           else
  647.             Result := IntToStr(Value);
  648.         ddDate    :
  649.           begin
  650.             DateToStrY2K(Data, Result);
  651.             if AQuoted then Result := Format('''%s''',[Result]);
  652.           end;
  653.         ddFloat   :
  654.           if ColumnData.DisplayFormat <> '' then
  655.             Result := Format(ColumnData.DisplayFormat,[Data])
  656.           else
  657.             Result := FloatToStr(Data);
  658.         ddCurrency:
  659.           if ColumnData.DisplayFormat <> '' then
  660.             Result := Format(ColumnData.DisplayFormat,[Data])
  661.           else
  662.             Result := FloatToStr(Data);
  663.         ddString  :
  664.           begin
  665.             Result := DIGetValue(pRecordData^.Data[DataIndex]);
  666.             if AQuoted then Result := Format('"%s"',[Result]);
  667.           end;
  668.       end;
  669.       ChangeText := True;
  670.       if Assigned(FOnGetDataItem) then
  671.         FOnGetDataItem(Self, KnotItem, ColumnData, Sender, Result, ChangeText);
  672.       if (Sender <> nil) and ChangeText then TPrivateChoiceEdit(Sender).Text := Result;
  673.     end;
  674.   end;
  675. end;
  676.  
  677. function TDCCustomStringGrid.GetRecordCode: string;
  678. begin
  679.   if Assigned(FOnGetRecordCode) then
  680.     FOnGetRecordCode(Self, Result)
  681.   else
  682.     Result := STGird_Empty_CODE;
  683. end;
  684.  
  685. procedure TDCCustomStringGrid.GSErrorCode(var Message: TMessage);
  686. begin
  687.   if not(csDestroying in ComponentState) and
  688.     (FState <> jsLoad) and (FState <> jsSave) then
  689.     with Message do
  690.     begin
  691.       if WParam = 0 then
  692.         DoErrorCode(LParam, nil)
  693.       else
  694.         DoErrorCode(LParam, Pointer(WParam));
  695.     end;
  696. end;
  697.  
  698. procedure TDCCustomStringGrid.InsertDataItem(EditorMode: boolean);
  699.  var
  700.   Key: Word;
  701.   ACol: integer;
  702. begin
  703.   {─εßαΓδσφΦσ φεΓεΘ τα∩Φ±Φ}
  704.   if State = jsBrowse then
  705.   begin
  706.     Row  := RowCount-1;
  707.     Key  := VK_DOWN; KeyDown(Key, []);
  708.     Col  := FixedCols;
  709.     ACol := Col-FixedCols;
  710.     while ACol < Columns.Count do
  711.     begin
  712.       if Columns[ACol].Options*[kcReadOnly,kcShowEdit]=[kcShowEdit]
  713.       then begin
  714.         Col := ACol+FixedCols;
  715.         if EditorMode then ShowEditor;
  716.         Break;
  717.       end;
  718.       Inc(ACol);
  719.     end;
  720.   end;
  721. end;
  722.  
  723. function TDCCustomStringGrid.IsUnique(Value: string;
  724.   ColumnData: TColumnData): boolean;
  725.  var
  726.   KnotItem: TKnotItem;
  727.   sText: string;
  728. begin
  729.   Result := True;
  730.   with Knots do
  731.   begin
  732.     KnotItem := GetFirstVisibleNode;
  733.     while KnotItem <> nil do
  734.     begin
  735.       if KnotItem.KnotID <> SelectedKnot.KnotID then
  736.       begin
  737.         sText := GetDataItem(KnotItem, ColumnData, nil);
  738.         if AnsiUpperCase(sText) = AnsiUpperCase(Value) then
  739.         begin
  740.           Result    := False;
  741.           Break;
  742.         end;
  743.       end;
  744.       KnotItem := KnotItem.GetNextVisible;
  745.     end;
  746.   end;
  747. end;
  748.  
  749. function TDCCustomStringGrid.ValidRecord(KnotItem: TKnotItem): boolean;
  750.  var
  751.   pSelectData: PRecordData_tag;
  752.   pColumnData: PColumnData_tag;
  753.   i: integer;
  754.   ItemData: TRecordItemData;
  755.   sText: string;
  756. begin
  757.   Result    := True;
  758.  
  759.   if Assigned(KnotItem) then
  760.     pSelectData := KnotItem.Data
  761.   else
  762.     pSelectData := nil;
  763.  
  764.   if not Assigned(pSelectData) then Exit;
  765.  
  766.   for i := 0 to FColumnsData.Count-1 do
  767.   begin
  768.     pColumnData := FColumnsData.Items[i];
  769.     ItemData    := pSelectData^.Data[pColumnData^.DataIndex];
  770.     if DIGetFlag(ItemData, DFLAG_CHECKED) = 0 then
  771.     begin
  772.       if eoUnique in pColumnData^.EditOptions then
  773.       begin
  774.         sText := GetDataItem(SelectedKnot, pColumnData^, nil);
  775.         if (sText <> '') and not IsUnique(sText, pColumnData^) then
  776.         begin
  777.           Result    := False;
  778.           Perform(GS_ERRORCODE, Integer(pColumnData), ERR_EDIT_NEEDUNIQ);
  779.           Exit;
  780.         end;
  781.       end;
  782.       if not(eoCanEmpty in pColumnData^.EditOptions) then
  783.       begin
  784.         sText := GetDataItem(SelectedKnot, pColumnData^, nil);
  785.         if sText = '' then
  786.         begin
  787.           Result     := False;
  788.           Perform(GS_ERRORCODE, Integer(pColumnData), ERR_EDIT_EMPTYVALUE);
  789.           Exit;
  790.         end;
  791.       end;
  792.       DISetFlag(ItemData, DFLAG_CHECKED)
  793.     end;
  794.     if Assigned(FOnCheckData) then FOnCheckData(Self, KnotItem, Result);
  795.   end;
  796. end;
  797.  
  798. procedure TDCCustomStringGrid.KeyDown(var Key: Word; Shift: TShiftState);
  799. begin
  800.   inherited;
  801.   case Key of
  802.     VK_INSERT:
  803.       begin
  804.         if ssCtrl in Shift then Key := 0;
  805.       end;
  806.   end
  807. end;
  808.  
  809. procedure TDCCustomStringGrid.KeyPress(var Key: Char);
  810. begin
  811.   inherited;
  812. end;
  813.  
  814. procedure TDCCustomStringGrid.LoadData;
  815. begin
  816.   {╟απ≡≤τΩα Σαφφ√⌡}
  817.   if Assigned(FOnLoadData) then FOnLoadData(Self);
  818. end;
  819.  
  820. procedure TDCCustomStringGrid.SaveData;
  821. begin
  822.   {╤ε⌡≡αφσφΦσ Σαφφ√⌡}
  823.   if Assigned(FOnSaveData) then FOnSaveData(Self);
  824. end;
  825.  
  826. procedure TDCCustomStringGrid.SetState(const Value: TJournalState);
  827. begin
  828.   if Value <> FState then
  829.   begin
  830.     FState := Value;
  831.     case Value of
  832.       jsLoad,
  833.       jsSave:
  834.         begin
  835.           Options := Options - [tgEditing];
  836.           Cursor  := crHourGlass;
  837.         end;
  838.       jsBrowse:
  839.         begin
  840.           Options := Options + [tgEditing];
  841.           Cursor := crDefault;
  842.         end;
  843.       jsEdit:
  844.         begin
  845.           {}
  846.         end;
  847.       jsView:
  848.         begin
  849.           Options := Options - [tgEditing];
  850.           Cursor  := crDefault;
  851.         end;
  852.     end;
  853.     PerformGridMessage(GS_UPDATE_STATE);
  854.   end;
  855. end;
  856.  
  857. procedure TDCCustomStringGrid.UpdateGridColumns;
  858.  var
  859.   i: integer;
  860.   KnotColumn: TKnotColumn;
  861.   pColumnData: PColumnData_tag;
  862. begin
  863.   BeginLayout;
  864.   Columns.Clear;
  865.   for i := 0 to FColumnsData.Count-1 do
  866.   begin
  867.     pColumnData := FColumnsData.Items[i];
  868.     KnotColumn  := Columns.Add;
  869.     with pColumnData^ do
  870.     begin
  871.       KnotColumn.Alignment     := Alignment;
  872.       KnotColumn.Comment       := Comment;
  873.       KnotColumn.Name          := Name;
  874.       KnotColumn.Title.Caption := Caption;
  875.       KnotColumn.Options       := KnotOptions;
  876.       if Width = -1 then
  877.         KnotColumn.Width       := DefaultWidth(EditType, DataType, MaxLength)
  878.       else
  879.         KnotColumn.Width       := Width;
  880.       case EditType of
  881.         edCheck:
  882.           KnotColumn.Options := KnotColumn.Options - [kcSizing];
  883.       end;
  884.       KnotColumn.ItemIndex     := ItemIndex;
  885.       KnotColumn.DisplayFormat := DisplayFormat;
  886.     end;
  887.   end;
  888.   EndLayout;
  889.   if HandleAllocated then SetScrollRange(Handle, SB_HORZ, 0, 0, True);
  890. end;
  891.  
  892. procedure TDCCustomStringGrid.PerformGridMessage(Msg: Cardinal);
  893.  var
  894.   ParentForm: TCustomForm;
  895. begin
  896.   if not(csDestroying in ComponentState) then
  897.   begin
  898.     ParentForm := GetParentForm(Parent);
  899.     if Assigned(ParentForm) then ParentForm.Perform(Msg, Integer(Self), 0);
  900.   end;
  901. end;
  902.  
  903. procedure TDCCustomStringGrid.InplaceKillFocus(Sender: TObject;
  904.   var StayOnControl: Boolean);
  905. begin
  906.   DoCheckCellEdit(Sender, StayOnControl, FEditColData);
  907. end;
  908.  
  909. procedure TDCCustomStringGrid.DoCheckCellEdit(Sender: TObject;
  910.   var isError: boolean; ColumnData: TColumnData);
  911. begin
  912.   with TPrivateChoiceEdit(Sender) do
  913.   begin
  914.     if (Text <> '') or not(CanEmpty) then
  915.     begin
  916.       if Assigned(FOnInplaceKillFocus) then FOnInplaceKillFocus(Sender, isError);
  917.       if not isError then
  918.       begin
  919.         if not isError and (eoUnique in ColumnData.EditOptions) then
  920.         begin
  921.           isError := not IsUnique(Text, ColumnData);
  922.           if isError then ErrorCode := ERR_EDIT_NEEDUNIQ;
  923.         end;
  924.       end;
  925.     end;
  926.   end;
  927. end;
  928.  
  929. procedure TDCCustomStringGrid.DoDestroyCellEdit;
  930. begin
  931.   State := jsBrowse;
  932.   FInplaceEdit := nil;
  933.   inherited;
  934. end;
  935.  
  936. procedure TDCCustomStringGrid.SaveDataItem(KnotItem: TKnotItem;
  937.   Sender: TObject);
  938.  var
  939.   AValue: string;
  940. begin
  941.   AValue := TPrivateChoiceEdit(Sender).Text;
  942.   if Assigned(FOnSetDataItem) then
  943.     FOnSetDataItem(Self, TPrivateChoiceEdit(Sender), KnotItem, FEditColData, AValue);
  944.  
  945.   SetValue(FEditColData.DataType, AValue,
  946.     PRecordData_tag(KnotItem.Data)^.Data[FEditColData.DataIndex]);
  947. end;
  948.  
  949. procedure TDCCustomStringGrid.Load;
  950. begin
  951.   {╟απ≡≤τΩα Σαφφ√⌡}
  952.   State := jsLoad;
  953.   Application.ProcessMessages;
  954.   LoadData;
  955.   if State <> jsView then State := jsBrowse;
  956. end;
  957.  
  958. procedure TDCCustomStringGrid.Save;
  959. begin
  960.   {╤ε⌡≡αφσφΦσ Σαφφ√⌡}
  961.   State := jsSave;
  962.   Application.ProcessMessages;
  963.   SaveData;
  964.   if State <> jsView then State := jsBrowse;
  965. end;
  966.  
  967. procedure TDCCustomStringGrid.SetValue(ADataType: TDetailDataType;
  968.   AText: string; var ARecordItem: TRecordItemData);
  969. begin
  970.   with ARecordItem do
  971.   begin
  972.     DISetFlag(ARecordItem, DFLAG_CHECKED);
  973.     case ADataType of
  974.       ddInteger:
  975.         DISetValue(ARecordItem, daInteger, AText);
  976.       ddDate, ddFloat, ddCurrency:
  977.         DISetValue(ARecordItem, daFloat, AText);
  978.       ddString:
  979.         DISetValue(ARecordItem, daString, AText);
  980.     end;
  981.     if FState <> jsLoad then
  982.       DISetFlag(ARecordItem, DFLAG_CHANGED)
  983.     else
  984.       DISetFlag(ARecordItem, DFLAG_CHANGED, True);
  985.   end;
  986. end;
  987.  
  988. procedure TDCCustomStringGrid.DoDrawColumnCell(Canvas: TCanvas;
  989.   ARect: TRect; ACol: integer; AColumn: TKnotColumn; AKnot: TKnotItem;
  990.   AState: TGridDrawState);
  991.  const
  992.   AlignFlags : array [TAlignment] of Integer =
  993.     ( DT_LEFT   or DT_NOPREFIX or DT_END_ELLIPSIS or DT_EXPANDTABS,
  994.       DT_RIGHT  or DT_NOPREFIX or DT_END_ELLIPSIS or DT_EXPANDTABS,
  995.       DT_CENTER or DT_NOPREFIX or DT_END_ELLIPSIS or DT_EXPANDTABS );
  996.  var
  997.   AText, AFormat: string;
  998.   pColumnData: PColumnData_tag;
  999.   R:TRect;
  1000.   nIndex: integer;
  1001. begin
  1002.   R := ARect;
  1003.   if GetColumnsData(AColumn.Name, pColumnData) then
  1004.   begin
  1005.     InflateRect(R, -2, -1);
  1006.     case pColumnData^.EditType of
  1007.       edCheck:
  1008.       begin
  1009.         AText := GetDataItem(AKnot, pColumnData^, nil);
  1010.         if IsValidInteger(AText) then
  1011.         begin
  1012.           case StrToInt(AText) of
  1013.             0: nIndex := nsiNormalCheck0;
  1014.             1: nIndex := nsiNormalCheck1;
  1015.             2: nIndex := nsiNormalCheckX;
  1016.             else
  1017.               nIndex := nsiNormalCheck0;
  1018.           end;
  1019.           if Focused and (gdSelected in AState) then
  1020.             AFormat := Format('/is{%d}', [nIndex])
  1021.           else
  1022.             AFormat := Format('/im{%d}', [nIndex]);
  1023.           DrawHighLightText(Canvas, PChar(AFormat), R, 1, DT_CENTER, FGridImages);
  1024.         end;
  1025.       end;
  1026.       else begin
  1027.         AText := GetDataItem(AKnot, pColumnData^, nil);
  1028.         DrawText(Canvas.Handle, PChar(AText), Length(AText), R, AlignFlags[AColumn.Alignment]);
  1029.       end;
  1030.     end;
  1031.   end;
  1032.   inherited;
  1033. end;
  1034.  
  1035. procedure TDCCustomStringGrid.UpdateRecordCountInfo;
  1036. begin
  1037.   if FState = jsBrowse then PerformGridMessage(GS_UPDATE_RECORDCOUNT);
  1038. end;
  1039.  
  1040. function TDCCustomStringGrid.ValidEditValue: boolean;
  1041. begin
  1042.   if (State = jsEdit) and Assigned(FInplaceEdit) then
  1043.   begin
  1044.     DoCheckCellEdit(TPrivateChoiceEdit(FInplaceEdit), Result, FEditColData);
  1045.     Perform(GS_ERRORCODE, Integer(@FEditColData),
  1046.       TPrivateChoiceEdit(FInplaceEdit).ErrorCode);
  1047.     Result := not Result;
  1048.   end
  1049.   else
  1050.     Result := True;
  1051. end;
  1052.  
  1053. procedure TDCCustomStringGrid.GetBookmarkData(KnotItem: TKnotItem;
  1054.   Data: Pointer);
  1055.  var
  1056.   AData: string;
  1057. begin
  1058.   AData := '';
  1059.   if KnotItem.Data <> nil then AData := RDGetCode(KnotItem.Data);
  1060.   if AData = '' then AData := IntToStr(KnotItem.KnotID);
  1061.  
  1062.   if KnotItem.Data <> nil then
  1063.     StrLCopy(PChar(Data), PChar(AData), BookMarkSize-1)
  1064. end;
  1065.  
  1066. procedure TDCCustomStringGrid.SetTreeEnabled(const Value: boolean);
  1067. begin
  1068.   FTreeEnabled := Value;
  1069. end;
  1070.  
  1071. function TDCCustomStringGrid.GetTreeEnabled: boolean;
  1072. begin
  1073.   Result := FTreeEnabled or (GroupingEnabled and (GroupBox.Count > 0));
  1074. end;
  1075.  
  1076. end.
  1077.