home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 December / Chip_2001-12_cd1.bin / zkuste / delphi / kolekce / d3456 / ALEXSOFT.ZIP / DBXGRID.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  2001-09-29  |  20.2 KB  |  592 lines

  1. (*////////////////////////////////////////////////////////////////////////////
  2. //   Part of AlexSoft VCL/DLL Library.                                      //
  3. //   All rights reserved. (c) Copyright 1998.                               //
  4. //   Created by: Alex Rabichooc                                             //
  5. //**************************************************************************//
  6. //  Users of this unit must accept this disclaimer of warranty:             //
  7. //    "This unit is supplied as is. The author disclaims all warranties,    //
  8. //    expressed or implied, including, without limitation, the warranties   //
  9. //    of merchantability and of fitness for any purpose.                    //
  10. //    The author assumes no liability for damages, direct or                //
  11. //    consequential, which may result from the use of this unit."           //
  12. //                                                                          //
  13. //  This Unit is donated to the public as public domain.                    //
  14. //                                                                          //
  15. //  This Unit can be freely used and distributed in commercial and          //
  16. //  private environments provided this notice is not modified in any way.   //
  17. //                                                                          //
  18. //  If you do find this Unit handy and you feel guilty for using such a     //
  19. //  great product without paying someone - sorry :-)                        //
  20. //                                                                          //
  21. //  Please forward any comments or suggestions to Alex Rabichooc at:        //
  22. //                                                                          //
  23. //  a_rabichooc@yahoo.com or alex@carmez.mldnet.com                         //
  24. /////////////////////////////////////////////////////////////////////////////*)
  25.  
  26. {---------------------------------------------------------------------------
  27.   TRaDBGrid - Extended TDBGrid component
  28.      properties
  29.        VisibleColCount: Integer;
  30.          - Use VisibleColCount to determine the number of scrollable columns
  31.            fully visible in the grid. VisibleColCount does not include the fixed
  32.            columns counted by the FixedCols property.
  33.            It does not include any partially visible columns on the right edge
  34.            of the grid.
  35.          - Set VisibleColCount to adjust TRaDBGrid Component to it's actual
  36.            Width;
  37.        VisibleRowCount: Integer;
  38.          - Use VisibleRowCount to determine the number of scrollable rows
  39.            fully visible in the grid. VisibleRowCount does not include the
  40.            fixed rows counted by the FixedRows property. It does not include
  41.            any partially visible rows on the bottom of the grid.
  42.          - Set VisibleRowCount to adjust TRaDBGrid Component to it's actual
  43.            Height;
  44. ----------------------------------------------------------------------------}
  45. unit dbXgrid;
  46.  
  47. interface
  48.  
  49. uses
  50.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  51.   Grids, DB, DBGrids, StdUtils;
  52.  
  53. type
  54.  
  55.   TRaDBGrid = class(TDBgrid)
  56.   private
  57.     FCanEditShow: boolean;
  58.     FMinRowCount: Integer;
  59.     FOnReplaceField: TFieldNotifyEvent;
  60.     FRedMinus: boolean;
  61.     procedure CMChildKey(var Message: TCMChildKey); message CM_CHILDKEY;
  62.     procedure CNReplaceField(var Message: TMessage); Message CN_REPLACEFIELD;
  63.     procedure CNCloseDBForm(var Message: TMessage); Message CN_CLOSEDBFORM;
  64.     function GetActualHeight: Integer;
  65.     function GetActualWidth: Integer;
  66.     function GetColWidth(Index: integer): Integer;
  67.     function GetRowHeight(Index: integer): Integer;
  68.     function GetVisColCount: Integer;
  69.     function GetVisRowCount: Integer;
  70.     procedure SetVisColCount(Value: Integer);
  71.     procedure SetVisRowCount(Value: Integer);
  72.     procedure SetOnReplaceField(const Value: TFieldNotifyEvent);
  73.     procedure SetRedMinus(const Value: boolean);
  74.   protected
  75.     function CanEditAcceptKey(Key: Char): Boolean; override;
  76.     function CanEditShow: Boolean; override;
  77.     procedure DoExit; override;
  78.     procedure EditButtonClick; override;
  79.     function  GetEditLimit: Integer; override;
  80.     procedure ColWidthsChanged; override;
  81.     procedure SetColumnAttributes; override;
  82.     procedure DrawColumnCell(const Rect: TRect; DataCol: Integer;
  83.       Column: TColumn; State: TGridDrawState); override;
  84.   public
  85.     constructor Create(AOwner:TComponent); override;
  86.     function FirstColumn: Integer;
  87.     procedure SetToActualWidth;
  88.     property ActualHeight: Integer read GetActualHeight;
  89.     property ActualWidth: Integer read GetActualWidth;
  90.     property ColWidth[Index: Integer]: Integer read GetColWidth;
  91.     property RowHeight[Index: Integer]: Integer read GetRowHeight;
  92.     procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
  93.     property MinRowCount: Integer read FMinRowCount write FMinRowCount;
  94.     property TabStops;
  95.   published
  96.     property VisibleColCount: Integer read GetVisColCount
  97.                                       write SetVisColCount stored False;
  98.     property VisibleRowCount: Integer read GetVisRowCount
  99.                                       write SetVisRowCount stored False;
  100.     property OnReplaceField: TFieldNotifyEvent read FOnReplaceField write SetOnReplaceField;
  101.     property RedMinus: boolean read FRedMinus write SetRedMinus;
  102.     property OnMouseDown;
  103.     property OnMouseUp;
  104.     property OnMouseMove;
  105.   end;
  106.  
  107. implementation
  108. uses StdCtrls, dbTools, dbConsts, dbCtrls, dbTables, dbPanel, dbBoxGrd, dbClient;
  109.  
  110. {TRaDBGrid}
  111. constructor TRaDBGrid.Create(AOwner: TComponent);
  112. begin
  113.    Inherited Create(AOwner);
  114.    Options := Options + [dgAlwaysShowEditor];
  115.    FRedMinus := True;
  116. end;
  117.  
  118. function TRaDBGrid.CanEditAcceptKey(Key: Char): Boolean;
  119. var AField: TField;
  120. begin
  121.   Result := Inherited CanEditAcceptKey(Key);
  122.   with Columns[SelectedIndex] do
  123.   if (Datasource <> nil) and (DataSource.DataSet <> nil) and
  124.      DataSource.DataSet.Active and Assigned(Field) and
  125.      (Field.FieldKind = fkData) and (Field.LookUpDataSet <> nil) then
  126.   begin
  127.      AField := Field.LookUpDataSet.FindField(Field.LookUpResultField);
  128.      if AField <> nil then
  129.          Result := AField.IsValidChar(Key);
  130.   end;
  131. end;
  132.  
  133. function TRaDBGrid.CanEditShow: Boolean;
  134. var Master: TDataSource;
  135. begin
  136.    FCanEditShow := Inherited CanEditShow;
  137.    if FCanEditShow then
  138.    begin
  139.       if (DataSource <> nil) and
  140.          (DataSource.DataSet <> nil) then
  141.       begin
  142.          Master := GetMasterSource(DataSource.DataSet);
  143.          if (Master <> nil) and
  144.             (Master.DataSet <> nil) and
  145.             (Master.DataSet.IsEmpty or
  146.                     (Master.DataSet.State in [dsEdit, dsInsert])) then
  147.          begin
  148.             FCanEditShow := False;
  149.             {$IFNDEF VER110}
  150.                if (((DataSource.DataSet is TNestedTable) and
  151.                     ((DataSource.DataSet as TNestedTable).DataSetField <> nil) and
  152.                     ((DataSource.DataSet as TNestedTable).DataSetField.DataSet = Master.DataSet)) or
  153.                    ((DataSource.DataSet is TClientDataSet) and
  154.                     ((DataSource.DataSet as TClientDataSet).DataSetField <> nil) and
  155.                     ((DataSource.DataSet as TClientDataSet).DataSetField.DataSet = Master.DataSet))) and
  156.                   not (Master.Dataset.IsEmpty or (Master.DataSet.State = dsInsert)) then
  157.                       FCanEditShow := True;
  158.             {$ENDIF}
  159.          end;
  160.        end;
  161.        if not FCanEditShow and (Parent is TRaDBPanel) then
  162.         with (Parent as TRaDBPanel) do
  163.         begin
  164.            if Box.CanFocus and (ActiveControl = Self) then
  165.               Box.DoKey(bgkFirstTab);
  166.            if (InplaceEditor <> nil) and InplaceEditor.Visible then
  167.               InplaceEditor.Hide;
  168.         end;
  169.    end;
  170.    Result := FCanEditShow;
  171. end;
  172.  
  173. procedure TRaDBGrid.ColWidthsChanged;
  174. begin
  175.    Inherited ColWidthsChanged;
  176.    InvalidateEditor;
  177. end;
  178.  
  179. procedure TRaDBGrid.SetColumnAttributes;
  180. var i, j, AWidth: Integer;
  181.     DefaultColumns: Boolean;
  182. begin
  183.    Inherited SetColumnAttributes;
  184.    if (DataSource=nil) or
  185.       (DataSource.DataSet=nil) or
  186.       not DataSource.DataSet.Active then
  187.      exit;
  188.    DefaultColumns := not StoreColumns;
  189.    for i := 0 to Columns.Count-1 do
  190.    begin
  191.       with Columns[i] do
  192.       if Field <> nil then
  193.       begin
  194.          SetFieldAttributes(Field);
  195.  
  196.          if DefaultColumns and not ReadOnly then
  197.          begin
  198.             if IsLookUpField(Field) then
  199.             begin
  200.                for j := 0 to Columns.Count-1 do
  201.                begin
  202.                   if (Columns[i].Field <> Columns[j].Field) then
  203.                   with Columns[j].Field do
  204.                   if UpperCase(Columns[i].Field.KeyFields) = UpperCase(FieldName) then
  205.                        break;
  206.                   if j = Columns.Count-1 then
  207.                      ButtonStyle := cbsEllipsis;
  208.                end;
  209.                if ButtonStyle <> cbsEllipsis then
  210.                   TabStops[i+ColCount - Columns.Count] := False;
  211.             end
  212.              else
  213.               case Field.DataType of
  214.                 ftMemo, ftFmtMemo, ftGraphic, ftTypedBinary, ftDate, ftDateTime:
  215.                       ButtonStyle := cbsEllipsis;
  216.                else
  217.                 if Field.DataSet <> nil then
  218.                 for j := 0 to Field.DataSet.FieldCount-1 do
  219.                 with Field.DataSet.Fields[j] do
  220.                   if (UpperCase(KeyFields) =
  221.                                     UpperCase(Columns[i].Field.FieldName)) then
  222.                   begin
  223.                      ButtonStyle := cbsEllipsis;
  224.                      break;
  225.                   end;
  226.               end;
  227.               if ButtonStyle = cbsEllipsis then
  228.               begin
  229.                 AWidth := GetDefaultWidth(Columns[i].Font,
  230.                                     Columns[i].Field.DisplayWidth)+
  231.                                           GetSystemMetrics(SM_CXVSCROLL);
  232.                 if AWidth > ColWidths[i+ColCount-Columns.Count] then
  233.                   ColWidths[i+ColCount-Columns.Count] := AWidth;
  234.               end;
  235.          end;
  236.       end;
  237.    end;
  238.    SetBounds(Left, Top, Width, Height);
  239. end;
  240.  
  241. function TRaDBGrid.GetVisColCount: Integer;
  242. begin
  243.    Result := Inherited VisibleColCount;
  244. end;
  245.  
  246. function TRaDBGrid.GetColWidth(Index: integer): Integer;
  247. var ColWidth: Integer;
  248. begin
  249.    ColWidth := ColWidths[Index];
  250.    if dgColLines in Options then
  251.      Inc(ColWidth, GridLineWidth);
  252.    Result := ColWidth;
  253. end;
  254.  
  255. function TRaDBGrid.GetRowHeight(Index: integer): Integer;
  256. var RowHeight: Integer;
  257. begin
  258.    RowHeight := 0;
  259.    case Index of
  260.      0: begin
  261.           if dgTitles in Options then
  262.             if dgRowLines in Options then
  263.                 RowHeight := RowHeights[0] + GridLineWidth
  264.               else
  265.                 RowHeight := RowHeights[0];
  266.         end;
  267.      else
  268.         begin
  269.            if dgRowLines in Options then
  270.                 RowHeight := RowHeights[1] + GridLineWidth
  271.               else
  272.                 RowHeight := RowHeights[1];
  273.         end;
  274.    end;
  275.    Result := RowHeight;
  276. end;
  277.  
  278. function TRaDBGrid.GetVisRowCount: Integer;
  279. var AHeight: Integer;
  280. begin
  281.    AHeight := Height - RowHeight[0];
  282.    if ScrollBars in [ssHorizontal, ssBoth] then
  283.      Dec(AHeight, 4+GetSystemMetrics(SM_CYHSCROLL));
  284.    Result := AHeight div RowHeight[1];
  285. end;
  286.  
  287. procedure TRaDBGrid.CMChildKey(var Message: TCMChildKey);
  288. var ShiftState: TShiftState;
  289.  
  290. type
  291.   TSelection = record
  292.     StartPos, EndPos: Integer;
  293.   end;
  294.  
  295.   function Selection: TSelection;
  296.   begin
  297.     if InplaceEditor <> nil then
  298.       SendMessage(InplaceEditor.Handle, EM_GETSEL, Longint(@Result.StartPos), Longint(@Result.EndPos))
  299.      else
  300.      begin
  301.         Result.StartPos := 0;
  302.         Result.EndPos := 0;
  303.      end;
  304.   end;
  305.  
  306.   function GetTextLen: Integer;
  307.   begin
  308.     if InplaceEditor <> nil then
  309.        Result := InplaceEditor.GetTextLen
  310.       else
  311.        Result := 0;
  312.    end;
  313.  
  314.   function RightSide: Boolean;
  315.   begin
  316.     with Selection do
  317.       Result := ((StartPos = 0) or (EndPos = StartPos)) and
  318.         (EndPos = GetTextLen);
  319.    end;
  320.  
  321.   function LeftSide: Boolean;
  322.   begin
  323.     with Selection do
  324.       Result := (StartPos = 0) and ((EndPos = 0) or (EndPos = GetTextLen));
  325.   end;
  326.  
  327. begin
  328.   ShiftState := GetShiftState;
  329.   with Message do
  330.   begin
  331.      case CharCode of
  332.          VK_F1:
  333.                if ShiftState = [ssCtrl] then
  334.                   if (Columns[SelectedIndex].ButtonStyle = cbsEllipsis)
  335.                      {$IFNDEF VER110}
  336.                        or ((SelectedField<>nil) and (SelectedField.DataType = ftDataSet))
  337.                      {$ENDIF} then
  338.                      EditButtonClick;
  339.          VK_RETURN:
  340.                if ShiftState = [] then
  341.                begin
  342.                   if InplaceEditor <> nil then
  343.                     SendMessage(InplaceEditor.Handle, WM_KEYDOWN, CharCode,
  344.                                                       TMessage(Message).LParam);
  345.                   CharCode := VK_TAB;
  346.                   Result := 1;
  347.                end;
  348.          VK_F8: if ShiftState = [] then
  349.                 begin
  350.                   Include(ShiftState, ssCtrl);
  351.                   CharCode := VK_DELETE;
  352.                   Result := 1;
  353.                 end;
  354.          VK_PRIOR:
  355.                 if ShiftState = [ssCtrl] then
  356.                 begin
  357.                   CharCode := VK_HOME;
  358.                   Result := 1;
  359.                 end;
  360.          VK_NEXT:
  361.                 if ShiftState = [ssCtrl] then
  362.                 begin
  363.                   CharCode := VK_END;
  364.                   Result := 1;
  365.                 end;
  366.          VK_RIGHT:
  367.                 if (ShiftState = []) and RightSide then
  368.                 begin
  369.                   if InplaceEditor <> nil then
  370.                      SendMessage(InplaceEditor.Handle, WM_KEYDOWN, VK_RETURN,
  371.                                                   TMessage(Message).LParam);
  372.                   CharCode := VK_TAB;
  373.                   Result := 1;
  374.                 end;
  375.          VK_LEFT:
  376.                 if (ShiftState = []) and LeftSide then
  377.                 begin
  378.                   if InplaceEditor <> nil then
  379.                      SendMessage(InplaceEditor.Handle, WM_KEYDOWN, VK_RETURN,
  380.                                                   TMessage(Message).LParam);
  381.                   Include(ShiftState, ssShift);
  382.                   CharCode := VK_TAB;
  383.                   Result := 1;
  384.                 end;
  385.      end;
  386.      if Result = 1 then
  387.      begin
  388.         KeyDown(CharCode, ShiftState);
  389.         exit;
  390.      end;
  391.   end;
  392.   inherited;
  393. end;
  394.  
  395. procedure TRaDBGrid.CNReplaceField(var Message: TMessage);
  396. var AField: TField;
  397.     Form: TCustomForm;
  398. begin
  399.    if CanFocus and FCanEditShow then
  400.    begin
  401.       AField := pointer(Message.LParam);
  402.       if Assigned(FOnReplaceField) then
  403.         FOnReplaceField(AField)
  404.        else
  405.         ReplaceField(AField);
  406.       SelectedField := AField;
  407.       SetFocus;
  408.    end;
  409.    Form := GetParentForm(Self);
  410.    if (Form <> nil) and (Form.CanFocus) then
  411.       Form.SetFocus;
  412. end;
  413.  
  414. procedure TRaDBGrid.CNCloseDBForm(var Message: TMessage);
  415. begin
  416.    if (DataSource <> nil) and
  417.       (DataSource.DataSet <> nil) and
  418.       DataSource.DataSet.Active then
  419.      if Message.WParam = mrCancel then
  420.         DataSource.DataSet.Cancel
  421.        else
  422.         DataSource.DataSet.CheckBrowseMode;
  423. end;
  424.  
  425. procedure TRaDBGrid.DoExit;
  426. begin
  427.    if (DataSource <> nil) and
  428.       (DataSource.DataSet <> nil) and
  429.       (DataSource.DataSet.Modified) then
  430.      DataSource.DataSet.Post;
  431.    Inherited DoExit;
  432. end;
  433.  
  434. procedure TRaDBGrid.EditButtonClick;
  435. begin
  436.    if Assigned(OnEditButtonClick) then
  437.       Inherited EditButtonClick
  438.      else
  439.       DoEditButtonClick(SelectedField, InplaceEditor);
  440. end;
  441.  
  442. function TRaDBGrid.GetActualHeight: Integer;
  443. var AHeight: Integer;
  444.     ARowCount: Integer;
  445. begin
  446.    ARowCount := (Height-RowHeight[0]) div RowHeight[1];
  447.    if ARowCount < MinRowCount then
  448.         ARowCount := MinRowCount;
  449.    AHeight := RowHeight[0]+RowHeight[1]*ARowCount;
  450.    if (ScrollBars in [ssHorizontal, ssBoth]) and (Width <> ActualWidth) then
  451.      Inc(AHeight, 4+GetSystemMetrics(SM_CYHSCROLL));
  452.    Result := AHeight;
  453. end;
  454.  
  455. function TRaDBGrid.GetActualWidth: Integer;
  456. var i: Integer;
  457.     AWidth: Integer;
  458. begin
  459.    AWidth := GetSystemMetrics(SM_CYVSCROLL)+4;
  460.    for i := 0 to ColCount-1 do
  461.       AWidth := AWidth + ColWidth[i];
  462.    Result := AWidth;
  463. end;
  464.  
  465. function TRaDBGrid.FirstColumn: Integer;
  466. var i: Integer;
  467. begin
  468.     Result := -1;
  469.     for i := 0 to Columns.Count-1 do
  470.       if TabStops[i+ColCount - Columns.Count] then
  471.       begin
  472.          Result := i;
  473.          break;
  474.       end;
  475. end;
  476.  
  477. procedure TRaDBGrid.SetToActualWidth;
  478. begin
  479.    Width := ActualWidth;
  480. end;
  481.  
  482. procedure TRaDBGrid.SetVisColCount(Value: Integer);
  483. var i: Integer;
  484.     AWidth: Integer;
  485. begin
  486.    if Value >= ColCount then
  487.       Value := ColCount-1;
  488.    AWidth := 0;
  489.    for i := 0 to Value do
  490.       AWidth := AWidth + ColWidth[i];
  491.    Width := AWidth+GetSystemMetrics(SM_CYVSCROLL)+4;
  492. end;
  493.  
  494. procedure TRaDBGrid.SetVisRowCount(Value: Integer);
  495. var AHeight: Integer;
  496. begin
  497.    AHeight := RowHeight[0]+RowHeight[1]*Value;
  498.    if ScrollBars in [ssHorizontal, ssBoth] then
  499.      Inc(AHeight, 4+GetSystemMetrics(SM_CYHSCROLL));
  500.    Height := AHeight;
  501. end;
  502.  
  503. function TRaDBGrid.GetEditLimit: Integer;
  504. var AField: TField;
  505. begin
  506.   if Assigned(SelectedField) and (SelectedField.DataType = ftString) and
  507.      (SelectedField.LookUpDataSet <> nil) then
  508.   begin
  509.      with SelectedField do
  510.         AField := LookUpDataSet.FindField(LookUpResultField);
  511.      if (AField <> nil) then
  512.         Result := AField.Size
  513.        else
  514.         Result := SelectedField.DisplayWidth;
  515.   end
  516.    else
  517.     Result := Inherited GetEditLimit;
  518. end;
  519.  
  520. procedure TRaDBGrid.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
  521. var ButtonVisible: Boolean;
  522.     MasterField: TField;
  523.     ActWidth, i: Integer;
  524. begin
  525.    Inherited SetBounds(ALeft, ATop, AWidth, AHeight);
  526.    if (Columns <> nil) then
  527.    begin
  528.       {for i := 0 to Columns.Count-1 do
  529.         with Columns[i] do
  530.         if Field <> nil then
  531.             SetFieldAttributes(Field);}
  532.  
  533.       if Columns[Columns.Count-1].Field <> nil then
  534.       with Columns[Columns.Count-1] do
  535.       begin
  536.          ButtonVisible := False;
  537.          AWidth := GetDefaultWidth(Font, Field.DisplayWidth);
  538.          case ButtonStyle of
  539.             cbsEllipsis: ButtonVisible := True;
  540.             cbsAuto:
  541.               if Field.FieldKind = fkLookup then
  542.               begin
  543.                  MasterField := nil;
  544.                  if (DataLink.DataSet <> nil) and (Field <> nil) then
  545.                     MasterField := DataLink.DataSet.FindField(Field.KeyFields);
  546.                  if Assigned(MasterField) and MasterField.CanModify and
  547.                     not ((cvReadOnly in AssignedValues) and ReadOnly) then
  548.                   if not Self.ReadOnly and DataLink.Active and
  549.                      not Datalink.ReadOnly then
  550.                       ButtonVisible := True;
  551.               end
  552.                 else
  553.                  if Assigned(Picklist) and
  554.                    (PickList.Count > 0) and
  555.                    not Readonly then
  556.                       ButtonVisible := True;
  557.          end;
  558.          if ButtonVisible then
  559.            AWidth := AWidth + GetSystemMetrics(SM_CXVSCROLL);
  560.          ActWidth := GetSystemMetrics(SM_CYVSCROLL)+4+AWidth;
  561.          for i := 0 to ColCount-2 do
  562.             ActWidth := ActWidth + ColWidth[i];
  563.          if (Self.Width-ActWidth > 0) and (Columns.Count <= ColCount) then
  564.             ColWidths[ColCount-1] := AWidth + Self.Width-ActWidth
  565.            else
  566.             ColWidths[ColCount-1] := AWidth;
  567.          //SetFieldAttributes(Field);
  568.       end;
  569.    end;
  570. end;
  571.  
  572. procedure TRaDBGrid.SetOnReplaceField(const Value: TFieldNotifyEvent);
  573. begin
  574.   FOnReplaceField := Value;
  575. end;
  576.  
  577. procedure TRaDBGrid.DrawColumnCell(const Rect: TRect; DataCol: Integer;
  578.   Column: TColumn; State: TGridDrawState);
  579. begin
  580.    if FRedMinus and (Column <> nil) and (Column.Field <> nil) and
  581.       (Column.Field is TFloatField) and (Column.Field.AsFloat < 0) then
  582.      Canvas.Font.Color := clRed;
  583.    inherited;
  584. end;
  585.  
  586. procedure TRaDBGrid.SetRedMinus(const Value: boolean);
  587. begin
  588.   FRedMinus := Value;
  589. end;
  590.  
  591. end.
  592.