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 31 by tony, Tue Jul 14 15:31:25 2015 UTC vs.
Revision 408 by tony, Tue Dec 13 22:37:43 2022 UTC

# Line 23 | Line 23
23   *  Contributor(s): ______________________________________.
24   *
25   *)
26 +
27   unit IBDynamicGrid;
28  
29   {$mode objfpc}{$H+}
# Line 32 | Line 33 | interface
33   uses
34    Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, DBGrids, DB,
35    IBSQLParser, Grids, IBLookupComboEditBox, LMessages, StdCtrls, ExtCtrls,
36 <  IBCustomDataSet;
36 >  IBCustomDataSet, LCLVersion;
37  
38   type
39    {
# Line 40 | Line 41 | type
41     - automatic resizing of selected columns to fill the available row length
42     - automatic positioning and sizing of a "totals" control, typically at the
43       column footer, on a per column basis.
44 <   - DataSet resorting on header row click, sorting the dataset by the selected column.
44 >   - DataSet re-sorting on header row click, sorting the dataset by the selected column.
45       A second click on the same header cell reversed the sort order.
46     - Reselection of the same row following resorting.
47     - A new cell editor that provides the same functionality as TIBLookupComboEditBox.
# Line 149 | 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;
# Line 165 | Line 167 | end;
167      property OnEditingDone;
168    end;
169  
170 +  TOnSelectPanelEditor = procedure(Sender: TObject; var aEditorPanel: TWinControl) of object;
171 +
172    TDBDynamicGrid = class(TDBGrid)
173    private
174      { Private declarations }
# Line 175 | Line 179 | end;
179      FOnEditorPanelHide: TNotifyEvent;
180      FOnEditorPanelShow: TNotifyEvent;
181      FOnKeyDownHander: TKeyDownHandler;
182 +    FOnSelectPanelEditor: TOnSelectPanelEditor;
183      FResizing: boolean;
184      FWeHaveFocus: boolean;
185 <    FHidingEditorPanel: boolean;
181 <    FAllowHide: boolean;
185 >    FMouseDown: boolean;
186      function ActiveControl: TControl;
187      procedure DoShowEditorPanel(Data: PtrInt);
188      procedure PositionTotals;
189      procedure KeyDownHandler(Sender: TObject; var Key: Word; Shift: TShiftState);
190 +    procedure PerformEditorHide(Data: PtrInt);
191      procedure SetEditorPanel(AValue: TWinControl);
192    protected
193      procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer; KeepBase: boolean); override;
# Line 191 | Line 196 | end;
196      procedure DoGridResize;
197      procedure DoEditorHide; override;
198      procedure DoEditorShow; override;
199 +    {$IF (LCL_FULLVERSION < 2030000)}
200      procedure DrawCellText(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState; aText: String); override;
201 +    {$ELSE}
202 +    procedure DrawCellText(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState; const aText: String); override;
203 +    {$IFEND}
204      Function  EditingAllowed(ACol : Integer = -1) : Boolean; override;
205      procedure EditorHide; override;
206      procedure IndicatorClicked(Button: TMouseButton; Shift:TShiftState); virtual;
# Line 220 | Line 229 | end;
229      property OnEditorPanelShow: TNotifyEvent read FOnEditorPanelShow write FOnEditorPanelShow;
230      property OnEditorPanelHide: TNotifyEvent read FOnEditorPanelHide write FOnEditorPanelHide;
231      property OnKeyDownHander: TKeyDownHandler read FOnKeyDownHander write FOnKeyDownHander;
232 +    property OnSelectPanelEditor: TOnSelectPanelEditor read FOnSelectPanelEditor
233 +                                                       write FOnSelectPanelEditor;
234   end;
235  
236    {TIBGridControlLink}
# Line 256 | Line 267 | end;
267      FBookmark: TLocationArray;
268      FDBLookupCellEditor: TDBLookupCellEditor;
269      FActive: boolean;
270 +    FFieldPosition: integer;
271      procedure ColumnHeaderClick(Index: integer);
272      function GetDataSource: TDataSource;
273      function GetEditorBorderStyle: TBorderStyle;
# Line 301 | Line 313 | end;
313  
314   implementation
315  
316 < uses Math, IBQuery, LCLType;
316 > uses LCLType, Variants, EditBtn;
317  
318   { TIBGridControlLink }
319  
# Line 361 | Line 373 | begin
373    FResizing := true;
374    try
375      ColSum := 0;
364    for I := 0 to  ColCount - 1 do
365       ColSum := ColSum + ColWidths[I];
376  
377 <    if Colsum <> ClientWidth then
377 >    if (ColCount = 1) and TDBDynamicGridColumn(Columns[0]).AutoSizeColumn then
378 >      Columns[0].Width := ClientWidth
379 >    else
380      begin
381 <      ResizeColCount := 0;
382 <      for I := 0 to Columns.Count -1 do
383 <        if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn then
384 <        begin
385 <          Inc(ResizeColCount);
386 <          Colsum := Colsum + TDBDynamicGridColumn(Columns[I]).DesignWidth - Columns[I].Width;
387 <          Columns[I].Width := TDBDynamicGridColumn(Columns[I]).DesignWidth;
388 <        end;
389 <
390 <        if (Colsum < ClientWidth) and (ResizeColCount > 0) then
391 <        begin
392 <          adjustment := (ClientWidth - ColSum) div ResizeColCount;
393 <          n := (ClientWidth - ColSum) mod ResizeColCount;
394 <
395 <          for I := 0 to Columns.Count -1 do
396 <            if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn then
397 <            begin
398 <              if I = 0 then
399 <                Columns[I].Width := Columns[I].Width + adjustment + n
400 <              else
401 <                Columns[I].Width := Columns[I].Width + adjustment;
402 <            end;
403 <        end;
381 >      for I := 0 to  ColCount - 1 do
382 >        if (I < FixedCols) or Columns[I - FixedCols].Visible then
383 >         ColSum := ColSum + ColWidths[I];
384 >
385 >      if Colsum <> ClientWidth then
386 >      begin
387 >        ResizeColCount := 0;
388 >        for I := 0 to Columns.Count -1 do
389 >          if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn and Columns[I].Visible then
390 >          begin
391 >            Inc(ResizeColCount);
392 >            Colsum := Colsum + TDBDynamicGridColumn(Columns[I]).DesignWidth - Columns[I].Width;
393 >            Columns[I].Width := TDBDynamicGridColumn(Columns[I]).DesignWidth;
394 >          end;
395 >
396 >          if (Colsum < ClientWidth) and (ResizeColCount > 0) then
397 >          begin
398 >            adjustment := (ClientWidth - ColSum) div ResizeColCount;
399 >            n := (ClientWidth - ColSum) mod ResizeColCount;
400 >
401 >            for I := 0 to Columns.Count -1 do
402 >              if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn and Columns[I].Visible then
403 >              begin
404 >                if I = 0 then
405 >                  Columns[I].Width := Columns[I].Width + adjustment + n
406 >                else
407 >                  Columns[I].Width := Columns[I].Width + adjustment;
408 >              end;
409 >          end;
410 >      end;
411      end;
412      PositionTotals;
413      UpdateEditorPanelBounds;
# Line 402 | Line 421 | begin
421    inherited DoEditorHide;
422    if Editor = FEditorPanel then
423    begin
424 <    if (FExpandedRow >= 0) and (FExpandedRow < RowCount) then
425 <      RowHeights[FExpandedRow] := DefaultRowHeight;
424 >    if FMouseDown then
425 >      Application.QueueAsyncCall(@PerformEditorHide,FExpandedRow)
426 >    else
427 >      PerformEditorHide(FExpandedRow);
428      FExpandedRow := -1;
408    if CanFocus then SetFocus;
409    DoOnResize;
410    ResetSizes;
411    DoOnChangeBounds;
412    if assigned(FOnEditorPanelHide) then
413       OnEditorPanelHide(self);
429    end;
430   end;
431  
432   procedure TDBDynamicGrid.DoEditorShow;
433   begin
434 +  if assigned(DataSource) and assigned(DataSource.DataSet) and
435 +             DataSource.DataSet.Active then
436 +  begin
437 +    if (DataSource.DataSet.RecordCount = 0) and (DataSource.DataSet.State <> dsInsert) then
438 +      DataSource.DataSet.Append
439 +  end;
440    if Editor = FEditorPanel then
441    begin
442      if ExpandEditorPanelBelowRow then
# Line 428 | Line 449 | begin
449      FEditorPanel.PerformTab(true);  {Select First Control}
450      if assigned(FOnEditorPanelShow) then
451         OnEditorPanelShow(self);
452 +    if assigned(Editor) and Editor.Visible then
453 +      Editor.SetFocus;
454    end
455    else
456      inherited DoEditorShow;
457   end;
458  
459 + {$IF LCL_FULLVERSION < 2030000}
460   procedure TDBDynamicGrid.DrawCellText(aCol, aRow: Integer; aRect: TRect;
461    aState: TGridDrawState; aText: String);
462 + {$ELSE}
463 + procedure TDBDynamicGrid.DrawCellText(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState; const aText: String);
464 + {$IFEND}
465   var Style: TTextStyle;
466      OldStyle: TTextStyle;
467   begin
# Line 547 | Line 574 | begin
574        Exit; {ignore these keys if we are in a  combobox}
575  
576      if (AControl <> nil) and (AControl is TCustomMemo)
577 <                         and (Key = VK_RETURN) then Exit; {Ignore Return in a CustomMemo}
577 >                         and (Key in [VK_RETURN,VK_UP,VK_DOWN]) then Exit; {Ignore keys in a CustomMemo}
578 >
579 >    if (AControl <> nil) and (AControl is TCustomGrid)
580 >                         and (Key in [VK_RETURN,VK_UP,VK_DOWN,VK_TAB]) then Exit; {Ignore keys in a Custom Grid}
581 >
582 >    if (AControl <> nil) and (AControl is TEBEdit) and (AControl.Owner is TDateEdit) then
583 >    begin
584 >      if (Key in [VK_LEFT,VK_RIGHT]) then Exit; {Ignore navigation keys}
585 >      if TDateEdit(AControl.Owner).DroppedDown and
586 >        (Key in [VK_RETURN,VK_UP,VK_DOWN,VK_ESCAPE]) then Exit; {Ignore TCalender navigation keys in a Data edit}
587 >    end;
588 >
589      if assigned(FOnKeyDownHander) then
590        OnKeyDownHander(Sender,Key,Shift,Done);
591      if Done then Exit;
# Line 570 | Line 608 | begin
608    end
609   end;
610  
611 + procedure TDBDynamicGrid.PerformEditorHide(Data: PtrInt);
612 + var ExpandedRow: integer;
613 + begin
614 +  if AppDestroying in Application.Flags then Exit;
615 +  ExpandedRow := integer(Data);
616 +  if (ExpandedRow >= 0) and (ExpandedRow < RowCount) then
617 +    RowHeights[ExpandedRow] := DefaultRowHeight;
618 +  if CanFocus then SetFocus;
619 +  DoOnResize;
620 +  ResetSizes;
621 +  DoOnChangeBounds;
622 +  if assigned(FOnEditorPanelHide) then
623 +     OnEditorPanelHide(self);
624 + end;
625 +
626   procedure TDBDynamicGrid.SetEditorPanel(AValue: TWinControl);
627   begin
628    if FEditorPanel = AValue then Exit;
629    if FEditorPanel <> nil then
630       RemoveFreeNotification(FEditorPanel);
631    FEditorPanel := AValue;
632 <  FreeNotification(FEditorPanel);
632 >  if FEditorPanel <> nil then
633 >     FreeNotification(FEditorPanel);
634   end;
635  
636   procedure TDBDynamicGrid.ChangeBounds(ALeft, ATop, AWidth, AHeight: integer;
# Line 604 | Line 658 | begin
658    inherited Loaded;
659    if assigned(FEditorPanel) and not (csDesigning in ComponentState)then
660      FEditorPanel.Visible := false;
661 <  DoGridResize
661 >  if Visible then
662 >    DoGridResize
663   end;
664  
665   procedure TDBDynamicGrid.DoOnResize;
# Line 628 | Line 683 | procedure TDBDynamicGrid.MouseDown(Butto
683    Y: Integer);
684   var Coord: TGridCoord;
685   begin
686 <  inherited MouseDown(Button, Shift, X, Y);
686 >  FMouseDown := true;
687 >  try
688 >    inherited MouseDown(Button, Shift, X, Y);
689 >  finally
690 >    FMouseDown := false;
691 >  end;
692  
693    Coord := MouseCoord(X,Y);
694    if (Coord.X = 0) and (Coord.Y > 0) then
# Line 637 | Line 697 | end;
697  
698   procedure TDBDynamicGrid.Notification(AComponent: TComponent;
699    Operation: TOperation);
700 + var i: integer;
701   begin
702    inherited Notification(AComponent, Operation);
703 <  if (Operation = opRemove) and
704 <     (AComponent = FEditorPanel) then FEditorPanel := nil;
703 >  if (Operation = opRemove) and not (csDestroying in ComponentState) then
704 >  begin
705 >    if AComponent = FEditorPanel then
706 >      FEditorPanel := nil
707 >    else
708 >    if AComponent is TControl then
709 >    begin
710 >      for i := 0 to Columns.Count - 1 do
711 >        if TDBDynamicGridColumn(Columns[I]).ColumnTotalsControl = AComponent then
712 >          TDBDynamicGridColumn(Columns[I]).ColumnTotalsControl := nil;
713 >    end;
714 >  end
715   end;
716  
717   procedure TDBDynamicGrid.TopLeftChanged;
# Line 653 | Line 724 | procedure TDBDynamicGrid.UpdateActive;
724   begin
725    inherited UpdateActive;
726  
727 +  if not (csLoading in ComponentState) and assigned(DataLink)
728 +                       and assigned(DataLink.DataSet) and DataLink.DataSet.Active then
729 +    DoGridResize;
730 +
731    if not (csLoading in ComponentState) and assigned(DataLink) and
732 +     assigned(FEditorPanel) and not FEditorPanel.Visible and
733       assigned(DataLink.DataSet) and (DataLink.DataSet.State = dsInsert) then
734       Application.QueueAsyncCall(@DoShowEditorPanel,0);
735   end;
# Line 686 | Line 762 | end;
762   procedure TDBDynamicGrid.HideEditorPanel;
763   begin
764    if Editor = FEditorPanel then
765 <    EditorMode := false;
765 >      EditorMode := false;
766   end;
767  
768   procedure TDBDynamicGrid.ShowEditorPanel;
769 + var aEditor: TWinControl;
770   begin
771    if (csDesigning in ComponentState) or
772     (DataSource = nil) or (DataSource.DataSet = nil)
773       or ((DataSource.DataSet.RecordCount = 0) and (DataSource.DataSet.State <> dsInsert)) then
774       Exit;
775 <  Editor := FEditorPanel;
775 >  aEditor := FEditorPanel;
776 >  if assigned(FOnSelectPanelEditor) then
777 >    OnSelectPanelEditor(self,aEditor);
778 >  if FEditorPanel <> aEditor then
779 >    SetEditorPanel(aEditor);
780 >  Editor := aEditor;
781    EditorMode := true;
782   end;
783  
# Line 734 | Line 816 | begin
816    Result := inherited Width
817   end;
818  
819 + type
820 +  THackedGrid = class(TIBDynamicGrid)
821 +  public
822 +    property FixedCols;
823 +  end;
824 +
825   { TDBLookupCellEditor }
826  
827 + function TDBLookupCellEditor.EditingKeyField: boolean;
828 + begin
829 +  with TIBDynamicGridColumn(TDBGrid(FGrid).Columns[FCol - THackedGrid(FGrid).FixedCols]) do
830 +    Result := CompareText(FieldName, DBLookupProperties.DataFieldName) = 0;
831 + end;
832 +
833   procedure TDBLookupCellEditor.WndProc(var TheMessage: TLMessage);
834   begin
835    if TheMessage.msg=LM_KILLFOCUS then begin
# Line 754 | Line 848 | begin
848    UpdateData(nil); {Force Record Update}
849    if FGrid<>nil then
850    Begin
851 <    (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, Trim(Text));
851 >    if EditingKeyField then
852 >    begin
853 >      if not VarIsNull(KeyValue) then
854 >       (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, KeyValue)
855 >    end
856 >    else
857 >      (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, Trim(Text));
858      (FGrid as TIBDynamicGrid).UpdateData;
859    end;
860    inherited CloseUp;
# Line 779 | Line 879 | begin
879    CheckAndInsert;
880    Msg.Col := FCol;
881    Msg.Row := FRow;
882 <  Msg.Value:= Trim(Text);
882 >  if EditingKeyField then
883 >  begin
884 >    if not VarIsNull(KeyValue) then
885 >      Msg.Value:= KeyValue
886 >    else
887 >      Msg.Value:= ''
888 >  end
889 >  else
890 >    Msg.Value:= Trim(Text);
891   end;
892  
893   procedure TDBLookupCellEditor.msg_SetGrid(var Msg: TGridMessage);
# Line 867 | Line 975 | begin
975      if DataFieldName <> '' then
976          Editor.DataSource := TDBGrid(Grid).DataSource;
977    end;
978 <  Editor.Text := Editor.FEditText;
978 >  if Editor.EditingKeyField then
979 >  begin
980 >    if not Field.IsNull then
981 >      Editor.KeyValue := Editor.FEditText
982 >  end
983 >  else
984 >    Editor.Text := Editor.FEditText;
985    Editor.SelStart := Length(Editor.Text);
986   end;
987  
# Line 892 | Line 1006 | end;
1006   destructor TIBDynamicGridColumn.Destroy;
1007   begin
1008    if assigned(FDBLookupProperties) then FDBLookupProperties.Free;
1009 +  Application.RemoveAsyncCalls(self);
1010    inherited Destroy;
1011   end;
1012  
# Line 909 | Line 1024 | begin
1024        OnColumnHeaderClick(self,Index);
1025  
1026      FLastColIndex := Index;
1027 <    if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active then
1027 >    FFieldPosition := 0;
1028 >    if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active
1029 >       and (DataSource.DataSet is TIBParserDataSet) then
1030      begin
1031 +      if FLastColIndex < Columns.Count then
1032 +      {try and cache field position while dataset still open}
1033 +        FFieldPosition := TIBParserDataSet(DataSource.DataSet).Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
1034        DataSource.DataSet.Active := false;
1035        Application.QueueAsyncCall(@DoReopen,0)
1036      end;
# Line 958 | Line 1078 | begin
1078    for i := 0 to Columns.Count - 1 do
1079    begin
1080      if TIBDynamicGridColumn(columns[i]).InitialSortColumn then
1081 +    begin
1082 +      FFieldPosition := 0;
1083        FLastColIndex := i
1084 +    end
1085    end
1086   end;
1087  
# Line 975 | Line 1098 | end;
1098  
1099   procedure TIBDynamicGrid.UpdateSQL(Sender: TObject; Parser: TSelectSQLParser);
1100   var OrderBy: string;
978    FieldPosition: integer;
1101   begin
1102      if assigned(DataSource) and assigned(DataSource.DataSet)
1103        and (DataSource.DataSet is TIBCustomDataSet) then
1104      begin
1105 <      if (FLastColIndex < 0) or (FLastColIndex >= Columns.Count) then Exit;
1106 <      FieldPosition := Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
1107 <      if FieldPosition = 0 then Exit;
1108 <
1109 <      if Descending then
1110 <        Parser.OrderByClause := IntToStr(FieldPosition) + ' desc'
1111 <      else
1112 <        Parser.OrderByClause := IntToStr(FieldPosition) + ' asc';
1105 >      if (FFieldPosition = 0) and (FLastColIndex >= 0) and (FLastColIndex < Columns.Count) then
1106 >        {Not cached - let's hope we can find it before the dataset is opened.
1107 >         Won't work if dynamic columns}
1108 >        FFieldPosition := Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
1109 >      if FFieldPosition > 0 then
1110 >      begin
1111 >        if Descending then
1112 >          Parser.OrderByClause := IntToStr(FFieldPosition) + ' desc'
1113 >        else
1114 >          Parser.OrderByClause := IntToStr(FFieldPosition) + ' asc';
1115 >      end;
1116  
1117        if assigned(FOnUpdateSortOrder) then
1118        begin
# Line 1130 | Line 1255 | end;
1255  
1256   procedure TIBDynamicGrid.Notification(AComponent: TComponent;
1257    Operation: TOperation);
1258 + var i: integer;
1259   begin
1260    inherited Notification(AComponent, Operation);
1261 <  if (Operation = opRemove) and
1262 <     (FIBControlLink <> nil) and (AComponent = DataSource) then FIBControlLink.IBDataSet := nil;
1261 >  if (Operation = opRemove) then
1262 >  begin
1263 >    if (FIBControlLink <> nil) and (AComponent = DataSource) then
1264 >      FIBControlLink.IBDataSet := nil
1265 >    else
1266 >    if AComponent is TDataSource then
1267 >    begin
1268 >      for i := 0 to Columns.Count - 1 do
1269 >        if TIBDynamicGridColumn(Columns[I]).DBLookupProperties.ListSource = AComponent then
1270 >          TIBDynamicGridColumn(Columns[I]).DBLookupProperties.ListSource := nil;
1271 >    end
1272 >  end
1273   end;
1274  
1275   procedure TIBDynamicGrid.UpdateActive;
# Line 1163 | Line 1299 | begin
1299    if assigned(FIBControlLink) then FIBControlLink.Free;
1300    if assigned(FIndexFieldsList) then FIndexFieldsList.Free;
1301    if assigned(FDBLookupCellEditor) then FDBLookupCellEditor.Free;
1302 +  Application.RemoveAsyncCalls(self);
1303    inherited Destroy;
1304   end;
1305  

Comparing ibx/trunk/ibcontrols/IBDynamicGrid.pas (property svn:eol-style):
Revision 31 by tony, Tue Jul 14 15:31:25 2015 UTC vs.
Revision 408 by tony, Tue Dec 13 22:37:43 2022 UTC

# Line 0 | Line 1
1 + native

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines