unit ExtendedStdCtrls;
{$mode objfpc}{$H+}
interface
uses
Classes, Windows, SysUtils, Forms, Graphics, LCLType, Controls, Clipbrd, Grids, StdCtrls;
type
{ TStringGrid }
TStringGrid = class(Grids.TStringGrid)
private
type
UnDoRec = record
R: TRect;
D: String;
end;
private
var
UnDoStack: array of UnDoRec;
UnDoPosition: Integer;
private
function GetData(const R: TRect): String;
procedure AddExistState;
protected
procedure DoDeleteSelection;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
function GetEditText(ACol, ARow: Longint): string; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure ReDoState;
procedure UnDoState;
procedure ClearUnDoStack;
end;
{ TStringGrid }
implementation
{ TStringGrid }
function TStringGrid.GetData(const R: TRect): String;
var
i, j, k: LongInt;
begin
Result := '';
for i:=R.Top to R.Bottom do
begin
for j:=R.Left to R.Right do
begin
if Columns.Enabled and (j>=FirstGridColumn) then
begin
k := ColumnIndexFromGridColumn(j);
if not Columns[k].Visible then continue;
Result := Result + Cells[j,i];
end
else
Result := Result + Cells[j,i];
if j<>R.Right then Result := Result + #9;
end;
if (R.Top <> R.Bottom) or (R.Left <> R.Right) then
Result := Result + sLineBreak;
end;
end;
procedure TStringGrid.AddExistState;
var
u: Integer;
begin
if UnDoPosition < -1 then
begin
UnDoPosition := -1;
Exit;
end;
u := Length(UnDoStack);
if UnDoPosition >= u then UnDoPosition := u - 1;
Inc(UnDoPosition);
if (UnDoPosition > -1) and (UnDoPosition <= u) then
begin
u := UnDoPosition + 1;
end;
SetLength(UnDoStack, u);
UnDoStack[UnDoPosition].R := Selection;
UnDoStack[UnDoPosition].D := GetData(Selection);
end;
procedure TStringGrid.ReDoState;
var
TmpStr: String;
i: Integer;
R: TRect;
begin
i := Length(UnDoStack);
if i < 1 then Exit;
if UnDoPosition > (i - 2) then Exit;
Inc(UnDoPosition);
Selection := UnDoStack[UnDoPosition].R;
TmpStr := GetData(UnDoStack[UnDoPosition].R);
if (UnDoStack[UnDoPosition].R.Left = UnDoStack[UnDoPosition].R.Right) and
(UnDoStack[UnDoPosition].R.Top = UnDoStack[UnDoPosition].R.Bottom) then
Cells[UnDoStack[UnDoPosition].R.Left, UnDoStack[UnDoPosition].R.Top] := UnDoStack[UnDoPosition].D
else
SelectionSetText(UnDoStack[UnDoPosition].D);
UnDoStack[UnDoPosition].D := TmpStr;
R.Top := -1;
R.Bottom := -1;
R.Left := -1;
R.Right := -1;
Selection := R;
end;
procedure TStringGrid.UnDoState;
var
TmpStr: String;
i: Integer;
R: TRect;
begin
i := Length(UnDoStack);
if i < 1 then Exit;
if UnDoPosition < 0 then Exit;
Selection := UnDoStack[UnDoPosition].R;
TmpStr := GetData(UnDoStack[UnDoPosition].R);
if (UnDoStack[UnDoPosition].R.Left = UnDoStack[UnDoPosition].R.Right) and
(UnDoStack[UnDoPosition].R.Top = UnDoStack[UnDoPosition].R.Bottom) then
Cells[UnDoStack[UnDoPosition].R.Left, UnDoStack[UnDoPosition].R.Top] := UnDoStack[UnDoPosition].D
else
SelectionSetText(UnDoStack[UnDoPosition].D);
UnDoStack[UnDoPosition].D := TmpStr;
Dec(UnDoPosition);
R.Top := -1;
R.Bottom := -1;
R.Left := -1;
R.Right := -1;
Selection := R;
end;
procedure TStringGrid.ClearUnDoStack;
begin
SetLength(UnDoStack, 0);
UnDoPosition := -1;
end;
procedure TStringGrid.DoDeleteSelection;
begin
if EditingAllowed(Col) then Clean(Selection, []);
end;
procedure TStringGrid.KeyDown(var Key: Word; Shift: TShiftState);
var
R: TRect;
procedure SetSelectionFromClipboard;
var
L: TStringList;
P: PChar;
begin
if HasMultiSelection then Exit;
if EditingAllowed(Col) and Clipboard.HasFormat(CF_TEXT) then
begin
L := TStringList.Create;
try
L.Clear;
L.Text := Clipboard.AsText;
if L.Count > 0 then
begin
R := Selection;
R.Bottom := R.Bottom + L.Count - 1;
P := Pchar(L[0]);
if P<>nil then
while P^<>#0 do
begin
if P^ = #9 then Inc(R.Right);
Inc(P);
end;
if R.Bottom >= RowCount then R.Bottom := RowCount - 1;
if R.Right >= ColCount then R.Right := ColCount - 1;
Selection := R;
end;
finally
L.Free;
end;
end;
end;
begin
case Key of
VK_A: //Select all
if Shift = [ssModifier] then
begin
Key := 0;
R.Top := FixedRows;
R.Left := FixedCols;
R.Right := ColCount - 1;
R.Bottom := RowCount - 1;
Selection := R;
end;
VK_V: //Insert
if Shift = [ssModifier] then
begin
Key := 0;
SetSelectionFromClipboard;
AddExistState;
doPasteFromClipboard;
end;
VK_X: //Cut to clipboard
begin
if Shift = [ssModifier] then
begin
Key := 0;
AddExistState;
doCutToClipboard;
end;
end;
VK_Z: //Undo / Redo
begin
Key := 0;
if Shift = [ssModifier] then UnDoState;
if Shift = [ssAlt] then ReDoState;
end;
VK_INSERT: //Copy to clipboard / paste from clipboard
begin
Key := 0;
if Shift = [ssModifier] then DoCopyToClipboard;
if Shift = [ssShift] then
begin
SetSelectionFromClipboard;
AddExistState;
doPasteFromClipboard;
end;
end;
VK_DELETE: //Delete / Cut to clipboard
begin
Key := 0;
if Shift = [ssShift] then
begin
AddExistState;
doCutToClipboard;
end
else
begin
AddExistState;
DoDeleteSelection;
end;
end;
end;
inherited KeyDown(Key, Shift);
end;
function TStringGrid.GetEditText(ACol, ARow: Longint): string;
begin
AddExistState;
Result := inherited GetEditText(ACol, ARow);
end;
constructor TStringGrid.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ClearUnDoStack;
end;
destructor TStringGrid.Destroy;
begin
ClearUnDoStack;
inherited Destroy;
end;
{ TStringGrid }
end.