unit MTEdit;

interface

{
  15.03.00 - minor bugfixes (when leaving cell by mouse click contens
             was remaining unchanged)
}

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Grids, ComCtrls, ToolWin, ExtCtrls, StdCtrls, Menus, Buttons, Matrices,
  MiscRoutines, ClipBrd, Setup, SharedData, SharedCode;

  // "Снимок" участка сетки. Используется при отмене / восстановлении.

type
  TCellRangeInfo = class(TObject)
  private
    L,T         : integer;
    Contens     : TMatrix;
    OneValue    : double;
    function    GetLeft:integer;
    function    GetTop:integer;
    function    GetRight:integer;
    function    GetBottom:integer;
  public
    constructor Create;
    destructor  Destroy;override;
    //          ВСЕМ методам HoldXX передаются координаты в сетке
    procedure   Hold(Grid:TStringGrid;_L,_T,_R,_B:integer);
    procedure   HoldFromMatrix(var Mat:TMatrix;_L,_T,_R,_B:integer);
    procedure   HoldFromVector(var Vec:TVector;_L,_T,_R,_B:integer);
    function    Fetch(Grid:TStringGrid):Boolean;
    property    Left:integer read GetLeft;
    property    Top:integer read GetTop;
    property    Right:integer read GetRight;
    property    Bottom:integer read GetBottom;
  end;

type
  TMTEditForm = class(TForm)
    MatrixGrid: TStringGrid;
    ToolBar: TToolBar;
    MatrixMenu: TPopupMenu;
    ClearAllItem: TMenuItem;
    SelectAllItem: TMenuItem;
    ClearSelectionItem: TMenuItem;
    FillAllItem: TMenuItem;
    ClearRowItem: TMenuItem;
    ClearColumnItem: TMenuItem;
    FillRowItem: TMenuItem;
    FillColumnItem: TMenuItem;
    CopyItem: TMenuItem;
    PasteItem: TMenuItem;
    DeselectItem: TMenuItem;
    FillSelectionItem: TMenuItem;
    MatrixMainMenu: TMainMenu;
    EditMenu: TMenuItem;
    CutItem: TMenuItem;
    CutMenuItemP: TMenuItem;
    OpenDialog: TOpenDialog;
    SaveDialog: TSaveDialog;
    NameItem: TMenuItem;
    SelectAllButton: TToolButton;
    DeselectButton: TToolButton;
    ToolButton3: TToolButton;
    CutButton: TToolButton;
    CopyButton: TToolButton;
    PasteButton: TToolButton;
    ToolButton7: TToolButton;
    FillAllButton: TToolButton;
    FillRowButton: TToolButton;
    FillColumnButton: TToolButton;
    FillSelectionButton: TToolButton;
    ToolButton12: TToolButton;
    ClearAllButton: TToolButton;
    ClearRowButton: TToolButton;
    ClearColumnButton: TToolButton;
    ClearSelectionButton: TToolButton;
    ToolButton17: TToolButton;
    NameButton: TToolButton;
    ILDisabled: TImageList;
    ILNormal: TImageList;
    ILHot: TImageList;
    UndoItem: TMenuItem;
    UndoButton: TToolButton;
    ToolButton2: TToolButton;
    procedure OnCreate(Sender: TObject);
    procedure ClearClick(Sender: TObject);
    procedure MatrixGridKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure ClearAllItemClick(Sender: TObject);
    procedure ClearRowItemClick(Sender: TObject);
    procedure ClearColumnItemClick(Sender: TObject);
    procedure SelectAllItemClick(Sender: TObject);
    procedure DeselectItemClick(Sender: TObject);
    procedure ClearSelectionItemClick(Sender: TObject);
    procedure FillAllItemClick(Sender: TObject);
    procedure FillRowItemClick(Sender: TObject);
    procedure FillColumnItemClick(Sender: TObject);
    procedure FillSelectionItemClick(Sender: TObject);
    procedure CopyItemClick(Sender: TObject);
    procedure PasteItemClick(Sender: TObject);
    procedure OpenItemClick(Sender: TObject);
    procedure SaveAsItemClick(Sender: TObject);
    procedure SaveItemClick(Sender: TObject);
    procedure CutItemClick(Sender: TObject);
    procedure NameItemClick(Sender: TObject);
    procedure MatrixGridKeyPress(Sender: TObject; var Key: Char);
    procedure FormDestroy(Sender: TObject);
    procedure UndoButtonClick(Sender: TObject);
    procedure FormDeactivate(Sender: TObject);
    procedure MatrixGridSetEditText(Sender: TObject; ACol, ARow: Integer;
      const Value: String);
  private
    Matrix          : PStreamableMatrix;
    Vector          : PStreamableVector;
    UndoBuffer      : array[0..1] of TCellRangeInfo;
    HoldIndex       : integer;
    _SourceChanged  : Boolean;
    procedure       CellChanged;
    procedure       HoldData(L,T,R,B:integer);
    function        ValidateRegion(L,T,R,B:integer):Boolean;
    function        UpdateRegion(L,T,R,B:integer):Boolean;
    function        FillRegion(L,T,R,B:integer;Value:double):Boolean;
    // True если в редактируемой ячейке уже есть символ экспоненты
    function        ExpPresent:Boolean;
    // True если в редактируемой ячейке уже есть десятичная точка
    function        DecimalPresent:Boolean;
    procedure       UpdateControls(ForceChange:Boolean);
    procedure       SetSourceChanged(SrcChanged:Boolean);
    function        GetSourceChanged:Boolean;
  public
    Recreatable     : Boolean;
    CanLoad         : Boolean;
    CanSave         : Boolean;
    OnChangeCells   : procedure(Sender:TObject) of object;stdcall;
    function        OnLeaveCell(Col,Row:integer):Boolean;
    procedure       AttachMatrix(_Matrix:PStreamableMatrix);
    procedure       AttachVector(_Vector:PStreamableVector);
    procedure       Disconnect;
    function        DataAttached:Boolean;

    procedure       SelectAll;
    procedure       RemoveSelection;

    procedure       CopyToClipboard;
    procedure       CutToClipboard;
    procedure       PasteFromClipboard;

    procedure       FillRow(R:integer;Value:double);
    procedure       FillCol(C:integer;Value:double);
    procedure       FillGrid(Value:double);
    procedure       FillSelection(Value:double);

    procedure       ClearRow(R:integer);
    procedure       ClearCol(C:integer);
    procedure       ClearGrid;
    procedure       ClearSelection;

    procedure       EnableGrid(OkCancel:Boolean);

    // Истина, если был произведен опрос таблицы. Не сбрасывается.
    property        SourceChanged : Boolean read GetSourceChanged
      write SetSourceChanged;
  end;

implementation

uses MainUnit;

{$R *.DFM}

// =======================================================================
// Сервисные функции (частные)
// =======================================================================

procedure TMTEditForm.CellChanged;
begin
  UpdateControls(True);
  if(@OnChangeCells<>nil) then
    OnChangeCells(Self);
end;

// {$OPTIMIZATION ON}

function TMTEditForm.ValidateRegion;
begin
{$IFDEF DEBUG}
  Result:=(DataAttached) and (L<=R) and (T<=B) and (L>=0) and (T>=0) and
    (R<MatrixGrid.ColCount) and (B<MatrixGrid.RowCount);
{$ELSE}
  Result:=True;
{$ENDIF}
end;

// {$OPTIMIZATION OFF}

procedure TMTEditForm.HoldData;
begin
  if(ValidateRegion(L,T,R,B)) then
  begin
    if(Vector<>nil) then
    begin
      if(HoldIndex<>-1) then
        UndoBuffer[HoldIndex].HoldFromVector(Vector^,L,T,R,B);
    end
    else
    begin
      Assert(Matrix<>nil);
      if(HoldIndex<>-1) then
        UndoBuffer[HoldIndex].HoldFromMatrix(Matrix^,L,T,R,B);
    end;
  end;
end;

function TMTEditForm.FillRegion;
var
  i,j : integer;
  s : string;
begin
  Result:=ValidateRegion(L,T,R,B);
  if(Result) then
  begin
    HoldData(L,T,R,B);
    s:=FloatToStr(Value);
    if(Vector<>nil) then
    begin
      for j:=L to R do
      begin
        MatrixGrid.Cells[j,T]:=s;
        Vector^.SetElement(j,Value);
      end;
    end
    else
    begin
      Assert(Matrix<>nil);
      for i:=T to B do
      begin
        for j:=L to R do
        begin
          MatrixGrid.Cells[j,i]:=s;
          Matrix^.SetElement(i-1,j-1,Value);
        end;
      end;
    end;
    CellChanged;
  end;
end;

function TMTEditForm.UpdateRegion;
var
  i,j : integer;
begin
  Result:=ValidateRegion(L,T,R,B);
  if(not Result) then exit;
  try
    if(Vector<>nil) then
    begin
      if(HoldIndex<>-1) then
        UndoBuffer[HoldIndex].HoldFromVector(Vector^,L,T,R,B);
      for j:=L to R do
        Vector^.SetElement(j,StrToFloat(MatrixGrid.Cells[j,T]));
      CellChanged;
    end
    else
    begin
      Assert(Matrix<>nil);
      if(HoldIndex<>-1) then
        UndoBuffer[HoldIndex].HoldFromMatrix(Matrix^,L,T,R,B);
      for i:=T to B do
        for j:=L to R do
          Matrix^.SetElement(i-1,j-1,StrToFloat(MatrixGrid.Cells[j,i]));
      CellChanged;
    end;
    Result:=True;
  except
    on EConvertError do
    begin
      Result:=False;
      exit;
    end;
  end;
end;

function TMTEditForm.ExpPresent;
var
  S : string;
begin
  S:=MatrixGrid.Cells[MatrixGrid.Col,MatrixGrid.Row];
  Result:=(Pos('e',S)<>0) or (Pos('E',S)<>0);
end;

function TMTEditForm.DecimalPresent;
begin
  if(SharedData.DecimalSymbol<>'') then
  begin
    Result:=Pos(SharedData.DecimalSymbol[1],
      MatrixGrid.Cells[MatrixGrid.Col,MatrixGrid.Row])<>0;
  end
  else
    REsult:=False;
end;

function TMTEditForm.OnLeaveCell;
begin
  Result:=UpdateRegion(Col,Row,Col,Row);
  if(Result) then
  begin
    if(Vector<>nil) then
    begin
      MatrixGrid.Cells[Col,Row]:=
        FloatToStr(Vector^.GetElement(Col));
    end
    else
    begin
      if(Matrix<>nil) then
      begin
        MatrixGrid.Cells[Col,Row]:=
          FloatToStr(Matrix^.GetElement(Row-1,Col-1));
      end;
    end;
  end;
end;

function TMTEditForm.GetSourceChanged;
begin
  Result:=_SourceChanged;
end;

procedure TMTEditForm.SetSourceChanged;
begin
  _SourceChanged:=SrcChanged;
  UndoItem.Enabled:=_SourceChanged;
  UndoButton.Enabled:=_SourceChanged;
end;

procedure TMTEditForm.UpdateControls;
begin
  // Если нет данных - нечего сохранять и редактировать
  Enabled:=DataAttached;
  NameItem.Enabled:=Enabled;
  if(not Enabled) then
  begin
    WindowState:=wsMinimized;
    exit;
  end;
  SetSourceChanged(_SourceChanged or ForceChange);
  // Если буфер обмена не содержит данных, то вставлять нечего
  PasteItem.Enabled:=ClipBoard.HasFormat(CF_TEXT);
  // Обновляем состояние кнопок
  PasteButton.Enabled:=PasteItem.Enabled;
  MainForm.CaptionChanged;
end;

// =======================================================================
// Сервисные функции (общедоступные)
// =======================================================================

procedure TMTEditForm.AttachMatrix;
var
  i,j : integer;
begin
  Matrix:=_Matrix;
  SourceChanged:=False;
  if(Matrix<>nil) then
  begin
    with MatrixGrid do
    begin
      if(FixedCols<>1) then FixedCols:=1;
      if(FixedRows<>1) then FixedRows:=1;
      if(ColCount<>(Matrix^.nCols+1)) then ColCount:=Matrix^.nCols+1;
      if(RowCount<>(Matrix^.nRows+1)) then RowCount:=Matrix^.nRows+1;
      for i:=1 to ColCount-1 do
        Cells[i,0]:=Format('%d',[i]);
      for j:=1 to RowCount-1 do
        Cells[0,j]:=Format('%d',[j]);
      for i:=0 to Matrix^.nRows-1 do
        for j:=0 to Matrix^.nCols-1 do
          Cells[j+1,i+1]:=Matrix^.GetString(i,j);
    end;
  end;
  UpdateControls(False);
end;

procedure TMTEditForm.AttachVector;
var
  i : integer;
begin
  Vector:=_Vector;
  SourceChanged:=False;
  if(Vector<>nil) then
  begin
    with MatrixGrid do
    begin
      if(FixedCols<>0) then FixedCols:=0;
      if(FixedRows<>1) then FixedRows:=1;
      if(ColCount<>Vector^.nSize) then ColCount:=Vector^.nSize;
      if(RowCount<>2) then RowCount:=2;
      for i:=0 to ColCount-1 do
        Cells[i,0]:=Format('%d',[i+1]);
      for i:=0 to Vector^.nSize-1 do
        Cells[i,1]:=Vector^.GetString(i);
    end;
  end;
  UpdateControls(False);
end;

procedure TMTEditForm.Disconnect;
begin
  Vector:=nil;
  Matrix:=nil;
  SourceChanged:=False;
  UpdateControls(False);
end;

function TMTEditForm.DataAttached;
begin
  Result:=(Matrix<>nil) or (Vector<>nil);
end;

procedure TMTEditForm.FillRow;
begin
  FillRegion(MatrixGrid.FixedCols,R,MatrixGrid.ColCount-1,R,Value);
end;

procedure TMTEditForm.FillCol;
begin
  FillRegion(C,MatrixGrid.FixedRows,C,MatrixGrid.RowCount-1,Value);
end;

procedure TMTEditForm.FillGrid;
begin
  FillRegion(MatrixGrid.FixedCols,MatrixGrid.FixedRows,
    MatrixGrid.ColCount-1,MatrixGrid.RowCount-1,Value);
end;

procedure TMTEditForm.FillSelection;
begin
  FillRegion(MatrixGrid.Selection.Left,MatrixGrid.Selection.Top,
    MatrixGrid.Selection.Right,MatrixGrid.Selection.Bottom,Value);
end;

procedure TMTEditForm.ClearRow;
begin
  FillRegion(MatrixGrid.FixedCols,R,MatrixGrid.ColCount-1,R,0.0);
end;

procedure TMTEditForm.ClearCol;
begin
  FillRegion(C,MatrixGrid.FixedRows,C,MatrixGrid.RowCount-1,0.0);
end;

procedure TMTEditForm.ClearGrid;
begin
  FillRegion(MatrixGrid.FixedCols,MatrixGrid.FixedRows,
    MatrixGrid.ColCount-1,MatrixGrid.RowCount-1,0.0);
end;

procedure TMTEditForm.ClearSelection;
begin
  FillRegion(MatrixGrid.Selection.Left,MatrixGrid.Selection.Top,
    MatrixGrid.Selection.Right,MatrixGrid.Selection.Bottom,0.0);
end;

procedure TMTEditForm.SelectAll;
var
  Rc : TGridRect;
begin
  Rc.Left:=MatrixGrid.FixedCols;
  Rc.Top:=MatrixGrid.FixedRows;
  Rc.Right:=MatrixGrid.ColCount-1;
  Rc.Bottom:=MatrixGrid.RowCount-1;
  MatrixGrid.Selection:=Rc;
end;

procedure TMTEditForm.RemoveSelection;
var
  Rc : TGridRect;
begin
  Rc.Left:=MatrixGrid.FixedCols;
  Rc.Top:=MatrixGrid.FixedRows;
  Rc.Right:=Rc.Left;
  Rc.Bottom:=Rc.Top;
  MatrixGrid.Selection:=Rc;
end;

procedure TMTEditForm.CopyToClipboard;
var
  s : string;
  i, j : integer;
begin
  if(not DataAttached) then exit;
  for i:=MatrixGrid.Selection.Top to MatrixGrid.Selection.Bottom do
  begin
    for j:=MatrixGrid.Selection.Left to MatrixGrid.Selection.Right-1 do
      s:=s+MatrixGrid.Cells[j,i]+#9;
    s:=s+MatrixGrid.Cells[MatrixGrid.Selection.Right,i]+#13;
  end;
  Clipboard.SetTextBuf(@(s[1]));
end;

procedure TMTEditForm.CutToClipboard;
begin
  CopyToClipboard;
  ClearSelection;
  UpdateRegion(MatrixGrid.Selection.Left,MatrixGrid.Selection.Top,
    MatrixGrid.Selection.Right,MatrixGrid.Selection.Bottom);
end;

procedure TMTEditForm.PasteFromClipboard;
var
  buffer, ptr : pointer;
  size : integer;
  s : string;
  i,j : integer;
begin
  // Пусть на каждую ячейку приходится 64 символа (достаточно много)
  // Тогда памяти потребуется RowCount * ColCount * 64 или меньше
  if(not DataAttached) then exit;
  size:=MatrixGrid.RowCount * MatrixGrid.ColCount * 64;
  GetMem(buffer,size);
  memZeroMemory(buffer,size);
  ptr:=buffer;
  Clipboard.GetTextBuf(buffer,size);
  if(StrLen(ptr)=0) then exit;
  for i:=MatrixGrid.Selection.Top to MatrixGrid.Selection.Bottom do
  begin
    for j:=MatrixGrid.Selection.Left to MatrixGrid.Selection.Right do
    begin
      s:=fSmartScanZ(ptr);
      try
        if(integer(ptr)>=(integer(buffer)+size)) then
          Raise EConvertError.Create('No more data in clipboard');
        StrToFloat(s);
      except
        on EConvertError do
        begin
          FreeMem(Buffer);
          exit;
        end;
      end;
      MatrixGrid.Cells[j,i]:=s;
    end;
  end;
  UpdateRegion(MatrixGrid.Selection.Left,MatrixGrid.Selection.Top,
    MatrixGrid.Selection.Right,MatrixGrid.Selection.Bottom);
  FreeMem(buffer);
end;

procedure TMTEditForm.EnableGrid;
begin
  if(OkCancel) then
    MatrixGrid.Options:=MatrixGrid.Options+[goEditing]
  else
    MatrixGrid.Options:=MatrixGrid.Options-[goEditing];
end;

// =======================================================================
// Обработчики событий
// =======================================================================

procedure TMTEditForm.OnCreate(Sender: TObject);
begin
  Matrix := nil;
  Vector := nil;
  OnChangeCells := nil;
  Recreatable:=False;
  CanLoad:=False;
  CanSave:=True;
  SourceChanged:=False;
  UndoBuffer[0]:=TCellRangeInfo.Create;
  UndoBuffer[1]:=TCellRangeInfo.Create;
  HoldIndex:=0;
end;

procedure TMTEditForm.ClearClick(Sender: TObject);
begin
  ClearGrid;
end;

procedure TMTEditForm.MatrixGridKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  col,row : integer;
  Value:double;
begin
  col:=MatrixGrid.Col;
  row:=MatrixGrid.Row;
  case Key of
    VK_F2: Key:=0;
    VK_RETURN:
    begin
      if(not (goEditing in MatrixGrid.Options)) then exit;
      if(MatrixGrid.EditorMode) then
      begin
        if((ssShift in Shift)and(ssCtrl in Shift)) then
        begin
          if(OnLeaveCell(Col,Row)) then
          begin
            Value:=StrToFloat(MatrixGrid.Cells[col,row]);
            // Восстанавливаем состояние ячейки
            UndoButtonClick(nil);
            MatrixGrid.EditorMode:=False;
            FillSelection(Value);
          end;
        end
        else
        begin
          if(OnLeaveCell(col,row)) then
          begin
            MatrixGrid.EditorMode:=False;
            if(MatrixGrid.Row<(MatrixGrid.RowCount-1)) then
              MatrixGrid.Row:=Row+1
            else
            begin
              if(MatrixGrid.Col<(MatrixGrid.ColCount-1)) then
              begin
                MatrixGrid.Row:=MatrixGrid.FixedRows;
                MatrixGrid.Col:=Col+1;
              end
              else
              begin
                MatrixGrid.Row:=MatrixGrid.FixedRows;
                MatrixGrid.Col:=MatrixGrid.FixedCols;
              end;
            end;
          end;
        end;
        Key:=0;
      end
      else
      begin
        MatrixGrid.EditorMode:=True;
      end;
    end;
    VK_ESCAPE:
    begin
      if(MatrixGrid.EditorMode) then
      begin
        if(Vector<>nil) then
          MatrixGrid.Cells[Col,Row]:=
            FloatToStr(Vector^.GetElement(Col))
        else
          MatrixGrid.Cells[Col,Row]:=
            FloatToStr(Matrix^.GetElement(Row-1,Col-1));
        MatrixGrid.EditorMode:=False;
      end;
    end;
    VK_UP:
    begin
      if(MatrixGrid.EditorMode) then
        if(not OnLeaveCell(col,row)) then
          Key:=0;
    end;
    VK_DOWN:
    begin
      if(MatrixGrid.EditorMode) then
        if(not OnLeaveCell(col,row)) then
          Key:=0;
    end;
    VK_LEFT:
    begin
      if(MatrixGrid.EditorMode or (ssShift in Shift)) then exit;
      Key:=VK_TAB;
      Include(Shift,ssShift);
      MatrixGridKeyDown(Self,Key,Shift);
      Key:=0;
    end;
    VK_RIGHT:
    begin
      if(MatrixGrid.EditorMode or (ssShift in Shift)) then exit;
      Key:=VK_TAB;
      Exclude(Shift,ssShift);
      MatrixGridKeyDown(Self,Key,Shift);
      Key:=0;
    end;
    VK_PRIOR:
    begin
      if(MatrixGrid.EditorMode) then
        if(not OnLeaveCell(col,row)) then
          Key:=0;
    end;
    VK_NEXT:
    begin
      if(MatrixGrid.EditorMode) then
        if(not OnLeaveCell(col,row)) then
          Key:=0;
    end;
    VK_TAB:
    begin
      if(ssShift in Shift) then
      begin
        if(col>MatrixGrid.FixedCols) then
        begin
          MatrixGrid.Col:=col-1;
        end
        else
        begin
          if(row>MatrixGrid.FixedRows) then
          begin
            MatrixGrid.Col:=MatrixGrid.ColCount-1;
            MatrixGrid.Row:=row-1;
          end
          else
          begin
            MatrixGrid.Col:=MatrixGrid.ColCount-1;
            MatrixGrid.Row:=MatrixGrid.RowCount-1;
          end;
        end;
      end
      else
      begin
        if(col<(MatrixGrid.ColCount-1)) then
        begin
          MatrixGrid.Col:=col+1;
        end
        else
        begin
          if(row<(MatrixGrid.RowCount-1)) then
          begin
            MatrixGrid.Col:=MatrixGrid.FixedCols;
            MatrixGrid.Row:=row+1;
          end
          else
          begin
            MatrixGrid.Col:=MatrixGrid.FixedCols;
            MatrixGrid.Row:=MatrixGrid.FixedRows;
          end;
        end;
      end;
    end;
    VK_DELETE:
    begin
      if(not (goEditing in MatrixGrid.Options)) then exit;
      if(MatrixGrid.EditorMode) then exit;
      if(ssShift in Shift) then
      begin
        CutToClipboard;
      end
      else
      begin
        ClearSelection;
      end;
    end;
    VK_INSERT:
    begin
      if(ssCtrl in Shift) then
      begin
        CopyToClipboard;
      end
      else
      begin
        if(not (goEditing in MatrixGrid.Options)) then exit;
        if(ssShift in Shift) then
          PasteFromClipboard;
      end;
    end;
  end;
end;

procedure TMTEditForm.ClearAllItemClick(Sender: TObject);
begin
  ClearGrid;
end;

procedure TMTEditForm.ClearRowItemClick(Sender: TObject);
begin
  ClearRow(MatrixGrid.Row);
end;

procedure TMTEditForm.ClearColumnItemClick(Sender: TObject);
begin
  ClearCol(MatrixGrid.Col);
end;

procedure TMTEditForm.SelectAllItemClick(Sender: TObject);
begin
  SelectAll;
end;

procedure TMTEditForm.DeselectItemClick(Sender: TObject);
begin
  RemoveSelection;
end;

procedure TMTEditForm.ClearSelectionItemClick(Sender: TObject);
begin
  ClearSelection;
end;

procedure TMTEditForm.FillAllItemClick(Sender: TObject);
var
  X : double;
begin
  if(DlgGetDouble(X,'','Заполнить значением:')) then
    FillGrid(X);
end;

procedure TMTEditForm.FillRowItemClick(Sender: TObject);
var
  X : double;
begin
  if(DlgGetDouble(X,'','Заполнить значением:')) then
    FillRow(MatrixGrid.Row,X);
end;

procedure TMTEditForm.FillColumnItemClick(Sender: TObject);
var
  X : double;
begin
  if(DlgGetDouble(X,'','Заполнить значением:')) then
    FillCol(MatrixGrid.Col,X);
end;

procedure TMTEditForm.FillSelectionItemClick(Sender: TObject);
var
  X : double;
begin
  if(DlgGetDouble(X,'','Заполнить значением:')) then
    FillSelection(X);
end;

procedure TMTEditForm.CopyItemClick(Sender: TObject);
begin
  CopyToClipboard;
end;

procedure TMTEditForm.PasteItemClick(Sender: TObject);
begin
  PasteFromClipboard;
end;

procedure TMTEditForm.OpenItemClick(Sender: TObject);
var
  TempMat : TStreamableMatrix;
  TempVec : TStreamableVector;
  S       : TFileStream;
label
  lCopyMatrix,lCopyVector;
begin
  if(not DataAttached) then
  begin
    Warning('TMTEditForm.Save: нет приемника данных.');
    exit;
  end;
  if(not OpenDialog.Execute) then exit;
  try
    try
      if(Matrix<>nil) then
      begin
        TempMat.Create(0,0);
        S:=TFileStream.Create(OpenDialog.FileName,fmOpenRead);
        if(not TempMat.LoadFromStreamAsText(TStream(S))) then
        begin
          S.Position:=0;
          if(not TempMat.LoadFromStream(TStream(S))) then
          begin
            Warning('Файл не '+OpenDialog.FileName
              +'содержит описания матрицы или поврежден');
            exit;
          end;
        end;
        if((Matrix^.nCols<>TempMat.nCols)or
           (Matrix^.nRows<>TempMat.nRows)) then
        begin
          if(Recreatable) then
          begin
            Matrix^.Recreate(TempMat.nRows,TempMat.nCols);
            goto lCopyMatrix;
          end
          else
            Warning('Текущая матрица имеет '+IntToStr(Matrix^.nRows)+
              ' строк, '+IntToStr(Matrix^.nCols)+' столбцов.'+#13+
              'Загружаемая матрица имеет другой размер.');
        end
        else
        begin
lCopyMatrix:
          Matrix^.CopyFrom(TempMat);
          SourceChanged:=False;
          UpdateControls(False);
          MainForm.CaptionChanged;
        end;
      end
      else // vector <> nil
      begin
        TempVec.Create(0);
        S:=TFileStream.Create(OpenDialog.FileName,fmOpenRead);
        if(not TempVec.LoadFromStreamAsText(TStream(S))) then
        begin
          S.Position:=0;
          if(not TempVec.LoadFromStream(TStream(S))) then
          begin
            Warning('Файл не '+OpenDialog.FileName
              +'содержит описания вектора или поврежден');
            exit;
          end;
        end;
        if(Vector^.nSize<>Tempvec.nSize) then
        begin
          if(Recreatable) then
          begin
            Vector^.Recreate(Tempvec.nSize);
            goto lCopyVector;
          end
          else
            Warning('Текущий вектор содержит '+IntToStr(Vector^.nSize)+
              ' элементов.'+#13+
              'Загружаемый вектор имеет другой размер.');
        end
        else
        begin
lCopyVector:
          Vector^.CopyFrom(TempVec);
          SourceChanged:=False;
          UpdateControls(False);
          MainForm.CaptionChanged;
        end;
      end;
    finally
      S.Destroy;
    end;
  except
    on EFOpenError do
    begin
      Error('Ошибка открытия файла "'+OpenDialog.FileName+'"');
      exit;
    end;
    on EReadError do
    begin
      Error('Ошибка чтения файла "'+OpenDialog.FileName+'"');
      exit;
    end;
  end;
end;

procedure TMTEditForm.SaveAsItemClick(Sender: TObject);
begin
  if(not DataAttached) then
  begin
    Warning('TMTEditForm.Save: нельзя сохранить несуществующие данные.');
    exit;
  end;
  if(SaveDialog.Execute) then
    SaveItemClick(Self);
end;

procedure TMTEditForm.SaveItemClick(Sender: TObject);
var
  S : TFileStream;
begin
  if(not DataAttached) then
  begin
    Warning('TMTEditForm.Save: нельзя сохранить несуществующие данные.');
    exit;
  end;
  if(SaveDialog.FileName='') then
  begin
    if(not SaveDialog.Execute) then exit;
  end;
  try
    S:=TFileStream.Create(SaveDialog.FileName,fmCreate);
    if(Matrix<>nil) then
    begin
      Matrix^.SaveToStream(TStream(S));
    end
    else
    begin
      Vector^.SaveToStream(TStream(S));
    end;
  except
    on EFCreateError do
    begin
      Error('Ошибка создания файла "'+SaveDialog.FileName+'"');
      S.Destroy;
      exit;
    end;
    on EWriteError do
    begin
      Error('Ошибка записи файла "'+SaveDialog.FileName+'"');
      S.Destroy;
      exit;
    end;
  end;
end;

procedure TMTEditForm.CutItemClick(Sender: TObject);
begin
  CutToClipboard;
end;

procedure TMTEditForm.NameItemClick(Sender: TObject);
var
  S,Def : string;
begin
  if(not DataAttached) then exit;
  if(Matrix<>nil) then
    Def:=Matrix^.GetName
  else
    Def:=Vector^.GetName;
  if(DlgGetString(S,Def,'Имя матрицы')) then
  begin
    if(Length(S)>0) then
    begin
      SourceChanged:=True;
      if(Matrix<>nil) then
        Matrix^.SetName(S)
      else
        Vector^.SetName(S);
      UpdateControls(False);
    end;
  end;
end;

procedure TMTEditForm.MatrixGridKeyPress(Sender: TObject; var Key: Char);
begin
  if(Key='*') then Key:='E';
  if((not((Key in ['0'..'9'])or(Key=#8)or(Key='.')or(Key=',')or(Key='e')
    or(Key='E')or(Key='+')or(Key='-')))or(Length(TEdit(Sender).Text)>25))
  then
  begin
    Key := #0;
  end
  else
  begin
    case Key of
    '.',',':
      begin
        if(DecimalPresent) then
        begin
          Key:=#0;
        end
        else
        begin
          if(SharedData.DecimalSymbol<>'') then
            Key:=SharedData.DecimalSymbol[1];
        end;
      end;
      'e','E':
      begin
        if(ExpPresent) then Key:=#0
      end;
    end;
  end;
end;

procedure TMTEditForm.FormDestroy(Sender: TObject);
begin
  UndoBuffer[0].Free;
  UndoBuffer[1].Free;
end;

procedure TMTEditForm.UndoButtonClick(Sender: TObject);
var
  f,s : integer;
begin
  case HoldIndex of
  0:
    begin
      f:=0;
      s:=1;
    end;
  1:
    begin
      f:=1;
      s:=0;
    end;
  else exit;
  end;
  UndoBuffer[s].Hold(MatrixGrid,UndoBuffer[f].Left,UndoBuffer[f].Top,
    UndoBuffer[f].Right,UndoBuffer[f].Bottom);
  UndoBuffer[f].Fetch(MatrixGrid);
  HoldIndex:=-1;
  UpdateRegion(UndoBuffer[f].Left,UndoBuffer[f].Top,
    UndoBuffer[f].Right,UndoBuffer[f].Bottom);
  HoldIndex:=s;
end;

// =======================================================================
// TCellRangeInfo
// =======================================================================

constructor TCellRangeInfo.Create;
begin
  Contens.Create(0,0);
end;

destructor TCellRangeInfo.Destroy;
begin
  Contens.Destroy;
  inherited;
end;

procedure TCellRangeInfo.Hold;
var
  i,j:integer;
begin
  try
    if((_L>_R)or(_T>_B)) then
      Raise EConvertError.Create('Неправильная область');
    L:=_L;
    T:=_T;
    if((_L=_R)and(_T=_B)) then
    begin
      if((Contens.nRows>0)or(Contens.nCols>0)) then
        Contens.Recreate(0,0);
      OneValue:=StrToFloat(Grid.Cells[L,T]);
      exit;
    end;
    Contens.Recreate(_B-_T+1,_R-_L+1);
    for i:=_T to _B do
      for j:=_L to _R do
        Contens.SetElement(i-T,j-L,StrToFloat(Grid.Cells[j,i]));
  except
    on EConvertError do
    begin
    end;
  end;
end;

procedure TCellRangeInfo.HoldFromMatrix;
begin
  if((_L>_R)or(_T>_B)) then
  begin
    L:=-1;
    T:=-1;
    Contens.Recreate(0,0);
  end;
  L:=_L;
  T:=_T;
  // Координаты в матрице на 1 меньше координат в сетке
  if((_L=_R)and(_T=_B)) then
  begin
    if((Contens.nRows>0)or(Contens.nCols>0)) then
      Contens.Recreate(0,0);
    OneValue:=Mat.GetElement(T-1,L-1);
  end
  else
  begin
    Contens.Recreate(_B-_T+1,_R-_L+1);
    Mat.GetSubMat(Contens,T-1,L-1);
  end;
end;

procedure TCellRangeInfo.HoldFromVector;
var
  j : integer;
begin
  if((_L>_R)or(_T>_B)) then
  begin
    L:=-1;
    T:=-1;
    Contens.Recreate(0,0);
  end;
  L:=_L;
  T:=_T;
  // Первая координата в векторе совпадает с координатой в сетке,
  // вторая координата на 1 меньше
  if((_L=_R)and(_T=_B)) then
  begin
    if((Contens.nRows>0)or(Contens.nCols>0)) then
      Contens.Recreate(0,0);
    OneValue:=Vec.GetElement(L);
  end
  else
  begin
    Contens.Recreate(_B-_T+1,_R-_L+1);
    for j:=0 to Contens.nCols-1 do
      Contens.SetElement(0,j,Vec.GetElement(j));
  end;
end;

function TCellRangeInfo.Fetch;
var
  i,j:integer;
  B,R:integer;
begin
  if((L<0)or(T<0)) then
  begin
    Result:=False;
    exit;
  end
  else
  begin
    if((Contens.nRows<1)or(Contens.nCols<1)) then
    begin
      Grid.Cells[L,T]:=FloatToStr(OneValue);
    end
    else
    begin
      B:=GetBottom;
      R:=GetRight;
      for i:=T to B do
        for j:=L to R do
          Grid.Cells[j,i]:=FloatToStr(Contens.Elements^[i-T][j-L]);
    end;
    Result:=True;
  end;
end;

function TCellRangeInfo.GetLeft;
begin
  Result:=L;
end;

function TCellRangeInfo.GetTop;
begin
  Result:=T;
end;

function TCellRangeInfo.GetRight;
begin
  if(Contens.nCols>0) then
    Result:=L+Contens.nCols-1
  else
    Result:=L;
end;

function TCellRangeInfo.GetBottom;
begin
  if(Contens.nRows>0) then
    Result:=T+Contens.nRows-1
  else
    Result:=T;
end;

procedure TMTEditForm.FormDeactivate(Sender: TObject);
begin
  if(MatrixGrid.EditorMode) then
  begin
    if(Vector<>nil) then
      MatrixGrid.Cells[MatrixGrid.Col,MatrixGrid.Row]:=
        FloatToStr(Vector^.GetElement(MatrixGrid.Col))
    else if Matrix <> nil then
      MatrixGrid.Cells[MatrixGrid.Col,MatrixGrid.Row]:=
   	    FloatToStr(Matrix^.GetElement(MatrixGrid.Row-1,MatrixGrid.Col-1));
    MatrixGrid.EditorMode:=False;
  end;
end;

// 15.03.00
procedure TMTEditForm.MatrixGridSetEditText(Sender: TObject; ACol,
  ARow: Integer; const Value: String);
begin
  UpdateRegion(ACol,ARow,ACol,ARow);
end;

end.

