unit DocumentCommands; interface uses Document; type TDocumentCommand = class(TObject) private FDocument : TDocument; protected procedure DoExecute; virtual; abstract; procedure DoRollback; virtual; // Used Self Encapsulate Field refactoring here. Now descendant commands // can access the document, even if they are declared in other units property Document : TDocument read FDocument write FDocument; public constructor Create(ADocument : TDocument); procedure Execute; procedure Rollback; // Reverse effect of Execute end; TDocumentCommandClass = class of TDocumentCommand; TOpenCommand = class(TDocumentCommand) private protected procedure DoExecute; override; public end; TCloseCommand = class(TDocumentCommand) private protected procedure DoExecute; override; public end; TSearchAndReplaceCommand = class(TDocumentCommand) private FOriginalDoc : TDocumentMemento; FFindText : string; FReplaceText : string; procedure ShowSearchReplaceDialog; protected procedure DoExecute; override; procedure DoRollback; override; public end; TPrettyPrintCommand = class(TDocumentCommand) private FOriginalDoc : TDocumentMemento; protected procedure DoExecute; override; procedure DoRollback; override; public destructor Destroy; override; end; implementation uses Dialogs, SysUtils, SearchReplaceForm; { TDocumentCommand } constructor TDocumentCommand.Create(ADocument : TDocument); begin inherited Create; FDocument := ADocument; end; procedure TDocumentCommand.DoRollback; begin end; procedure TDocumentCommand.Execute; begin if Assigned(FDocument) then begin DoExecute; end; end; procedure TDocumentCommand.Rollback; begin if Assigned(FDocument) then begin DoRollback; end; end; { TOpenCommand } procedure TOpenCommand.DoExecute; var FileName : string; begin if PromptForFileName(FileName, 'XML files (*.xml)|*.xml|CSV files (*.csv)|*.csv') then begin FDocument.OpenFile(FileName); end; end; { TCloseCommand } procedure TCloseCommand.DoExecute; begin FDocument.CloseFile; end; { TSearchAndReplaceCommand } procedure TSearchAndReplaceCommand.ShowSearchReplaceDialog; var ReplaceDialog : TSearchReplaceDlg; begin FFindText := ''; FReplaceText := ''; // Ask for the the find and replace text ReplaceDialog := TSearchReplaceDlg.Create(nil); try if ReplaceDialog.Execute then begin // Perform the search and replace FFindText := ReplaceDialog.FindText; FReplaceText := ReplaceDialog.ReplaceText; end; finally ReplaceDialog.Free; end; end; procedure TSearchAndReplaceCommand.DoExecute; begin // Just in case, make sure the current memento is freed FreeAndNil(FOriginalDoc); // Keep a copy of the document FOriginalDoc := FDocument.Memento; // Show the S&R dialog if no search has yet been done if FFindText = '' then begin ShowSearchReplaceDialog; end; // Only do sensible operations if FFindText <> '' then begin FDocument.SearchAndReplace(FFindText,FReplaceText); end; end; procedure TSearchAndReplaceCommand.DoRollback; begin if Assigned(FOriginalDoc) then begin FDocument.Memento := FOriginalDoc; end; end; { TPrettyPrintCommand } destructor TPrettyPrintCommand.Destroy; begin FOriginalDoc.Free; inherited; end; procedure TPrettyPrintCommand.DoExecute; begin // Just in case, make sure the current memento is freed FreeAndNil(FOriginalDoc); FOriginalDoc := FDocument.Memento; FDocument.PrettyPrint; end; procedure TPrettyPrintCommand.DoRollback; begin if Assigned(FOriginalDoc) then begin FDocument.Memento := FOriginalDoc; end; end; end.