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 27 by tony, Tue Apr 14 13:10:23 2015 UTC vs.
Revision 217 by tony, Fri Mar 16 10:27:26 2018 UTC

# Line 23 | Line 23
23   *  Contributor(s): ______________________________________.
24   *
25   *)
26 +
27   unit IBDynamicGrid;
28  
29   {$mode objfpc}{$H+}
# Line 91 | 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 117 | 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 147 | 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 163 | 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 173 | Line 179 | end;
179      FOnEditorPanelHide: TNotifyEvent;
180      FOnEditorPanelShow: TNotifyEvent;
181      FOnKeyDownHander: TKeyDownHandler;
182 +    FOnSelectPanelEditor: TOnSelectPanelEditor;
183      FResizing: boolean;
184      FWeHaveFocus: boolean;
185 <    FHidingEditorPanel: boolean;
186 <    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 217 | Line 225 | end;
225      property OnEditorPanelShow: TNotifyEvent read FOnEditorPanelShow write FOnEditorPanelShow;
226      property OnEditorPanelHide: TNotifyEvent read FOnEditorPanelHide write FOnEditorPanelHide;
227      property OnKeyDownHander: TKeyDownHandler read FOnKeyDownHander write FOnKeyDownHander;
228 +    property OnSelectPanelEditor: TOnSelectPanelEditor read FOnSelectPanelEditor
229 +                                                       write FOnSelectPanelEditor;
230   end;
231  
232    {TIBGridControlLink}
# Line 253 | Line 263 | end;
263      FBookmark: TLocationArray;
264      FDBLookupCellEditor: TDBLookupCellEditor;
265      FActive: boolean;
266 +    FFieldPosition: integer;
267      procedure ColumnHeaderClick(Index: integer);
268      function GetDataSource: TDataSource;
269      function GetEditorBorderStyle: TBorderStyle;
# Line 298 | Line 309 | end;
309  
310   implementation
311  
312 < uses Math, IBQuery, LCLType;
312 > uses LCLType, Variants, EditBtn;
313  
314   { TIBGridControlLink }
315  
# Line 358 | Line 369 | begin
369    FResizing := true;
370    try
371      ColSum := 0;
361    for I := 0 to  ColCount - 1 do
362       ColSum := ColSum + ColWidths[I];
372  
373 <    if Colsum <> ClientWidth then
373 >    if (ColCount = 1) and TDBDynamicGridColumn(Columns[0]).AutoSizeColumn then
374 >      Columns[0].Width := ClientWidth
375 >    else
376      begin
377 <      ResizeColCount := 0;
378 <      for I := 0 to Columns.Count -1 do
379 <        if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn then
380 <        begin
381 <          Inc(ResizeColCount);
382 <          Colsum := Colsum + TDBDynamicGridColumn(Columns[I]).DesignWidth - Columns[I].Width;
383 <          Columns[I].Width := TDBDynamicGridColumn(Columns[I]).DesignWidth;
384 <        end;
385 <
386 <        if (Colsum < ClientWidth) and (ResizeColCount > 0) then
387 <        begin
388 <          adjustment := (ClientWidth - ColSum) div ResizeColCount;
389 <          n := (ClientWidth - ColSum) mod ResizeColCount;
390 <
391 <          for I := 0 to Columns.Count -1 do
392 <            if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn then
393 <            begin
394 <              if I = 0 then
395 <                Columns[I].Width := Columns[I].Width + adjustment + n
396 <              else
397 <                Columns[I].Width := Columns[I].Width + adjustment;
398 <            end;
399 <        end;
377 >      for I := 0 to  ColCount - 1 do
378 >        if (I < FixedCols) or Columns[I - FixedCols].Visible then
379 >         ColSum := ColSum + ColWidths[I];
380 >
381 >      if Colsum <> ClientWidth then
382 >      begin
383 >        ResizeColCount := 0;
384 >        for I := 0 to Columns.Count -1 do
385 >          if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn and Columns[I].Visible then
386 >          begin
387 >            Inc(ResizeColCount);
388 >            Colsum := Colsum + TDBDynamicGridColumn(Columns[I]).DesignWidth - Columns[I].Width;
389 >            Columns[I].Width := TDBDynamicGridColumn(Columns[I]).DesignWidth;
390 >          end;
391 >
392 >          if (Colsum < ClientWidth) and (ResizeColCount > 0) then
393 >          begin
394 >            adjustment := (ClientWidth - ColSum) div ResizeColCount;
395 >            n := (ClientWidth - ColSum) mod ResizeColCount;
396 >
397 >            for I := 0 to Columns.Count -1 do
398 >              if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn and Columns[I].Visible then
399 >              begin
400 >                if I = 0 then
401 >                  Columns[I].Width := Columns[I].Width + adjustment + n
402 >                else
403 >                  Columns[I].Width := Columns[I].Width + adjustment;
404 >              end;
405 >          end;
406 >      end;
407      end;
408      PositionTotals;
409      UpdateEditorPanelBounds;
# Line 399 | Line 417 | begin
417    inherited DoEditorHide;
418    if Editor = FEditorPanel then
419    begin
420 <    if (FExpandedRow >= 0) and (FExpandedRow < RowCount) then
421 <      RowHeights[FExpandedRow] := DefaultRowHeight;
420 >    if FMouseDown then
421 >      Application.QueueAsyncCall(@PerformEditorHide,FExpandedRow)
422 >    else
423 >      PerformEditorHide(FExpandedRow);
424      FExpandedRow := -1;
405    if CanFocus then SetFocus;
406    if assigned(FOnEditorPanelHide) then
407       OnEditorPanelHide(self);
408    DoOnResize;
409    ResetSizes;
410    Invalidate;
425    end;
426   end;
427  
428   procedure TDBDynamicGrid.DoEditorShow;
429   begin
430 +  if assigned(DataSource) and assigned(DataSource.DataSet) and
431 +             DataSource.DataSet.Active then
432 +  begin
433 +    if (DataSource.DataSet.RecordCount = 0) and (DataSource.DataSet.State <> dsInsert) then
434 +      DataSource.DataSet.Append
435 +  end;
436    if Editor = FEditorPanel then
437    begin
438      if ExpandEditorPanelBelowRow then
# Line 425 | Line 445 | begin
445      FEditorPanel.PerformTab(true);  {Select First Control}
446      if assigned(FOnEditorPanelShow) then
447         OnEditorPanelShow(self);
448 +    if assigned(Editor) and Editor.Visible then
449 +      Editor.SetFocus;
450    end
451    else
452      inherited DoEditorShow;
# Line 489 | Line 511 | begin
511      inherited KeyDown(Key, Shift);
512   end;
513  
514 + function TDBDynamicGrid.ActiveControl: TControl;
515 + var AParent: TWinControl;
516 + begin
517 +  Result := nil;
518 +  AParent := Parent;
519 +  while (AParent <> nil) and  not (AParent is TCustomForm) do
520 +    AParent := AParent.Parent;
521 +  if (AParent <> nil) and (AParent is TCustomForm)then
522 +      Result := TCustomForm(AParent).ActiveControl;
523 + end;
524 +
525   procedure TDBDynamicGrid.DoShowEditorPanel(Data: PtrInt);
526   begin
527    if AppDestroying in Application.Flags then Exit;
# Line 520 | Line 553 | end;
553   procedure TDBDynamicGrid.KeyDownHandler(Sender: TObject; var Key: Word;
554    Shift: TShiftState);
555   var Done: boolean;
556 +    AControl: TControl;
557   begin
558    if Visible and assigned(FEditorPanel) and FEditorPanel.Visible and FWeHaveFocus then
559    begin
560      Done := false;
561 +    AControl := ActiveControl;
562 +    if (AControl <> nil) and (AControl is TCustomComboBox)
563 +                         and ((Key in [VK_UP,VK_DOWN]) or
564 +                         (TCustomComboBox(AControl).DroppedDown and (Key = VK_RETURN)) or
565 +                         ((TCustomComboBox(AControl).Text <> '') and (Key =  VK_ESCAPE))) then
566 +      Exit; {ignore these keys if we are in a  combobox}
567 +
568 +    if (AControl <> nil) and (AControl is TCustomMemo)
569 +                         and (Key in [VK_RETURN,VK_UP,VK_DOWN]) then Exit; {Ignore keys in a CustomMemo}
570 +
571 +    if (AControl <> nil) and (AControl is TCustomGrid)
572 +                         and (Key in [VK_RETURN,VK_UP,VK_DOWN,VK_TAB]) then Exit; {Ignore keys in a Custom Grid}
573 +
574 +    if (AControl <> nil) and (AControl is TEBEdit) and (AControl.Owner is TDateEdit) then
575 +    begin
576 +      if (Key in [VK_LEFT,VK_RIGHT]) then Exit; {Ignore navigation keys}
577 +      if TDateEdit(AControl.Owner).DroppedDown and
578 +        (Key in [VK_RETURN,VK_UP,VK_DOWN,VK_ESCAPE]) then Exit; {Ignore TCalender navigation keys in a Data edit}
579 +    end;
580 +
581      if assigned(FOnKeyDownHander) then
582        OnKeyDownHander(Sender,Key,Shift,Done);
583      if Done then Exit;
# Line 546 | Line 600 | begin
600    end
601   end;
602  
603 + procedure TDBDynamicGrid.PerformEditorHide(Data: PtrInt);
604 + var ExpandedRow: integer;
605 + begin
606 +  if AppDestroying in Application.Flags then Exit;
607 +  ExpandedRow := integer(Data);
608 +  if (ExpandedRow >= 0) and (ExpandedRow < RowCount) then
609 +    RowHeights[ExpandedRow] := DefaultRowHeight;
610 +  if CanFocus then SetFocus;
611 +  DoOnResize;
612 +  ResetSizes;
613 +  DoOnChangeBounds;
614 +  if assigned(FOnEditorPanelHide) then
615 +     OnEditorPanelHide(self);
616 + end;
617 +
618   procedure TDBDynamicGrid.SetEditorPanel(AValue: TWinControl);
619   begin
620    if FEditorPanel = AValue then Exit;
621    if FEditorPanel <> nil then
622       RemoveFreeNotification(FEditorPanel);
623    FEditorPanel := AValue;
624 <  FreeNotification(FEditorPanel);
624 >  if FEditorPanel <> nil then
625 >     FreeNotification(FEditorPanel);
626   end;
627  
628   procedure TDBDynamicGrid.ChangeBounds(ALeft, ATop, AWidth, AHeight: integer;
# Line 580 | Line 650 | begin
650    inherited Loaded;
651    if assigned(FEditorPanel) and not (csDesigning in ComponentState)then
652      FEditorPanel.Visible := false;
653 <  DoGridResize
653 >  if Visible then
654 >    DoGridResize
655   end;
656  
657   procedure TDBDynamicGrid.DoOnResize;
# Line 604 | Line 675 | procedure TDBDynamicGrid.MouseDown(Butto
675    Y: Integer);
676   var Coord: TGridCoord;
677   begin
678 <  inherited MouseDown(Button, Shift, X, Y);
678 >  FMouseDown := true;
679 >  try
680 >    inherited MouseDown(Button, Shift, X, Y);
681 >  finally
682 >    FMouseDown := false;
683 >  end;
684  
685    Coord := MouseCoord(X,Y);
686    if (Coord.X = 0) and (Coord.Y > 0) then
# Line 613 | Line 689 | end;
689  
690   procedure TDBDynamicGrid.Notification(AComponent: TComponent;
691    Operation: TOperation);
692 + var i: integer;
693   begin
694    inherited Notification(AComponent, Operation);
695 <  if (Operation = opRemove) and
696 <     (AComponent = FEditorPanel) then FEditorPanel := nil;
695 >  if (Operation = opRemove) and not (csDestroying in ComponentState) then
696 >  begin
697 >    if AComponent = FEditorPanel then
698 >      FEditorPanel := nil
699 >    else
700 >    if AComponent is TControl then
701 >    begin
702 >      for i := 0 to Columns.Count - 1 do
703 >        if TDBDynamicGridColumn(Columns[I]).ColumnTotalsControl = AComponent then
704 >          TDBDynamicGridColumn(Columns[I]).ColumnTotalsControl := nil;
705 >    end;
706 >  end
707   end;
708  
709   procedure TDBDynamicGrid.TopLeftChanged;
# Line 629 | Line 716 | procedure TDBDynamicGrid.UpdateActive;
716   begin
717    inherited UpdateActive;
718  
719 +  if not (csLoading in ComponentState) and assigned(DataLink)
720 +                       and assigned(DataLink.DataSet) and DataLink.DataSet.Active then
721 +    DoGridResize;
722 +
723    if not (csLoading in ComponentState) and assigned(DataLink) and
724 +     assigned(FEditorPanel) and not FEditorPanel.Visible and
725       assigned(DataLink.DataSet) and (DataLink.DataSet.State = dsInsert) then
726       Application.QueueAsyncCall(@DoShowEditorPanel,0);
727   end;
# Line 662 | Line 754 | end;
754   procedure TDBDynamicGrid.HideEditorPanel;
755   begin
756    if Editor = FEditorPanel then
757 <    EditorMode := false;
757 >      EditorMode := false;
758   end;
759  
760   procedure TDBDynamicGrid.ShowEditorPanel;
761 + var aEditor: TWinControl;
762   begin
763    if (csDesigning in ComponentState) or
764     (DataSource = nil) or (DataSource.DataSet = nil)
765       or ((DataSource.DataSet.RecordCount = 0) and (DataSource.DataSet.State <> dsInsert)) then
766       Exit;
767 <  Editor := FEditorPanel;
767 >  aEditor := FEditorPanel;
768 >  if assigned(FOnSelectPanelEditor) then
769 >    OnSelectPanelEditor(self,aEditor);
770 >  if FEditorPanel <> aEditor then
771 >    SetEditorPanel(aEditor);
772 >  Editor := aEditor;
773    EditorMode := true;
774   end;
775  
# Line 710 | Line 808 | begin
808    Result := inherited Width
809   end;
810  
811 + type
812 +  THackedGrid = class(TIBDynamicGrid)
813 +  public
814 +    property FixedCols;
815 +  end;
816 +
817   { TDBLookupCellEditor }
818  
819 + function TDBLookupCellEditor.EditingKeyField: boolean;
820 + begin
821 +  with TIBDynamicGridColumn(TDBGrid(FGrid).Columns[FCol - THackedGrid(FGrid).FixedCols]) do
822 +    Result := CompareText(FieldName, DBLookupProperties.DataFieldName) = 0;
823 + end;
824 +
825   procedure TDBLookupCellEditor.WndProc(var TheMessage: TLMessage);
826   begin
827    if TheMessage.msg=LM_KILLFOCUS then begin
# Line 730 | Line 840 | begin
840    UpdateData(nil); {Force Record Update}
841    if FGrid<>nil then
842    Begin
843 <    (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, Trim(Text));
843 >    if EditingKeyField then
844 >    begin
845 >      if not VarIsNull(KeyValue) then
846 >       (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, KeyValue)
847 >    end
848 >    else
849 >      (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, Trim(Text));
850      (FGrid as TIBDynamicGrid).UpdateData;
851    end;
852    inherited CloseUp;
# Line 755 | Line 871 | begin
871    CheckAndInsert;
872    Msg.Col := FCol;
873    Msg.Row := FRow;
874 <  Msg.Value:= Trim(Text);
874 >  if EditingKeyField then
875 >  begin
876 >    if not VarIsNull(KeyValue) then
877 >      Msg.Value:= KeyValue
878 >    else
879 >      Msg.Value:= ''
880 >  end
881 >  else
882 >    Msg.Value:= Trim(Text);
883   end;
884  
885   procedure TDBLookupCellEditor.msg_SetGrid(var Msg: TGridMessage);
# Line 770 | Line 894 | begin
894    FCol := Msg.Col;
895    FRow := Msg.Row;
896    FEditText := Msg.Value;
773  SelStart := Length(Text);
897    TIBDynamicGrid(FGrid).SetupEditor(self,FCol);
898   end;
899  
# Line 819 | Line 942 | begin
942      Editor.OnAutoInsert := OnAutoInsert;
943      Editor.OnCanAutoInsert := OnCanAutoInsert;
944      Editor.OnDrawItem := OnDrawItem;
945 +    Editor.OnCloseUp := OnCloseUp;
946  
947      {Setup Data Links}
948      if KeyField <> '' then
# Line 843 | Line 967 | begin
967      if DataFieldName <> '' then
968          Editor.DataSource := TDBGrid(Grid).DataSource;
969    end;
970 <  Editor.Text := Editor.FEditText;
970 >  if Editor.EditingKeyField then
971 >  begin
972 >    if not Field.IsNull then
973 >      Editor.KeyValue := Editor.FEditText
974 >  end
975 >  else
976 >    Editor.Text := Editor.FEditText;
977 >  Editor.SelStart := Length(Editor.Text);
978   end;
979  
980   procedure TIBDynamicGridColumn.SetInitialSortColumn(AValue: boolean);
# Line 867 | Line 998 | end;
998   destructor TIBDynamicGridColumn.Destroy;
999   begin
1000    if assigned(FDBLookupProperties) then FDBLookupProperties.Free;
1001 +  Application.RemoveAsyncCalls(self);
1002    inherited Destroy;
1003   end;
1004  
# Line 884 | Line 1016 | begin
1016        OnColumnHeaderClick(self,Index);
1017  
1018      FLastColIndex := Index;
1019 <    if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active then
1019 >    FFieldPosition := 0;
1020 >    if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active
1021 >       and (DataSource.DataSet is TIBParserDataSet) then
1022      begin
1023 +      if FLastColIndex < Columns.Count then
1024 +      {try and cache field position while dataset still open}
1025 +        FFieldPosition := TIBParserDataSet(DataSource.DataSet).Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
1026        DataSource.DataSet.Active := false;
1027        Application.QueueAsyncCall(@DoReopen,0)
1028      end;
# Line 933 | Line 1070 | begin
1070    for i := 0 to Columns.Count - 1 do
1071    begin
1072      if TIBDynamicGridColumn(columns[i]).InitialSortColumn then
1073 +    begin
1074 +      FFieldPosition := 0;
1075        FLastColIndex := i
1076 +    end
1077    end
1078   end;
1079  
# Line 950 | Line 1090 | end;
1090  
1091   procedure TIBDynamicGrid.UpdateSQL(Sender: TObject; Parser: TSelectSQLParser);
1092   var OrderBy: string;
953    FieldPosition: integer;
1093   begin
1094      if assigned(DataSource) and assigned(DataSource.DataSet)
1095        and (DataSource.DataSet is TIBCustomDataSet) then
1096      begin
1097 <      if (FLastColIndex < 0) or (FLastColIndex >= Columns.Count) then Exit;
1098 <      FieldPosition := Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
1099 <      if FieldPosition = 0 then Exit;
1100 <
1101 <      if Descending then
1102 <        Parser.OrderByClause := IntToStr(FieldPosition) + ' desc'
1103 <      else
1104 <        Parser.OrderByClause := IntToStr(FieldPosition) + ' asc';
1097 >      if (FFieldPosition = 0) and (FLastColIndex >= 0) and (FLastColIndex < Columns.Count) then
1098 >        {Not cached - let's hope we can find it before the dataset is opened.
1099 >         Won't work if dynamic columns}
1100 >        FFieldPosition := Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
1101 >      if FFieldPosition > 0 then
1102 >      begin
1103 >        if Descending then
1104 >          Parser.OrderByClause := IntToStr(FFieldPosition) + ' desc'
1105 >        else
1106 >          Parser.OrderByClause := IntToStr(FFieldPosition) + ' asc';
1107 >      end;
1108  
1109        if assigned(FOnUpdateSortOrder) then
1110        begin
# Line 1049 | Line 1191 | end;
1191   procedure TIBDynamicGrid.Loaded;
1192   begin
1193    inherited Loaded;
1194 +  IBControlLinkChanged;
1195    ProcessColumns;
1196   end;
1197  
# Line 1104 | Line 1247 | end;
1247  
1248   procedure TIBDynamicGrid.Notification(AComponent: TComponent;
1249    Operation: TOperation);
1250 + var i: integer;
1251   begin
1252    inherited Notification(AComponent, Operation);
1253 <  if (Operation = opRemove) and
1254 <     (FIBControlLink <> nil) and (AComponent = DataSource) then FIBControlLink.IBDataSet := nil;
1253 >  if (Operation = opRemove) then
1254 >  begin
1255 >    if (FIBControlLink <> nil) and (AComponent = DataSource) then
1256 >      FIBControlLink.IBDataSet := nil
1257 >    else
1258 >    if AComponent is TDataSource then
1259 >    begin
1260 >      for i := 0 to Columns.Count - 1 do
1261 >        if TIBDynamicGridColumn(Columns[I]).DBLookupProperties.ListSource = AComponent then
1262 >          TIBDynamicGridColumn(Columns[I]).DBLookupProperties.ListSource := nil;
1263 >    end
1264 >  end
1265   end;
1266  
1267   procedure TIBDynamicGrid.UpdateActive;
# Line 1137 | Line 1291 | begin
1291    if assigned(FIBControlLink) then FIBControlLink.Free;
1292    if assigned(FIndexFieldsList) then FIndexFieldsList.Free;
1293    if assigned(FDBLookupCellEditor) then FDBLookupCellEditor.Free;
1294 +  Application.RemoveAsyncCalls(self);
1295    inherited Destroy;
1296   end;
1297  

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines