ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/IBDynamicGrid.pas
(Generate patch)

Comparing ibx/trunk/ibcontrols/IBDynamicGrid.pas (file contents):
Revision 21 by tony, Thu Feb 26 10:33:34 2015 UTC vs.
Revision 39 by tony, Tue May 17 08:14:52 2016 UTC

# Line 15 | Line 15
15   *
16   *  The Initial Developer of the Original Code is Tony Whyman.
17   *
18 < *  The Original Code is (C) 2011 Tony Whyman, MWA Software
18 > *  The Original Code is (C) 2015 Tony Whyman, MWA Software
19   *  (http://www.mwasoftware.co.uk).
20   *
21   *  All Rights Reserved.
# Line 23 | Line 23
23   *  Contributor(s): ______________________________________.
24   *
25   *)
26 +
27   unit IBDynamicGrid;
28  
29   {$mode objfpc}{$H+}
# Line 31 | Line 32 | interface
32  
33   uses
34    Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, DBGrids, DB,
35 <  IBSqlParser, Grids, IBLookupComboEditBox, LMessages, StdCtrls, ExtCtrls;
35 >  IBSQLParser, Grids, IBLookupComboEditBox, LMessages, StdCtrls, ExtCtrls,
36 >  IBCustomDataSet;
37  
38   type
39    {
# Line 55 | Line 57 | type
57    TOnUpdateSortOrder = procedure (Sender: TObject; ColIndex: integer; var OrderBy: string) of Object;
58    TKeyDownHandler = procedure (Sender: TObject; var Key: Word;  Shift: TShiftState; var Done: boolean) of object;
59  
58  { TDynamicGridDataLink }
59
60  TDynamicGridDataLink = class(TDataLink)
61  private
62    FOwner: TIBDynamicGrid;
63  protected
64    procedure DataEvent(Event: TDataEvent; Info: Ptrint); override;
65    procedure DataSetScrolled(Distance: Integer); override;
66  public
67    constructor Create(AOwner: TIBDynamicGrid);
68  end;
69
60    { TDBDynamicGridColumn }
61  
62    TDBDynamicGridColumn = class(TColumn)
# Line 102 | Line 92 | type
92      FListSource: TDataSource;
93      FOnAutoInsert: TAutoInsert;
94      FOnCanAutoInsert: TCanAutoInsert;
95 +    FOnCloseUp: TNotifyEvent;
96      FOnDrawItem: TDrawItemEvent;
97      FOwner: TIBDynamicGridColumn;
98      FRelationName: string;
# Line 128 | Line 119 | type
119      property Style: TComboBoxStyle read FStyle write FStyle default csDropDown;
120      property OnAutoInsert: TAutoInsert read FOnAutoInsert write FOnAutoInsert;
121      property OnCanAutoInsert: TCanAutoInsert read FOnCanAutoInsert write FOnCanAutoInsert;
122 +    property OnCloseUp: TNotifyEvent read FOnCloseUp write FOnCloseUp;
123      property OnDrawItem: TDrawItemEvent read FOnDrawItem write FOnDrawItem;
124   end;
125  
# Line 158 | Line 150 | end;
150      FGrid: TCustomGrid;
151      FCol,FRow: Integer;
152      FEditText: string;
153 +    function EditingKeyField: boolean;
154    protected
155      procedure WndProc(var TheMessage : TLMessage); override;
156      procedure CloseUp; override;
157      procedure KeyDown(var Key : Word; Shift : TShiftState); override;
158 +    procedure Loaded; override;
159      procedure msg_GetValue(var Msg: TGridMessage); message GM_GETVALUE;
160      procedure msg_SetGrid(var Msg: TGridMessage); message GM_SETGRID;
161      procedure msg_SetValue(var Msg: TGridMessage); message GM_SETVALUE;
# Line 187 | Line 181 | end;
181      FWeHaveFocus: boolean;
182      FHidingEditorPanel: boolean;
183      FAllowHide: boolean;
184 +    FMouseDown: boolean;
185 +    function ActiveControl: TControl;
186      procedure DoShowEditorPanel(Data: PtrInt);
187      procedure PositionTotals;
188      procedure KeyDownHandler(Sender: TObject; var Key: Word; Shift: TShiftState);
189 +    procedure PerformEditorHide(Data: PtrInt);
190      procedure SetEditorPanel(AValue: TWinControl);
191    protected
192      procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer; KeepBase: boolean); override;
# Line 219 | Line 216 | end;
216      constructor Create(TheComponent: TComponent); override;
217      destructor Destroy ;override;
218      procedure ResizeColumns;
219 +    property VisibleRowCount;
220    published
221      property EditorPanel: TWinControl read FEditorPanel write SetEditorPanel;
222      property ExpandEditorPanelBelowRow: boolean read FExpandEditorPanelBelowRow write FExpandEditorPanelBelowRow;
# Line 228 | Line 226 | end;
226      property OnKeyDownHander: TKeyDownHandler read FOnKeyDownHander write FOnKeyDownHander;
227   end;
228  
229 +  {TIBGridControlLink}
230 +
231 +  TIBGridControlLink = class(TIBControlLink)
232 +  private
233 +    FOwner: TIBDynamicGrid;
234 +  protected
235 +    procedure UpdateSQL(Sender: TObject); override;
236 +  public
237 +    constructor Create(AOwner: TIBDynamicGrid);
238 +  end;
239 +
240 +  TLocationArray = array of variant;
241 +  PLocationArray = ^TLocationArray;
242 +  TOnRestorePosition = procedure(Sender: TObject; Location: PLocationArray) of object;
243 +
244    { TIBDynamicGrid }
245  
246    TIBDynamicGrid = class(TDBDynamicGrid)
247    private
248      { Private declarations }
249      FAllowColumnSort: boolean;
250 <    FDataLink: TDynamicGridDataLink;
250 >    FIBControlLink: TIBGridControlLink;
251      FOnColumnHeaderClick: TOnColumnHeaderClick;
252 +    FOnRestorePosition: TOnRestorePosition;
253      FOnUpdateSortOrder: TOnUpdateSortOrder;
254      FDefaultPositionAtEnd: boolean;
255      FDescending: boolean;
# Line 243 | Line 257 | end;
257      FLastColIndex: integer;
258      FIndexFieldNames: string;
259      FIndexFieldsList: TStringList;
260 <    FBookmark: array of variant;
260 >    FBookmark: TLocationArray;
261      FDBLookupCellEditor: TDBLookupCellEditor;
262      FActive: boolean;
263 +    FFieldPosition: integer;
264      procedure ColumnHeaderClick(Index: integer);
265      function GetDataSource: TDataSource;
266      function GetEditorBorderStyle: TBorderStyle;
267 +    procedure IBControlLinkChanged;
268      procedure SetDataSource(AValue: TDataSource);
269      procedure SetEditorBorderStyle(AValue: TBorderStyle);
270      procedure ProcessColumns;
271      procedure SetIndexFieldNames(AValue: string);
272      procedure UpdateSQL(Sender: TObject; Parser: TSelectSQLParser);
273      procedure UpdateSortColumn(Sender: TObject);
274 <    procedure DataSetScrolled(Sender: TObject);
275 <    procedure RestorePosition(Data: PtrInt);
274 >    procedure RestorePosition;
275 >    procedure SavePosition;
276      procedure DoReOpen(Data: PtrInt);
277      procedure SetupEditor(aEditor: TDBLookupCellEditor; aCol: integer);
278    protected
# Line 266 | Line 282 | end;
282      function  CreateColumns: TGridColumns; override;
283      procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
284      procedure LinkActive(Value: Boolean); override;
285 +    procedure MoveSelection; override;
286      procedure Notification(AComponent: TComponent; Operation: TOperation); override;
287      procedure UpdateActive; override;
288    public
# Line 283 | Line 300 | end;
300      property DefaultPositionAtEnd: boolean read  FDefaultPositionAtEnd write FDefaultPositionAtEnd;
301      property IndexFieldNames: string read FIndexFieldNames write SetIndexFieldNames;
302      property OnColumnHeaderClick: TOnColumnHeaderClick read FOnColumnHeaderClick write FOnColumnHeaderClick;
303 +    property OnRestorePosition: TOnRestorePosition read FOnRestorePosition write FOnRestorePosition;
304      property OnUpdateSortOrder: TOnUpdateSortOrder read FOnUpdateSortOrder write FOnUpdateSortOrder;
305   end;
306  
307   implementation
308  
309 < uses Math, IBQuery, IBCustomDataSet, LCLType;
309 > uses Math, IBQuery, LCLType, Variants;
310 >
311 > { TIBGridControlLink }
312 >
313 > constructor TIBGridControlLink.Create(AOwner: TIBDynamicGrid);
314 > begin
315 >  inherited Create;
316 >  FOwner := AOwner;
317 > end;
318 >
319 > procedure TIBGridControlLink.UpdateSQL(Sender: TObject);
320 > begin
321 >  FOwner.UpdateSQL(self,TIBParserDataSet(Sender).Parser)
322 > end;
323  
324   { TDBLookupProperties }
325  
# Line 335 | Line 366 | begin
366    FResizing := true;
367    try
368      ColSum := 0;
338    for I := 0 to  ColCount - 1 do
339       ColSum := ColSum + ColWidths[I];
369  
370 <    if Colsum <> ClientWidth then
370 >    if (ColCount = 1) and TDBDynamicGridColumn(Columns[0]).AutoSizeColumn then
371 >      Columns[0].Width := ClientWidth
372 >    else
373      begin
374 <      ResizeColCount := 0;
375 <      for I := 0 to Columns.Count -1 do
376 <        if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn then
377 <        begin
378 <          Inc(ResizeColCount);
379 <          Colsum := Colsum + TDBDynamicGridColumn(Columns[I]).DesignWidth - Columns[I].Width;
380 <          Columns[I].Width := TDBDynamicGridColumn(Columns[I]).DesignWidth;
381 <        end;
382 <
383 <        if (Colsum < ClientWidth) and (ResizeColCount > 0) then
384 <        begin
385 <          adjustment := (ClientWidth - ColSum) div ResizeColCount;
386 <          n := (ClientWidth - ColSum) mod ResizeColCount;
387 <
388 <          for I := 0 to Columns.Count -1 do
389 <            if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn then
390 <            begin
391 <              if I = 0 then
392 <                Columns[I].Width := Columns[I].Width + adjustment + n
393 <              else
394 <                Columns[I].Width := Columns[I].Width + adjustment;
395 <            end;
396 <        end;
374 >      for I := 0 to  ColCount - 1 do
375 >        if (I < FixedCols) or Columns[I - FixedCols].Visible then
376 >         ColSum := ColSum + ColWidths[I];
377 >
378 >      if Colsum <> ClientWidth then
379 >      begin
380 >        ResizeColCount := 0;
381 >        for I := 0 to Columns.Count -1 do
382 >          if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn and Columns[I].Visible then
383 >          begin
384 >            Inc(ResizeColCount);
385 >            Colsum := Colsum + TDBDynamicGridColumn(Columns[I]).DesignWidth - Columns[I].Width;
386 >            Columns[I].Width := TDBDynamicGridColumn(Columns[I]).DesignWidth;
387 >          end;
388 >
389 >          if (Colsum < ClientWidth) and (ResizeColCount > 0) then
390 >          begin
391 >            adjustment := (ClientWidth - ColSum) div ResizeColCount;
392 >            n := (ClientWidth - ColSum) mod ResizeColCount;
393 >
394 >            for I := 0 to Columns.Count -1 do
395 >              if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn and Columns[I].Visible then
396 >              begin
397 >                if I = 0 then
398 >                  Columns[I].Width := Columns[I].Width + adjustment + n
399 >                else
400 >                  Columns[I].Width := Columns[I].Width + adjustment;
401 >              end;
402 >          end;
403 >      end;
404      end;
405      PositionTotals;
406      UpdateEditorPanelBounds;
# Line 374 | Line 412 | end;
412   procedure TDBDynamicGrid.DoEditorHide;
413   begin
414    inherited DoEditorHide;
415 <  if (FExpandedRow >= 0) and (FExpandedRow < RowCount) then
416 <    RowHeights[FExpandedRow] := DefaultRowHeight;
417 <  FExpandedRow := -1;
418 <  if CanFocus then SetFocus;
419 <  if assigned(FOnEditorPanelHide) then
420 <     OnEditorPanelHide(self);
421 <  DoOnResize;
415 >  if Editor = FEditorPanel then
416 >  begin
417 >    if FMouseDown then
418 >      Application.QueueAsyncCall(@PerformEditorHide,FExpandedRow)
419 >    else
420 >      PerformEditorHide(FExpandedRow);
421 >    FExpandedRow := -1;
422 >  end;
423   end;
424  
425   procedure TDBDynamicGrid.DoEditorShow;
426   begin
427 +  if assigned(DataSource) and assigned(DataSource.DataSet) and
428 +             DataSource.DataSet.Active then
429 +  begin
430 +    if (DataSource.DataSet.RecordCount = 0) and (DataSource.DataSet.State <> dsInsert) then
431 +      DataSource.DataSet.Append
432 +  end;
433    if Editor = FEditorPanel then
434    begin
435      if ExpandEditorPanelBelowRow then
# Line 397 | Line 442 | begin
442      FEditorPanel.PerformTab(true);  {Select First Control}
443      if assigned(FOnEditorPanelShow) then
444         OnEditorPanelShow(self);
445 +    if assigned(Editor) and Editor.Visible then
446 +      Editor.SetFocus;
447    end
448    else
449      inherited DoEditorShow;
# Line 461 | Line 508 | begin
508      inherited KeyDown(Key, Shift);
509   end;
510  
511 + function TDBDynamicGrid.ActiveControl: TControl;
512 + var AParent: TWinControl;
513 + begin
514 +  Result := nil;
515 +  AParent := Parent;
516 +  while (AParent <> nil) and  not (AParent is TCustomForm) do
517 +    AParent := AParent.Parent;
518 +  if (AParent <> nil) and (AParent is TCustomForm)then
519 +      Result := TCustomForm(AParent).ActiveControl;
520 + end;
521 +
522   procedure TDBDynamicGrid.DoShowEditorPanel(Data: PtrInt);
523   begin
524    if AppDestroying in Application.Flags then Exit;
# Line 492 | Line 550 | end;
550   procedure TDBDynamicGrid.KeyDownHandler(Sender: TObject; var Key: Word;
551    Shift: TShiftState);
552   var Done: boolean;
553 +    AControl: TControl;
554   begin
555    if Visible and assigned(FEditorPanel) and FEditorPanel.Visible and FWeHaveFocus then
556    begin
557      Done := false;
558 +    AControl := ActiveControl;
559 +    if (AControl <> nil) and (AControl is TCustomComboBox)
560 +                         and ((Key in [VK_UP,VK_DOWN]) or
561 +                         (TCustomComboBox(AControl).DroppedDown and (Key = VK_RETURN)) or
562 +                         ((TCustomComboBox(AControl).Text <> '') and (Key =  VK_ESCAPE))) then
563 +      Exit; {ignore these keys if we are in a  combobox}
564 +
565 +    if (AControl <> nil) and (AControl is TCustomMemo)
566 +                         and (Key in [VK_RETURN,VK_UP,VK_DOWN]) then Exit; {Ignore Return in a CustomMemo}
567 +
568 +    if (AControl <> nil) and (AControl is TCustomGrid)
569 +                         and (Key in [VK_RETURN,VK_UP,VK_DOWN,VK_TAB]) then Exit; {Ignore Return in a CustomMemo}
570 +
571      if assigned(FOnKeyDownHander) then
572        OnKeyDownHander(Sender,Key,Shift,Done);
573      if Done then Exit;
# Line 518 | Line 590 | begin
590    end
591   end;
592  
593 + procedure TDBDynamicGrid.PerformEditorHide(Data: PtrInt);
594 + var ExpandedRow: integer;
595 + begin
596 +  if AppDestroying in Application.Flags then Exit;
597 +  ExpandedRow := integer(Data);
598 +  if (ExpandedRow >= 0) and (ExpandedRow < RowCount) then
599 +    RowHeights[ExpandedRow] := DefaultRowHeight;
600 +  if CanFocus then SetFocus;
601 +  DoOnResize;
602 +  ResetSizes;
603 +  DoOnChangeBounds;
604 +  if assigned(FOnEditorPanelHide) then
605 +     OnEditorPanelHide(self);
606 + end;
607 +
608   procedure TDBDynamicGrid.SetEditorPanel(AValue: TWinControl);
609   begin
610    if FEditorPanel = AValue then Exit;
611    if FEditorPanel <> nil then
612       RemoveFreeNotification(FEditorPanel);
613    FEditorPanel := AValue;
614 <  FreeNotification(FEditorPanel);
614 >  if FEditorPanel <> nil then
615 >     FreeNotification(FEditorPanel);
616   end;
617  
618   procedure TDBDynamicGrid.ChangeBounds(ALeft, ATop, AWidth, AHeight: integer;
# Line 552 | Line 640 | begin
640    inherited Loaded;
641    if assigned(FEditorPanel) and not (csDesigning in ComponentState)then
642      FEditorPanel.Visible := false;
643 <  DoGridResize
643 >  if Visible then
644 >    DoGridResize
645   end;
646  
647   procedure TDBDynamicGrid.DoOnResize;
# Line 576 | Line 665 | procedure TDBDynamicGrid.MouseDown(Butto
665    Y: Integer);
666   var Coord: TGridCoord;
667   begin
668 <  inherited MouseDown(Button, Shift, X, Y);
668 >  FMouseDown := true;
669 >  try
670 >    inherited MouseDown(Button, Shift, X, Y);
671 >  finally
672 >    FMouseDown := false;
673 >  end;
674  
675    Coord := MouseCoord(X,Y);
676    if (Coord.X = 0) and (Coord.Y > 0) then
# Line 585 | Line 679 | end;
679  
680   procedure TDBDynamicGrid.Notification(AComponent: TComponent;
681    Operation: TOperation);
682 + var i: integer;
683   begin
684    inherited Notification(AComponent, Operation);
685 <  if (Operation = opRemove) and
686 <     (AComponent = FEditorPanel) then FEditorPanel := nil;
685 >  if (Operation = opRemove) and not (csDestroying in ComponentState) then
686 >  begin
687 >    if AComponent = FEditorPanel then
688 >      FEditorPanel := nil
689 >    else
690 >    if AComponent is TControl then
691 >    begin
692 >      for i := 0 to Columns.Count - 1 do
693 >        if TDBDynamicGridColumn(Columns[I]).ColumnTotalsControl = AComponent then
694 >          TDBDynamicGridColumn(Columns[I]).ColumnTotalsControl := nil;
695 >    end;
696 >  end
697   end;
698  
699   procedure TDBDynamicGrid.TopLeftChanged;
# Line 602 | Line 707 | begin
707    inherited UpdateActive;
708  
709    if not (csLoading in ComponentState) and assigned(DataLink) and
710 +     assigned(FEditorPanel) and not FEditorPanel.Visible and
711       assigned(DataLink.DataSet) and (DataLink.DataSet.State = dsInsert) then
712       Application.QueueAsyncCall(@DoShowEditorPanel,0);
713   end;
# Line 634 | Line 740 | end;
740   procedure TDBDynamicGrid.HideEditorPanel;
741   begin
742    if Editor = FEditorPanel then
743 <    EditorMode := false;
743 >      EditorMode := false;
744   end;
745  
746   procedure TDBDynamicGrid.ShowEditorPanel;
747   begin
748 <  if csDesigning in ComponentState then Exit;
748 >  if (csDesigning in ComponentState) or
749 >   (DataSource = nil) or (DataSource.DataSet = nil)
750 >     or ((DataSource.DataSet.RecordCount = 0) and (DataSource.DataSet.State <> dsInsert)) then
751 >     Exit;
752    Editor := FEditorPanel;
753    EditorMode := true;
754   end;
# Line 679 | Line 788 | begin
788    Result := inherited Width
789   end;
790  
791 + type
792 +  THackedGrid = class(TIBDynamicGrid)
793 +  public
794 +    property FixedCols;
795 +  end;
796 +
797   { TDBLookupCellEditor }
798  
799 + function TDBLookupCellEditor.EditingKeyField: boolean;
800 + begin
801 +  with TIBDynamicGridColumn(TDBGrid(FGrid).Columns[FCol - THackedGrid(FGrid).FixedCols]) do
802 +    Result := CompareText(FieldName, DBLookupProperties.DataFieldName) = 0;
803 + end;
804 +
805   procedure TDBLookupCellEditor.WndProc(var TheMessage: TLMessage);
806   begin
807    if TheMessage.msg=LM_KILLFOCUS then begin
# Line 698 | Line 819 | procedure TDBLookupCellEditor.CloseUp;
819   begin
820    UpdateData(nil); {Force Record Update}
821    if FGrid<>nil then
822 <    (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, Text);
822 >  Begin
823 >    if EditingKeyField then
824 >    begin
825 >      if not VarIsNull(KeyValue) then
826 >       (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, KeyValue)
827 >    end
828 >    else
829 >      (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, Trim(Text));
830 >    (FGrid as TIBDynamicGrid).UpdateData;
831 >  end;
832    inherited CloseUp;
833   end;
834  
# Line 710 | Line 840 | begin
840      inherited KeyDown(Key, Shift);
841   end;
842  
843 + procedure TDBLookupCellEditor.Loaded;
844 + begin
845 +  inherited Loaded;
846 +  Text := '';
847 + end;
848 +
849   procedure TDBLookupCellEditor.msg_GetValue(var Msg: TGridMessage);
850   begin
851    CheckAndInsert;
852    Msg.Col := FCol;
853    Msg.Row := FRow;
854 <  Msg.Value:= Trim(Text);
854 >  if EditingKeyField then
855 >  begin
856 >    if not VarIsNull(KeyValue) then
857 >      Msg.Value:= KeyValue
858 >    else
859 >      Msg.Value:= ''
860 >  end
861 >  else
862 >    Msg.Value:= Trim(Text);
863   end;
864  
865   procedure TDBLookupCellEditor.msg_SetGrid(var Msg: TGridMessage);
# Line 730 | Line 874 | begin
874    FCol := Msg.Col;
875    FRow := Msg.Row;
876    FEditText := Msg.Value;
733  SelStart := Length(Text);
877    TIBDynamicGrid(FGrid).SetupEditor(self,FCol);
878   end;
879  
# Line 779 | Line 922 | begin
922      Editor.OnAutoInsert := OnAutoInsert;
923      Editor.OnCanAutoInsert := OnCanAutoInsert;
924      Editor.OnDrawItem := OnDrawItem;
925 +    Editor.OnCloseUp := OnCloseUp;
926  
927      {Setup Data Links}
928      if KeyField <> '' then
# Line 803 | Line 947 | begin
947      if DataFieldName <> '' then
948          Editor.DataSource := TDBGrid(Grid).DataSource;
949    end;
950 <  Editor.Text := Editor.FEditText;
950 >  if Editor.EditingKeyField then
951 >  begin
952 >    if not Field.IsNull then
953 >      Editor.KeyValue := Editor.FEditText
954 >  end
955 >  else
956 >    Editor.Text := Editor.FEditText;
957 >  Editor.SelStart := Length(Editor.Text);
958   end;
959  
960   procedure TIBDynamicGridColumn.SetInitialSortColumn(AValue: boolean);
# Line 830 | Line 981 | begin
981    inherited Destroy;
982   end;
983  
833 { TDynamicGridDataLink }
834
835 procedure TDynamicGridDataLink.DataEvent(Event: TDataEvent; Info: Ptrint);
836 begin
837  if (Event = deCheckBrowseMode) and (Info = 1) and not DataSet.Active then
838  begin
839    if (DataSet is TIBDataSet) then
840      FOwner.UpdateSQL(self,TIBDataSet(DataSet).Parser)
841    else
842    if (DataSet is TIBQuery) then
843      FOwner.UpdateSQL(self,TIBQuery(DataSet).Parser)
844  end
845  else
846  inherited DataEvent(Event, Info);
847 end;
848
849 procedure TDynamicGridDataLink.DataSetScrolled(Distance: Integer);
850 begin
851  inherited DataSetScrolled(Distance);
852  FOwner.DataSetScrolled(self)
853 end;
854
855 constructor TDynamicGridDataLink.Create(AOwner: TIBDynamicGrid);
856 begin
857  inherited Create;
858  FOwner := AOwner
859 end;
860
984  
985   { TIBDynamicGrid }
986  
# Line 872 | Line 995 | begin
995        OnColumnHeaderClick(self,Index);
996  
997      FLastColIndex := Index;
998 <    if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active then
998 >    FFieldPosition := 0;
999 >    if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active
1000 >       and (DataSource.DataSet is TIBParserDataSet) then
1001      begin
1002 +      if FLastColIndex < Columns.Count then
1003 +      {try and cache field position while dataset still open}
1004 +        FFieldPosition := TIBParserDataSet(DataSource.DataSet).Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
1005        DataSource.DataSet.Active := false;
1006        Application.QueueAsyncCall(@DoReopen,0)
1007      end;
# Line 901 | Line 1029 | end;
1029   procedure TIBDynamicGrid.SetDataSource(AValue: TDataSource);
1030   begin
1031    inherited DataSource := AValue;
1032 <  FDataLink.DataSource := AValue;
1032 >  IBControlLinkChanged;
1033   end;
1034  
1035   procedure TIBDynamicGrid.SetEditorBorderStyle(AValue: TBorderStyle);
# Line 921 | Line 1049 | begin
1049    for i := 0 to Columns.Count - 1 do
1050    begin
1051      if TIBDynamicGridColumn(columns[i]).InitialSortColumn then
1052 +    begin
1053 +      FFieldPosition := 0;
1054        FLastColIndex := i
1055 +    end
1056    end
1057   end;
1058  
# Line 938 | Line 1069 | end;
1069  
1070   procedure TIBDynamicGrid.UpdateSQL(Sender: TObject; Parser: TSelectSQLParser);
1071   var OrderBy: string;
941    FieldPosition: integer;
1072   begin
1073 <    if (Sender = TObject(FDataLink)) and assigned(DataSource) and assigned(DataSource.DataSet)
1073 >    if assigned(DataSource) and assigned(DataSource.DataSet)
1074        and (DataSource.DataSet is TIBCustomDataSet) then
1075      begin
1076 <      FieldPosition := Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
1077 <      if FieldPosition = 0 then Exit;
1078 <
1079 <      if Descending then
1080 <        Parser.OrderByClause := IntToStr(FieldPosition) + ' desc'
1081 <      else
1082 <        Parser.OrderByClause := IntToStr(FieldPosition) + ' asc';
1076 >      if (FFieldPosition = 0) and (FLastColIndex >= 0) and (FLastColIndex < Columns.Count) then
1077 >        {Not cached - let's hope we can find it before the dataset is opened.
1078 >         Won't work if dynamic columns}
1079 >        FFieldPosition := Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
1080 >      if FFieldPosition > 0 then
1081 >      begin
1082 >        if Descending then
1083 >          Parser.OrderByClause := IntToStr(FFieldPosition) + ' desc'
1084 >        else
1085 >          Parser.OrderByClause := IntToStr(FFieldPosition) + ' asc';
1086 >      end;
1087  
1088        if assigned(FOnUpdateSortOrder) then
1089        begin
# Line 972 | Line 1106 | begin
1106  
1107   end;
1108  
1109 < procedure TIBDynamicGrid.DataSetScrolled(Sender: TObject);
976 < var i: integer;
977 <    F: TField;
1109 > procedure TIBDynamicGrid.RestorePosition;
1110   begin
979    SetLength(FBookmark,FIndexFieldsList.Count);
980    for i := 0 to FIndexFieldsList.Count - 1 do
981    begin
982      F := DataSource.DataSet.FindField(FIndexFieldsList[i]);
983      if assigned(F) then
984         FBookmark[i] := F.AsVariant;
985    end;
986 end;
987
988 procedure TIBDynamicGrid.RestorePosition(Data: PtrInt);
989 begin
990  if AppDestroying in Application.Flags then Exit;
991
1111    if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active then
1112    begin
1113 +    if assigned(FOnRestorePosition) then
1114 +      OnRestorePosition(self,@FBookmark);
1115      if (Length(FBookmark) > 0) and
1116        DataSource.DataSet.Locate(FIndexFieldNames,FBookmark,[]) then Exit;
1117  
# Line 999 | Line 1120 | begin
1120    end;
1121   end;
1122  
1123 + procedure TIBDynamicGrid.SavePosition;
1124 + var i: integer;
1125 +    F: TField;
1126 + begin
1127 +  if FIndexFieldsList = nil then Exit;
1128 +
1129 +  SetLength(FBookmark,FIndexFieldsList.Count);
1130 +  for i := 0 to FIndexFieldsList.Count - 1 do
1131 +  begin
1132 +    F := DataSource.DataSet.FindField(FIndexFieldsList[i]);
1133 +    if assigned(F) then
1134 +       FBookmark[i] := F.AsVariant;
1135 +  end;
1136 + end;
1137 +
1138   procedure TIBDynamicGrid.DoReOpen(Data: PtrInt);
1139   begin
1140    DataSource.DataSet.Active := true;
1141   end;
1142  
1143 + procedure TIBDynamicGrid.IBControlLinkChanged;
1144 + begin
1145 +  if (DataSource <> nil) and (DataSource.DataSet <> nil) and (DataSource.DataSet is TIBParserDataSet) then
1146 +    FIBControlLink.IBDataSet := TIBCustomDataSet(DataSource.DataSet)
1147 +  else
1148 +    FIBControlLink.IBDataSet := nil;
1149 + end;
1150 +
1151   procedure TIBDynamicGrid.SetupEditor(aEditor: TDBLookupCellEditor; aCol: integer
1152    );
1153   var C: TIBDynamicGridColumn;
1154   begin
1155    C := ColumnFromGridColumn(aCol) as TIBDynamicGridColumn;
1156 <  C.SetupEditor(aEditor);
1156 >  if (c <> nil) then
1157 >    C.SetupEditor(aEditor);
1158   end;
1159  
1160   procedure TIBDynamicGrid.DoEditorHide;
# Line 1025 | Line 1170 | end;
1170   procedure TIBDynamicGrid.Loaded;
1171   begin
1172    inherited Loaded;
1173 +  IBControlLinkChanged;
1174    ProcessColumns;
1175   end;
1176  
# Line 1054 | Line 1200 | begin
1200    AllowOutboundEvents := false;
1201    try
1202      Coord := MouseCoord(X,Y);
1203 <  if AllowColumnSort and  (Coord.X <> -1) and
1203 >  if AllowColumnSort and  (Coord.X <> -1) and (FixedRows > 0) and
1204     (Coord.Y = 0) and (MouseCoord(X+5,Y).X = Coord.X) {not on boundary}
1205                     and (MouseCoord(X-5,Y).X = Coord.X) then
1206      ColumnHeaderClick(Coord.X-1);
# Line 1063 | Line 1209 | begin
1209    end;
1210   end;
1211  
1212 + procedure TIBDynamicGrid.MoveSelection;
1213 + begin
1214 +  inherited MoveSelection;
1215 +  SavePosition;
1216 + end;
1217 +
1218   procedure TIBDynamicGrid.LinkActive(Value: Boolean);
1219   begin
1220 +  IBControlLinkChanged;
1221    inherited LinkActive(Value);
1222    if (FActive <> Value) and Value then
1223 <    Application.QueueAsyncCall(@RestorePosition,0);
1223 >    RestorePosition;
1224    FActive := Value
1225   end;
1226  
1227   procedure TIBDynamicGrid.Notification(AComponent: TComponent;
1228    Operation: TOperation);
1229 + var i: integer;
1230   begin
1231    inherited Notification(AComponent, Operation);
1232 <  if (Operation = opRemove) and
1233 <     (FDataLink <> nil) and (AComponent = DataSource) then DataSource := nil;
1232 >  if (Operation = opRemove) then
1233 >  begin
1234 >    if (FIBControlLink <> nil) and (AComponent = DataSource) then
1235 >      FIBControlLink.IBDataSet := nil
1236 >    else
1237 >    if AComponent is TDataSource then
1238 >    begin
1239 >      for i := 0 to Columns.Count - 1 do
1240 >        if TIBDynamicGridColumn(Columns[I]).DBLookupProperties.ListSource = AComponent then
1241 >          TIBDynamicGridColumn(Columns[I]).DBLookupProperties.ListSource := nil;
1242 >    end
1243 >  end
1244   end;
1245  
1246   procedure TIBDynamicGrid.UpdateActive;
1247   begin
1248    inherited UpdateActive;
1249 <  if assigned(FDataLink) and assigned(FDataLink.DataSet) and
1250 <     FDataLink.DataSet.Active and (FDataLink.DataSet.State = dsInsert) then
1251 <    DataSetScrolled(nil);
1249 >  if assigned(DataLink) and assigned(DataLink.DataSet) and
1250 >     DataLink.DataSet.Active and (DataLink.DataSet.State = dsInsert) then
1251 >   SavePosition;
1252   end;
1253  
1254   constructor TIBDynamicGrid.Create(TheComponent: TComponent);
1255   begin
1256    inherited Create(TheComponent);
1257    FAllowColumnSort := true;
1258 <  FDataLink := TDynamicGridDataLink.Create(self);
1258 >  FIBControlLink := TIBGridControlLink.Create(self);
1259    FIndexFieldsList := TStringList.Create;
1260    FIndexFieldsList.Delimiter := ';';
1261    FIndexFieldsList.StrictDelimiter := true;
# Line 1103 | Line 1267 | end;
1267  
1268   destructor TIBDynamicGrid.Destroy;
1269   begin
1270 <  if assigned(FDataLink) then FDataLink.Free;
1270 >  if assigned(FIBControlLink) then FIBControlLink.Free;
1271    if assigned(FIndexFieldsList) then FIndexFieldsList.Free;
1272    if assigned(FDBLookupCellEditor) then FDBLookupCellEditor.Free;
1273    inherited Destroy;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines