mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 10:18:05 +02:00
parent
78b3ddb3f6
commit
baabe96213
@ -37,6 +37,9 @@ type
|
||||
cedhtModified
|
||||
);
|
||||
|
||||
TUndoOpType = (uopNone = 0, uopAdd, uopChange, uopDel);
|
||||
TUndoCompState = (ucsNone = 0, ucsStartChange, ucsSaveChange);
|
||||
|
||||
TComponentEditorDesigner = class(TIDesigner)
|
||||
private
|
||||
FChangeStamp: int64;
|
||||
@ -48,6 +51,8 @@ type
|
||||
procedure AddHandler(HookType: TComponentEditorDesignerHookType; const Handler: TMethod);
|
||||
procedure RemoveHandler(HookType: TComponentEditorDesignerHookType; const Handler: TMethod);
|
||||
public
|
||||
FUndoState: TUndoCompState;
|
||||
|
||||
destructor Destroy; override;
|
||||
procedure Modified; override;
|
||||
function CopySelection: boolean; virtual; abstract;
|
||||
@ -63,6 +68,14 @@ type
|
||||
function InvokeComponentEditor(AComponent: TComponent;
|
||||
MenuIndex: integer): boolean; virtual; abstract;
|
||||
|
||||
function CanUndo: Boolean; virtual; abstract;
|
||||
function CanRedo: Boolean; virtual; abstract;
|
||||
function Undo: Boolean; virtual; abstract;
|
||||
function Redo: Boolean; virtual; abstract;
|
||||
function AddUndoAction(const AComp: TComponent; AOpType: TUndoOpType;
|
||||
IsSetNewId: boolean; AFieldName: string; const AOldVal, ANewVal: variant): boolean; virtual; abstract;
|
||||
function IsUndoNotLock: boolean; virtual; abstract;
|
||||
|
||||
procedure DrawDesignerItems(OnlyIfNeeded: boolean); virtual; abstract;
|
||||
function CreateUniqueComponentName(const AClassName: string
|
||||
): string; virtual; abstract;
|
||||
|
@ -1295,6 +1295,16 @@ var
|
||||
NewValue: string;
|
||||
OldExpanded: boolean;
|
||||
OldChangeStep: integer;
|
||||
RootDesigner: TIDesigner;
|
||||
CurrComp: TComponent;
|
||||
i, j, saveIndex, tmpInt: integer;
|
||||
newVal, tmpStr, newValAsInt: string;
|
||||
oldVal: array of string;
|
||||
isExcept, isIntValInStr: boolean;
|
||||
parRow, tmpRow: TOIPropertyGridRow;
|
||||
CompEditDsg: TComponentEditorDesigner;
|
||||
prpInfo: PPropInfo;
|
||||
|
||||
begin
|
||||
//debugln(['TOICustomPropertyGrid.SetRowValue A ',dbgs(FStates*[pgsChangingItemIndex,pgsApplyingValue]<>[]),' FItemIndex=',dbgs(FItemIndex),' CanEditRowValue=',CanEditRowValue]);
|
||||
if not CanEditRowValue(CheckFocus) or Rows[FItemIndex].IsReadOnly then exit;
|
||||
@ -1308,6 +1318,49 @@ begin
|
||||
//DebugLn(['TOICustomPropertyGrid.SetRowValue Old="',CurRow.Editor.GetVisualValue,'" New="',NewValue,'"']);
|
||||
if CurRow.Editor.GetVisualValue=NewValue then exit;
|
||||
|
||||
RootDesigner := FindRootDesigner(FCurrentEditorLookupRoot);
|
||||
if (RootDesigner is TComponentEditorDesigner) and
|
||||
not (RootDesigner as TComponentEditorDesigner).IsUndoNotLock then Exit;
|
||||
CompEditDsg := (RootDesigner as TComponentEditorDesigner);
|
||||
|
||||
|
||||
isExcept := false;
|
||||
saveIndex := FItemIndex;
|
||||
SetLength(oldVal, Selection.Count);
|
||||
for i := 0 to Selection.Count - 1 do
|
||||
begin
|
||||
CurrComp := CompEditDsg.Form.FindComponent(Selection.Items[i].GetNamePath);
|
||||
|
||||
while CurRow.Parent <> nil do
|
||||
CurRow := CurRow.Parent;
|
||||
|
||||
prpInfo := GetPropInfo(TObject(CurrComp), CurRow.Name);
|
||||
if not Assigned(prpInfo) then
|
||||
ShowMessage('error: propInfo = nil')
|
||||
else
|
||||
case prpInfo^.PropType^.Kind of
|
||||
tkInteger, tkInt64:
|
||||
oldVal[i] := IntToStr(GetOrdProp(TObject(CurrComp), prpInfo));
|
||||
tkChar, tkWChar, tkUChar:
|
||||
oldVal[i] := Char(GetOrdProp(TObject(CurrComp), prpInfo));
|
||||
tkEnumeration:
|
||||
oldVal[i] := GetEnumName(prpInfo^.PropType, GetOrdProp(TObject(CurrComp), CurRow.Name));
|
||||
tkFloat:
|
||||
oldVal[i] := FloatToStr(GetFloatProp(TObject(CurrComp), prpInfo));
|
||||
tkBool:
|
||||
oldVal[i] := BoolToStr(Boolean(GetOrdProp(TObject(CurrComp), prpInfo)), 'True', 'False');
|
||||
tkString, tkLString, tkAString, tkUString, tkWString:
|
||||
oldVal[i] := GetStrProp(TObject(CurrComp), prpInfo);
|
||||
tkSet:
|
||||
oldVal[i] := GetSetProp(TObject(CurrComp), CurRow.Name);
|
||||
tkVariant:
|
||||
oldVal[i] := GetVariantProp(TObject(CurrComp), prpInfo);
|
||||
end;
|
||||
end;
|
||||
FItemIndex := saveIndex;
|
||||
CurRow := Rows[FItemIndex];
|
||||
|
||||
|
||||
OldChangeStep:=fChangeStep;
|
||||
Include(FStates,pgsApplyingValue);
|
||||
try
|
||||
@ -1321,6 +1374,7 @@ begin
|
||||
except
|
||||
on E: Exception do begin
|
||||
MessageDlg(oisError, E.Message, mtError, [mbOk], 0);
|
||||
isExcept := true;
|
||||
end;
|
||||
end;
|
||||
{$ENDIF}
|
||||
@ -1329,6 +1383,33 @@ begin
|
||||
exit;
|
||||
end;
|
||||
|
||||
if not isExcept then
|
||||
begin
|
||||
if Assigned(prpInfo) and (prpInfo^.PropType^.Kind = tkSet) then
|
||||
newVal := GetSetProp(TObject(CurrComp), CurRow.Parent.Name)
|
||||
else if Assigned(prpInfo) and (prpInfo^.PropType^.Kind = tkInteger) and not TryStrToInt(NewValue, i) then
|
||||
newVal := IntToStr(CurRow.Editor.GetOrdValue)
|
||||
else
|
||||
newVal := NewValue;
|
||||
|
||||
for i := 0 to Selection.Count - 1 do
|
||||
begin
|
||||
CurRow := Rows[saveIndex];
|
||||
if CompEditDsg.Form.Name = Selection.Items[i].GetNamePath then
|
||||
CurrComp := CompEditDsg.Form
|
||||
else
|
||||
CurrComp := CompEditDsg.Form.FindComponent(Selection.Items[i].GetNamePath);
|
||||
if CurrComp <> nil then
|
||||
begin
|
||||
while CurRow.Parent <> nil do
|
||||
CurRow := CurRow.Parent;
|
||||
|
||||
CompEditDsg.AddUndoAction(CurrComp, uopChange, i = 0,
|
||||
curRow.Name, oldVal[i], newVal);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// set value in edit control
|
||||
SetCurrentEditValue(CurRow.Editor.GetVisualValue);
|
||||
|
||||
|
@ -42,6 +42,8 @@ uses
|
||||
FormEditingIntf, NonControlDesigner, DesignerProcs;
|
||||
|
||||
type
|
||||
TArrSize = array of array [0 .. 3] of integer;
|
||||
|
||||
TControlSelection = class;
|
||||
TGrabber = class;
|
||||
|
||||
@ -340,6 +342,7 @@ type
|
||||
procedure SetRubberbandType(const AValue: TRubberbandType);
|
||||
procedure SetSnapping(const AValue: boolean);
|
||||
procedure SetVisible(const AValue: Boolean);
|
||||
procedure GetCompSize(var arr: TArrSize);
|
||||
protected
|
||||
procedure AdjustGrabbers;
|
||||
procedure InvalidateGrabbers;
|
||||
@ -373,6 +376,8 @@ type
|
||||
procedure FindNearestTopGuideLine(var NearestInt: TNearestInt);
|
||||
procedure ImproveNearestInt(var NearestInt: TNearestInt; Candidate: integer);
|
||||
public
|
||||
arrOldSize, arrNewSize: TArrSize;
|
||||
|
||||
constructor Create; reintroduce;
|
||||
destructor Destroy; override;
|
||||
procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
|
||||
@ -408,8 +413,8 @@ type
|
||||
|
||||
// resizing, moving, aligning, mirroring, ...
|
||||
function IsResizing: boolean;
|
||||
procedure BeginResizing;
|
||||
procedure EndResizing(ApplyUserBounds: boolean);
|
||||
procedure BeginResizing(IsStartUndo: boolean);
|
||||
procedure EndResizing(ApplyUserBounds, ActionIsProcess: boolean);
|
||||
procedure SaveBounds;
|
||||
procedure UpdateBounds;
|
||||
procedure RestoreBounds;
|
||||
@ -512,6 +517,8 @@ var TheControlSelection: TControlSelection;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
ComponentEditors;
|
||||
|
||||
const
|
||||
GRAB_CURSOR: array[TGrabIndex] of TCursor = (
|
||||
@ -961,13 +968,61 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TControlSelection.BeginResizing;
|
||||
procedure TControlSelection.BeginResizing(IsStartUndo: boolean);
|
||||
var
|
||||
CompEditDesig: TComponentEditorDesigner;
|
||||
begin
|
||||
if FResizeLockCount=0 then BeginUpdate;
|
||||
inc(FResizeLockCount);
|
||||
|
||||
SetLength(arrOldSize, Count);
|
||||
GetCompSize(arrOldSize);
|
||||
|
||||
CompEditDesig := FindRootDesigner(Items[0].Persistent) as TComponentEditorDesigner;
|
||||
if (Count > 0) and (CompEditDesig.FUndoState = ucsNone) and IsStartUndo then
|
||||
CompEditDesig.FUndoState := ucsStartChange;
|
||||
end;
|
||||
|
||||
procedure TControlSelection.EndResizing(ApplyUserBounds: boolean);
|
||||
procedure TControlSelection.EndResizing(ApplyUserBounds, ActionIsProcess: boolean);
|
||||
var
|
||||
IsActionsBegin: boolean;
|
||||
|
||||
function SaveAct(AIndex: integer): boolean;
|
||||
var
|
||||
CurrComp: TComponent;
|
||||
RootDesigner: TIDesigner;
|
||||
begin
|
||||
Result := false;
|
||||
RootDesigner := FindRootDesigner(Items[AIndex].Persistent);
|
||||
if ((RootDesigner as TComponentEditorDesigner).FUndoState = ucsStartChange)
|
||||
and Items[AIndex].IsTComponent then
|
||||
begin
|
||||
if SelectionForm.Name = TComponent(Items[AIndex].Persistent).Name then
|
||||
CurrComp := SelectionForm
|
||||
else
|
||||
CurrComp := SelectionForm.FindComponent(TComponent(Items[AIndex].Persistent).Name);
|
||||
Result := (CurrComp <> nil) and (RootDesigner is TComponentEditorDesigner) and
|
||||
((arrOldSize[AIndex, 0] <> arrNewSize[AIndex, 0]) or
|
||||
(arrOldSize[AIndex, 1] <> arrNewSize[AIndex, 1]) or
|
||||
(arrOldSize[AIndex, 2] <> arrNewSize[AIndex, 2]) or
|
||||
(arrOldSize[AIndex, 3] <> arrNewSize[AIndex, 3]));
|
||||
if Result then
|
||||
with (RootDesigner as TComponentEditorDesigner) do
|
||||
begin
|
||||
AddUndoAction(CurrComp, uopChange, not(IsActionsBegin), 'Top',
|
||||
arrOldSize[AIndex, 0], arrNewSize[AIndex, 0]);
|
||||
AddUndoAction(CurrComp, uopChange, false, 'Left',
|
||||
arrOldSize[AIndex, 1], arrNewSize[AIndex, 1]);
|
||||
AddUndoAction(CurrComp, uopChange, false, 'Height',
|
||||
arrOldSize[AIndex, 2], arrNewSize[AIndex, 2]);
|
||||
AddUndoAction(CurrComp, uopChange, false, 'Width',
|
||||
arrOldSize[AIndex, 3], arrNewSize[AIndex, 3]);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
if FResizeLockCount<=0 then begin
|
||||
DebugLn('WARNING: TControlSelection.EndResizing FResizeLockCount=',IntToStr(FResizeLockCount));
|
||||
@ -975,6 +1030,20 @@ begin
|
||||
end;
|
||||
if FResizeLockCount=1 then
|
||||
if ApplyUserBounds then DoApplyUserBounds;
|
||||
|
||||
IsActionsBegin := false;
|
||||
SetLength(arrNewSize, Count);
|
||||
GetCompSize(arrNewSize);
|
||||
for i := 0 to Count - 1 do
|
||||
IsActionsBegin := SaveAct(i) or IsActionsBegin;
|
||||
if ActionIsProcess then
|
||||
begin
|
||||
if IsActionsBegin then
|
||||
(FindRootDesigner(Items[0].Persistent) as TComponentEditorDesigner).FUndoState := ucsSaveChange
|
||||
end
|
||||
else
|
||||
(FindRootDesigner(Items[0].Persistent) as TComponentEditorDesigner).FUndoState := ucsNone;
|
||||
|
||||
dec(FResizeLockCount);
|
||||
if FResizeLockCount>0 then exit;
|
||||
EndUpdate;
|
||||
@ -1949,6 +2018,20 @@ begin
|
||||
Exclude(FStates,cssVisible);
|
||||
end;
|
||||
|
||||
procedure TControlSelection.GetCompSize(var arr: TArrSize);
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i := 0 to Count - 1 do
|
||||
if Items[i].IsTComponent then
|
||||
begin
|
||||
arr[i, 0] := Items[i].Top;
|
||||
arr[i, 1] := Items[i].Left;
|
||||
arr[i, 2] := Items[i].Height;
|
||||
arr[i, 3] := Items[i].Width;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TControlSelection.GetItems(Index:integer):TSelectedControl;
|
||||
begin
|
||||
Result:=TSelectedControl(FControls[Index]);
|
||||
@ -2186,10 +2269,10 @@ begin
|
||||
if (NewLeft <> FLeft) or (NewTop <> FTop) then
|
||||
begin
|
||||
Result := True;
|
||||
BeginResizing;
|
||||
BeginResizing(false);
|
||||
FLeft := NewLeft;
|
||||
FTop := NewTop;
|
||||
EndResizing(True);
|
||||
EndResizing(True, False);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -2211,14 +2294,14 @@ begin
|
||||
if (NewLeft <> FLeft) or (NewTop <> FTop) then
|
||||
begin
|
||||
Result := True;
|
||||
BeginResizing;
|
||||
BeginResizing(True);
|
||||
FLeft := NewLeft;
|
||||
FTop := NewTop;
|
||||
{$IFDEF VerboseDesigner}
|
||||
DebugLn('[TControlSelection.MoveSelectionWithSnapping] B ',
|
||||
' Bounds='+dbgs(FLeft)+','+dbgs(FTop)+','+dbgs(FWidth)+','+dbgs(FHeight));
|
||||
{$ENDIF}
|
||||
EndResizing(True);
|
||||
EndResizing(True, True);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -2241,7 +2324,7 @@ begin
|
||||
if [gpLeft,gpRight] * GrabberPos = [] then dx:=0;
|
||||
if (dx=0) and (dy=0) then exit;
|
||||
|
||||
BeginResizing;
|
||||
BeginResizing(true);
|
||||
if gpLeft in GrabberPos then begin
|
||||
FLeft:=FLeft+dx;
|
||||
FWidth:=FWidth-dx;
|
||||
@ -2256,19 +2339,19 @@ begin
|
||||
else if gpBottom in GrabberPos then begin
|
||||
FHeight:=FHeight+dy;
|
||||
end;
|
||||
EndResizing(true);
|
||||
EndResizing(true, true);
|
||||
end;
|
||||
|
||||
procedure TControlSelection.SetBounds(NewLeft, NewTop,
|
||||
NewWidth, NewHeight: integer);
|
||||
begin
|
||||
if (Count=0) or (IsResizing) then exit;
|
||||
BeginResizing;
|
||||
BeginResizing(false);
|
||||
FLeft:=NewLeft;
|
||||
FTop:=NewTop;
|
||||
FWidth:=NewWidth;
|
||||
FHeight:=NewHeight;
|
||||
EndResizing(true);
|
||||
EndResizing(true, false);
|
||||
end;
|
||||
|
||||
function TControlSelection.GrabberAtPos(X,Y:integer):TGrabber;
|
||||
@ -2816,7 +2899,7 @@ begin
|
||||
if (Items[0].IsTopLvl)
|
||||
or ((HorizAlignment=csaNone) and (VertAlignment=csaNone)) then exit;
|
||||
|
||||
BeginResizing;
|
||||
BeginResizing(true);
|
||||
|
||||
// initializing
|
||||
ALeft:=Items[0].Left;
|
||||
@ -2937,7 +3020,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
EndResizing(false);
|
||||
EndResizing(false, false);
|
||||
end;
|
||||
|
||||
procedure TControlSelection.MirrorHorizontal;
|
||||
@ -2945,7 +3028,7 @@ var
|
||||
i, ALeft, ARight, Middle, NewLeft: integer;
|
||||
begin
|
||||
if (FControls.Count=0) or (Items[0].IsTopLvl) then exit;
|
||||
BeginResizing;
|
||||
BeginResizing(true);
|
||||
|
||||
// initializing
|
||||
ALeft:=Items[0].Left;
|
||||
@ -2966,7 +3049,7 @@ begin
|
||||
end;
|
||||
|
||||
UpdateBounds;
|
||||
EndResizing(false);
|
||||
EndResizing(false, false);
|
||||
end;
|
||||
|
||||
procedure TControlSelection.MirrorVertical;
|
||||
@ -2974,7 +3057,7 @@ var
|
||||
i, ATop, ABottom, Middle, NewTop: integer;
|
||||
begin
|
||||
if (FControls.Count=0) or (Items[0].IsTopLvl) then exit;
|
||||
BeginResizing;
|
||||
BeginResizing(true);
|
||||
|
||||
// initializing
|
||||
ATop:=Items[0].Top;
|
||||
@ -2995,7 +3078,7 @@ begin
|
||||
end;
|
||||
|
||||
UpdateBounds;
|
||||
EndResizing(false);
|
||||
EndResizing(false, false);
|
||||
end;
|
||||
|
||||
procedure TControlSelection.SizeComponents(
|
||||
@ -3004,7 +3087,7 @@ procedure TControlSelection.SizeComponents(
|
||||
var i: integer;
|
||||
begin
|
||||
if (FControls.Count=0) or (Items[0].IsTopLvl) then exit;
|
||||
BeginResizing;
|
||||
BeginResizing(true);
|
||||
|
||||
// initialize
|
||||
case HorizSizing of
|
||||
@ -3041,14 +3124,14 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
EndResizing(false);
|
||||
EndResizing(false, false);
|
||||
end;
|
||||
|
||||
procedure TControlSelection.ScaleComponents(Percent: integer);
|
||||
var i: integer;
|
||||
begin
|
||||
if (FControls.Count=0) then exit;
|
||||
BeginResizing;
|
||||
BeginResizing(true);
|
||||
|
||||
if Percent<1 then Percent:=1;
|
||||
if Percent>1000 then Percent:=1000;
|
||||
@ -3064,7 +3147,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
EndResizing(false);
|
||||
EndResizing(false, false);
|
||||
end;
|
||||
|
||||
function TControlSelection.CheckForLCLChanges(Update: boolean): boolean;
|
||||
|
@ -38,9 +38,10 @@ interface
|
||||
|
||||
uses
|
||||
// FCL + LCL
|
||||
Types, Classes, SysUtils, Math, LCLProc, LCLType, LResources, LCLIntf, LMessages,
|
||||
InterfaceBase, Forms, Controls, GraphType, Graphics, Dialogs, ExtCtrls, Menus,
|
||||
ClipBrd, TypInfo, contnrs,
|
||||
Types, Classes, Math, SysUtils, contnrs, variants, TypInfo,
|
||||
LCLProc, LCLType, LResources, LCLIntf, LMessages, InterfaceBase,
|
||||
Forms, Controls, GraphType, Graphics, Dialogs, ExtCtrls, Menus,
|
||||
ClipBrd,
|
||||
// IDEIntf
|
||||
IDEDialogs, PropEdits, PropEditUtils, ComponentEditors, MenuIntf, IDEImagesIntf,
|
||||
FormEditingIntf, ComponentReg,
|
||||
@ -81,6 +82,17 @@ type
|
||||
);
|
||||
TDesignerFlags = set of TDesignerFlag;
|
||||
|
||||
TUndoList = record
|
||||
obj: string;
|
||||
fieldName: string;
|
||||
propInfo: TPropInfo;
|
||||
oldVal, newVal: Variant;
|
||||
compName, parentName: TComponentName;
|
||||
opType: TUndoOpType;
|
||||
isValid: Boolean;
|
||||
id: int64;
|
||||
end;
|
||||
|
||||
{ TDesigner }
|
||||
|
||||
TDesigner = class(TComponentEditorDesigner)
|
||||
@ -117,6 +129,10 @@ type
|
||||
FShiftState: TShiftState;
|
||||
FTheFormEditor: TCustomFormEditor;
|
||||
FPopupMenuComponentEditor: TBaseComponentEditor;
|
||||
FUndoList: array of TUndoList;
|
||||
FUndoCurr: integer;
|
||||
FUndoLock: integer;
|
||||
FUndoActId: int64;
|
||||
|
||||
//hint stuff
|
||||
FHintTimer: TTimer;
|
||||
@ -197,6 +213,12 @@ type
|
||||
): boolean;
|
||||
function DoInsertFromStream(s: TStream; PasteParent: TWinControl;
|
||||
PasteFlags: TComponentPasteSelectionFlags): Boolean;
|
||||
|
||||
function DoUndo: Boolean;
|
||||
function DoRedo: Boolean;
|
||||
procedure SetNewVal(IsActUndo: boolean);
|
||||
procedure SetNextUndoActId;
|
||||
|
||||
procedure DoShowAnchorEditor;
|
||||
procedure DoShowTabOrderEditor;
|
||||
procedure DoShowChangeClassDialog;
|
||||
@ -276,6 +298,15 @@ type
|
||||
procedure DoProcessCommand(Sender: TObject; var Command: word;
|
||||
var Handled: boolean);
|
||||
|
||||
function CanUndo: Boolean; override;
|
||||
function CanRedo: Boolean; override;
|
||||
function Undo: Boolean; override;
|
||||
function Redo: Boolean; override;
|
||||
function AddUndoAction(const AComp: TComponent; AOpType: TUndoOpType;
|
||||
IsSetNewId: boolean; AFieldName: string; const AOldVal, ANewVal: variant): boolean; override;
|
||||
function IsUndoNotLock: boolean; override;
|
||||
procedure ClearUndoItem(AIndex: Integer);
|
||||
|
||||
function NonVisualComponentLeftTop(AComponent: TComponent): TPoint;
|
||||
function NonVisualComponentAtPos(X, Y: integer): TComponent;
|
||||
procedure MoveNonVisualComponentIntoForm(AComponent: TComponent);
|
||||
@ -594,6 +625,8 @@ end;
|
||||
|
||||
constructor TDesigner.Create(TheDesignerForm: TCustomForm;
|
||||
AControlSelection: TControlSelection);
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
inherited Create;
|
||||
//debugln(['TDesigner.Create Self=',dbgs(Pointer(Self)),' TheDesignerForm=',DbgSName(TheDesignerForm)]);
|
||||
@ -627,6 +660,14 @@ begin
|
||||
DeletingPersistent:=TList.Create;
|
||||
IgnoreDeletingPersistent:=TList.Create;
|
||||
FPopupMenuComponentEditor := nil;
|
||||
|
||||
SetLength(FUndoList, 64);
|
||||
for i := Low(FUndoList) to High(FUndoList) do
|
||||
ClearUndoItem(i);
|
||||
FUndoCurr := Low(FUndoList);
|
||||
FUndoLock := 0;
|
||||
FUndoState := ucsNone;
|
||||
SetNextUndoActId;
|
||||
end;
|
||||
|
||||
procedure TDesigner.PrepareFreeDesigner(AFreeComponent: boolean);
|
||||
@ -1192,6 +1233,8 @@ begin
|
||||
// finish adding component
|
||||
NotifyPersistentAdded(NewComponent);
|
||||
Modified;
|
||||
// add action in undo list
|
||||
AddUndoAction(NewComponent, uopAdd, i = 0, 'Name', '', NewComponent.Name);
|
||||
end;
|
||||
|
||||
if NewSelection.Count>0 then
|
||||
@ -1209,6 +1252,154 @@ begin
|
||||
Result:=true;
|
||||
end;
|
||||
|
||||
function TDesigner.DoUndo: Boolean;
|
||||
var currId: int64;
|
||||
begin
|
||||
repeat
|
||||
Result := CanUndo;
|
||||
if not Result then Exit;
|
||||
Dec(FUndoCurr);
|
||||
currId := FUndoList[FUndoCurr].id;
|
||||
SetNewVal(true);
|
||||
until currId <> FUndoList[FUndoCurr - 1].id;
|
||||
end;
|
||||
|
||||
function TDesigner.DoRedo: Boolean;
|
||||
var currId: int64;
|
||||
begin
|
||||
repeat
|
||||
Result := CanRedo;
|
||||
if not Result then Exit;
|
||||
SetNewVal(false);
|
||||
currId := FUndoList[FUndoCurr].id;
|
||||
Inc(FUndoCurr);
|
||||
until currId <> FUndoList[FUndoCurr].id;
|
||||
end;
|
||||
|
||||
procedure TDesigner.SetNewVal(IsActUndo: boolean);
|
||||
|
||||
procedure SetPropVal(AVal: variant);
|
||||
var
|
||||
tmpStr, str: string;
|
||||
tmpCompName: TComponentName;
|
||||
tmpObj: TObject;
|
||||
tmpInt: integer;
|
||||
begin
|
||||
tmpCompName := FUndoList[FUndoCurr].compName;
|
||||
if FUndoList[FUndoCurr].fieldName = 'Name' then
|
||||
begin
|
||||
if IsActUndo then
|
||||
tmpCompName := FUndoList[FUndoCurr].newVal
|
||||
else
|
||||
tmpCompName := FUndoList[FUndoCurr].oldVal;
|
||||
end;
|
||||
|
||||
if FForm.Name <> tmpCompName then
|
||||
tmpObj := TObject(FForm.FindComponent(tmpCompName))
|
||||
else
|
||||
tmpObj := TObject(FForm);
|
||||
|
||||
if VarIsError(AVal) or VarIsEmpty(AVal) or VarIsNull(AVal) then
|
||||
ShowMessage('error: invalid var type');
|
||||
tmpStr := VarToStr(AVal);
|
||||
|
||||
with FUndoList[FUndoCurr] do
|
||||
case propInfo.propType^.Kind of
|
||||
tkInteger, tkInt64:
|
||||
begin
|
||||
if (propInfo.propType^.Name = 'TColor') or
|
||||
(propInfo.propType^.Name = 'TGraphicsColor') then
|
||||
SetOrdProp(tmpObj, fieldName, StringToColor(tmpStr))
|
||||
else if propInfo.propType^.Name = 'TCursor' then
|
||||
SetOrdProp(tmpObj, fieldName, StringToCursor(tmpStr))
|
||||
else
|
||||
SetOrdProp(tmpObj, fieldName, StrToInt(tmpStr));
|
||||
end;
|
||||
tkChar, tkWChar, tkUChar:
|
||||
begin
|
||||
if Length(tmpStr) = 1 then
|
||||
SetOrdProp(tmpObj, FUndoList[FUndoCurr].fieldName, Ord(tmpStr[1]))
|
||||
else if (tmpStr[1] = '#') then
|
||||
begin
|
||||
str := Copy(tmpStr, 2, Length(tmpStr) - 1);
|
||||
if TryStrToInt(str, tmpInt) and (tmpInt >= 0) and (tmpInt <= High(Byte)) then
|
||||
SetOrdProp(tmpObj, FUndoList[FUndoCurr].fieldName, tmpInt);
|
||||
end;
|
||||
end;
|
||||
tkEnumeration:
|
||||
SetEnumProp(tmpObj, FUndoList[FUndoCurr].fieldName, tmpStr);
|
||||
tkFloat:
|
||||
SetFloatProp(tmpObj, fieldName, StrToFloat(tmpStr));
|
||||
tkBool:
|
||||
SetOrdProp(tmpObj, FUndoList[FUndoCurr].fieldName, Integer(StrToBool(tmpStr)));
|
||||
tkString, tkLString, tkAString, tkUString, tkWString:
|
||||
SetStrProp(tmpObj, fieldName, tmpStr);
|
||||
tkSet:
|
||||
SetSetProp(tmpObj, FUndoList[FUndoCurr].fieldName, tmpStr);
|
||||
tkVariant:
|
||||
SetVariantProp(tmpObj, fieldName, AVal);
|
||||
else
|
||||
ShowMessage(Format('error: unknown TTypeKind(%d)', [Integer(propInfo.propType^.Kind)]));
|
||||
end;
|
||||
PropertyEditorHook.Modified(tmpObj);
|
||||
end;
|
||||
|
||||
var
|
||||
CurTextCompStream: TMemoryStream;
|
||||
SaveControlSelection: TControlSelection;
|
||||
begin
|
||||
if (IsActUndo and (FUndoList[FUndoCurr].opType in [uopAdd])) or
|
||||
(not IsActUndo and (FUndoList[FUndoCurr].opType in [uopDel])) then
|
||||
begin
|
||||
Inc(FUndoLock);
|
||||
SaveControlSelection := TControlSelection.Create;
|
||||
try
|
||||
SaveControlSelection.Assign(ControlSelection);
|
||||
ControlSelection.Clear;
|
||||
ControlSelection.Add(FForm.FindComponent(FUndoList[FUndoCurr].compName));
|
||||
DeleteSelection;
|
||||
finally
|
||||
ControlSelection.Assign(SaveControlSelection);
|
||||
SaveControlSelection.Free;
|
||||
Dec(FUndoLock);
|
||||
end;
|
||||
end;
|
||||
|
||||
if (IsActUndo and (FUndoList[FUndoCurr].opType in [uopDel])) or
|
||||
(not IsActUndo and (FUndoList[FUndoCurr].opType in [uopAdd])) then
|
||||
begin
|
||||
CurTextCompStream := TMemoryStream.Create;
|
||||
try
|
||||
Inc(FUndoLock);
|
||||
CurTextCompStream.Write(FUndoList[FUndoCurr].obj[1], Length(FUndoList[FUndoCurr].obj));
|
||||
CurTextCompStream.Position := 0;
|
||||
DoInsertFromStream(CurTextCompStream,
|
||||
TWinControl(FForm.FindChildControl(FUndoList[FUndoCurr].parentName)), []);
|
||||
finally
|
||||
CurTextCompStream.Free;
|
||||
Dec(FUndoLock);
|
||||
end;
|
||||
end;
|
||||
|
||||
if FUndoList[FUndoCurr].opType = uopChange then
|
||||
begin
|
||||
Inc(FUndoLock);
|
||||
if IsActUndo then
|
||||
SetPropVal(FUndoList[FUndoCurr].oldVal)
|
||||
else
|
||||
SetPropVal(FUndoList[FUndoCurr].newVal);
|
||||
Dec(FUndoLock);
|
||||
end;
|
||||
|
||||
PropertyEditorHook.RefreshPropertyValues;
|
||||
end;
|
||||
|
||||
procedure TDesigner.SetNextUndoActId;
|
||||
begin
|
||||
Randomize;
|
||||
FUndoActId := Random(High(Int64));
|
||||
end;
|
||||
|
||||
procedure TDesigner.DoShowAnchorEditor;
|
||||
begin
|
||||
if Assigned(FOnShowAnchorEditor) then
|
||||
@ -1413,6 +1604,123 @@ begin
|
||||
Handled := True;
|
||||
end;
|
||||
|
||||
function TDesigner.CanUndo: Boolean;
|
||||
begin
|
||||
Result := Assigned(Form) and (FUndoCurr > Low(FUndoList)) and
|
||||
(FUndoList[FUndoCurr - 1].isValid) and (FUndoList[FUndoCurr - 1].opType <> uopNone);
|
||||
end;
|
||||
|
||||
function TDesigner.CanRedo: Boolean;
|
||||
begin
|
||||
Result := Assigned(Form) and (FUndoCurr <= High(FUndoList)) and
|
||||
(FUndoList[FUndoCurr].isValid) and (FUndoList[FUndoCurr].opType <> uopNone);
|
||||
end;
|
||||
|
||||
function TDesigner.Undo: Boolean;
|
||||
begin
|
||||
Result := DoUndo;
|
||||
end;
|
||||
|
||||
function TDesigner.Redo: Boolean;
|
||||
begin
|
||||
Result := DoRedo;
|
||||
end;
|
||||
|
||||
function TDesigner.AddUndoAction(const AComp: TComponent; AOpType: TUndoOpType;
|
||||
IsSetNewId: boolean; AFieldName: string; const AOldVal, ANewVal: variant): boolean;
|
||||
|
||||
procedure ShiftUndoList;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i := Low(FUndoList) + 1 to High(FUndoList) do
|
||||
FUndoList[i - 1] := FUndoList[i];
|
||||
Dec(FUndoCurr);
|
||||
end;
|
||||
|
||||
var
|
||||
i: integer;
|
||||
SaveControlSelection: TControlSelection;
|
||||
AStream: TStringStream;
|
||||
begin
|
||||
Result := (FUndoLock = 0);
|
||||
if not(Result) then Exit;
|
||||
Inc(FUndoLock);
|
||||
|
||||
if FUndoCurr > High(FUndoList) then
|
||||
ShiftUndoList;
|
||||
|
||||
i := FUndoCurr;
|
||||
while (i <= High(FUndoList)) do
|
||||
begin
|
||||
ClearUndoItem(i);
|
||||
Inc(i);
|
||||
end;
|
||||
|
||||
if IsSetNewId then
|
||||
SetNextUndoActId;
|
||||
|
||||
if (AOpType in [uopAdd, uopDel]) and (FForm.Name <> AComp.Name) then
|
||||
begin
|
||||
SaveControlSelection := TControlSelection.Create;
|
||||
try
|
||||
SaveControlSelection.Assign(ControlSelection);
|
||||
AStream := TStringStream.Create('');
|
||||
try
|
||||
ControlSelection.Clear;
|
||||
ControlSelection.Add(AComp);
|
||||
CopySelectionToStream(AStream);
|
||||
FUndoList[FUndoCurr].obj := AStream.DataString;
|
||||
finally
|
||||
AStream.Free;
|
||||
end;
|
||||
finally
|
||||
ControlSelection.Assign(SaveControlSelection);
|
||||
SaveControlSelection.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
with FUndoList[FUndoCurr] do
|
||||
begin
|
||||
oldVal := AOldVal;
|
||||
newVal := ANewVal;
|
||||
fieldName := AFieldName;
|
||||
compName := AComp.Name;
|
||||
if not(AComp.Equals(Form)) and AComp.HasParent then
|
||||
parentName := AComp.GetParentComponent.Name
|
||||
else
|
||||
parentName := '';
|
||||
opType := AOpType;
|
||||
isValid := true;
|
||||
id := FUndoActId;
|
||||
propInfo := GetPropInfo(TObject(AComp), AFieldName)^;
|
||||
end;
|
||||
Inc(FUndoCurr);
|
||||
Dec(FUndoLock);
|
||||
end;
|
||||
|
||||
function TDesigner.IsUndoNotLock: boolean;
|
||||
begin
|
||||
Result := FUndoLock = 0;
|
||||
end;
|
||||
|
||||
procedure TDesigner.ClearUndoItem(AIndex: Integer);
|
||||
begin
|
||||
if (AIndex < 0) or (AIndex >= Length(FUndoList)) then Exit;
|
||||
with FUndoList[AIndex] do
|
||||
begin
|
||||
obj := '';
|
||||
fieldName := '';
|
||||
VarClear(oldVal);
|
||||
VarClear(newVal);
|
||||
compName := '';
|
||||
parentName := '';
|
||||
opType := uopNone;
|
||||
isValid := false;
|
||||
id := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDesigner.NonVisualComponentLeftTop(AComponent: TComponent): TPoint;
|
||||
var
|
||||
ParentForm: TPoint;
|
||||
@ -1976,6 +2284,7 @@ var
|
||||
DebugLn('NEW COMPONENT ADDED: Form.ComponentCount=',DbgS(Form.ComponentCount),
|
||||
' NewComponent.Owner.Name=',NewComponent.Owner.Name);
|
||||
{$ENDIF}
|
||||
AddUndoAction(NewComponent, uopAdd, true, 'Name', '', NewComponent.Name);
|
||||
end;
|
||||
|
||||
procedure RubberbandSelect;
|
||||
@ -2047,6 +2356,7 @@ var
|
||||
|
||||
var
|
||||
Handled: Boolean;
|
||||
i, j: Integer;
|
||||
begin
|
||||
FHintTimer.Enabled := False;
|
||||
FHintWindow.Visible := False;
|
||||
@ -2103,6 +2413,26 @@ begin
|
||||
begin
|
||||
if SelectedCompClass = nil then
|
||||
begin
|
||||
if (FUndoState = ucsSaveChange) and (FUndoCurr > 4) then
|
||||
begin
|
||||
j := FUndoCurr - ControlSelection.Count * 4;
|
||||
for i := 0 to ControlSelection.Count - 1 do
|
||||
begin
|
||||
if (FUndoList[j].fieldName = 'Top')
|
||||
and (FUndoList[j + 1].fieldName = 'Left')
|
||||
and (FUndoList[j + 2].fieldName = 'Height')
|
||||
and (FUndoList[j + 3].fieldName = 'Width') then
|
||||
begin
|
||||
FUndoList[j].newVal := ControlSelection.Items[i].Top;
|
||||
FUndoList[j + 1].newVal := ControlSelection.Items[i].Left;
|
||||
FUndoList[j + 2].newVal := ControlSelection.Items[i].Height;
|
||||
FUndoList[j + 3].newVal := ControlSelection.Items[i].Width;
|
||||
end;
|
||||
j := j + 4;
|
||||
end;
|
||||
end;
|
||||
FUndoState := ucsNone;
|
||||
|
||||
// layout mode (selection, moving and resizing)
|
||||
if not (dfHasSized in FFlags) then
|
||||
begin
|
||||
@ -2471,7 +2801,14 @@ begin
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
for i := 0 to ControlSelection.Count - 1 do
|
||||
begin
|
||||
if not ControlSelection[i].IsTComponent then continue;
|
||||
AComponent := TComponent(ControlSelection[i].Persistent);
|
||||
AddUndoAction(AComponent, uopDel, i = 0, 'Name', AComponent.Name, '');
|
||||
end;
|
||||
|
||||
// mark selected components for deletion
|
||||
for i:=0 to ControlSelection.Count-1 do
|
||||
MarkPersistentForDeletion(ControlSelection[i].Persistent);
|
||||
|
23
ide/main.pp
23
ide/main.pp
@ -3783,7 +3783,7 @@ var
|
||||
Editable: Boolean;
|
||||
SelAvail: Boolean;
|
||||
SelEditable: Boolean;
|
||||
SrcEditorActive: Boolean;
|
||||
SrcEditorActive, DsgEditorActive: Boolean;
|
||||
ActiveDesigner: TComponentEditorDesigner;
|
||||
begin
|
||||
GetCurrentUnit(ASrcEdit, AnUnitInfo);
|
||||
@ -3791,14 +3791,15 @@ begin
|
||||
SelAvail := Assigned(ASrcEdit) and ASrcEdit.SelectionAvailable;
|
||||
SelEditable := Editable and SelAvail;
|
||||
SrcEditorActive := DisplayState = dsSource;
|
||||
DsgEditorActive := DisplayState = dsForm;
|
||||
ActiveDesigner := GetActiveDesignerSkipMainBar;
|
||||
with MainIDEBar do
|
||||
begin
|
||||
if Assigned(ActiveDesigner) then
|
||||
begin
|
||||
// activate them when Designer start to support Undo/Redo
|
||||
itmEditUndo.Enabled := False;
|
||||
itmEditRedo.Enabled := False;
|
||||
itmEditUndo.Enabled := DsgEditorActive and ActiveDesigner.CanUndo; {and not ActiveDesigner.ReadOnly}
|
||||
itmEditRedo.Enabled := DsgEditorActive and ActiveDesigner.CanRedo; {and not ActiveDesigner.ReadOnly}
|
||||
itmEditCut.Enabled := ActiveDesigner.CanCopy;
|
||||
itmEditCopy.Enabled := itmEditCut.Enabled;
|
||||
itmEditPaste.Enabled := ActiveDesigner.CanPaste;
|
||||
@ -13644,13 +13645,25 @@ begin
|
||||
end;
|
||||
|
||||
procedure TMainIDE.mnuEditRedoClicked(Sender: TObject);
|
||||
var
|
||||
ActiveDesigner: TComponentEditorDesigner;
|
||||
begin
|
||||
DoSourceEditorCommand(ecRedo);
|
||||
ActiveDesigner := GetActiveDesignerSkipMainBar;
|
||||
if Assigned(ActiveDesigner) then
|
||||
ActiveDesigner.Redo
|
||||
else
|
||||
DoSourceEditorCommand(ecRedo);
|
||||
end;
|
||||
|
||||
procedure TMainIDE.mnuEditUndoClicked(Sender: TObject);
|
||||
var
|
||||
ActiveDesigner: TComponentEditorDesigner;
|
||||
begin
|
||||
DoSourceEditorCommand(ecUndo);
|
||||
ActiveDesigner := GetActiveDesignerSkipMainBar;
|
||||
if Assigned(ActiveDesigner) then
|
||||
ActiveDesigner.Undo
|
||||
else
|
||||
DoSourceEditorCommand(ecUndo);
|
||||
end;
|
||||
|
||||
procedure TMainIDE.mnuEditIndentBlockClicked(Sender: TObject);
|
||||
|
Loading…
Reference in New Issue
Block a user