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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines