View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0001927 | Double Commander | Logic | public | 2017-10-06 17:39 | 2020-11-30 07:44 |
Reporter | CryHam | Assigned To | cordylus | ||
Priority | high | Severity | minor | Reproducibility | always |
Status | closed | Resolution | open | ||
Projection | none | ETA | none | ||
Product Version | 1.0.0 (trunk) | Product Build | trunk@7564 | ||
Summary | 0001927: Compare by Contents - Not Implemented from Search result and archives. | ||||
Description | The command: File - Compare by Contents, does not work from: - search results (after find with Alt-f7 and used Feed to listbox) - archives (e.g. browsing inside a zip) It shows a messagebox "Not Implemented." Basically it only works when both files are in a regular view of directory. In code as: msgWarning(rsMsgNotImplemented); in file umaincommands.pas, procedure TMainCommands.cm_CompareContents Not sure what extra code search result needs. But for archives I guess a standard way is to extract to a random temporary file (in system TEMP) and use pass its name to the diff tool. Would be great to implement it. | ||||
Steps To Reproduce | (are in description) | ||||
Tags | No tags attached. | ||||
Attached Files | compare-search-result.diff (1,456 bytes)
Index: src/umaincommands.pas =================================================================== --- src/umaincommands.pas (revision 7831) +++ src/umaincommands.pas (working copy) @@ -2674,7 +2674,7 @@ begin with frmMain do begin - // For now work only for filesystem. + // For now work only for filesystem and search result. // Later use temporary file system for other file sources. try @@ -2689,8 +2689,9 @@ end else begin - // For now work only for filesystem. - if not (ActiveFrame.FileSource.IsClass(TFileSystemFileSource)) then + // For now work only for filesystem and search result. + if not (ActiveFrame.FileSource.IsClass(TFileSystemFileSource)) + and not (ActiveFrame.FileSource.IsClass(TSearchResultFileSource)) then begin msgWarning(rsMsgNotImplemented); Exit; @@ -2719,8 +2720,9 @@ if NotActiveSelectedFiles.Count = 1 then begin - // For now work only for filesystem. - if not (NotActiveFrame.FileSource.IsClass(TFileSystemFileSource)) then + // For now work only for filesystem and search result. + if not (NotActiveFrame.FileSource.IsClass(TFileSystemFileSource)) + and not (NotActiveFrame.FileSource.IsClass(TSearchResultFileSource)) then begin msgWarning(rsMsgNotImplemented); Exit; bug1927.patch (38,295 bytes)
Index: src/fdiffer.pas =================================================================== --- src/fdiffer.pas (revision 7907) +++ src/fdiffer.pas (working copy) @@ -29,7 +29,7 @@ Classes, SysUtils, FileUtil, Forms, Controls, Dialogs, Menus, ComCtrls, ActnList, ExtCtrls, EditBtn, Buttons, SynEdit, uSynDiffControls, uPariterControls, uDiffOND, uFormCommands, uHotkeyManager, uOSForms, - uBinaryDiffViewer; + uBinaryDiffViewer, uShowForm; type @@ -214,6 +214,7 @@ EncodingList: TStringList; ScrollLock: LongInt; FShowIdentical: Boolean; + FWaitData: TWaitData; FCommands: TFormCommands; procedure ShowIdentical; procedure Clear(bLeft, bRight: Boolean); @@ -248,7 +249,7 @@ procedure cm_SaveRight(const Params: array of string); end; -procedure ShowDiffer(const FileNameLeft, FileNameRight: String); +procedure ShowDiffer(const FileNameLeft, FileNameRight: String; WaitData: TWaitData = nil); implementation @@ -261,10 +262,11 @@ const HotkeysCategory = 'Differ'; -procedure ShowDiffer(const FileNameLeft, FileNameRight: String); +procedure ShowDiffer(const FileNameLeft, FileNameRight: String; WaitData: TWaitData = nil); begin with TfrmDiffer.Create(Application) do begin + FWaitData := WaitData; edtFileNameLeft.Text:= FileNameLeft; edtFileNameRight.Text:= FileNameRight; FShowIdentical:= actAutoCompare.Checked; @@ -984,6 +986,7 @@ BinaryViewerRight.SecondViewer:= nil; HotMan.UnRegister(Self); inherited Destroy; + if Assigned(FWaitData) then FWaitData.Done; end; procedure TfrmDiffer.BuildHashList(bLeft, bRight: Boolean); Index: src/feditor.pas =================================================================== --- src/feditor.pas (revision 7907) +++ src/feditor.pas (working copy) @@ -161,7 +161,7 @@ sEncodingIn, sEncodingOut, sOriginalText: String; - FWaitData: TEditorWaitData; + FWaitData: TWaitData; FCommands: TFormCommands; property Commands: TFormCommands read FCommands implements IFormCommands; @@ -222,8 +222,7 @@ procedure cm_EditRplc(const {%H-}Params:array of string); end; - procedure ShowEditor(WaitData: TEditorWaitData); - function ShowEditor(const sFileName: String): TfrmEditor; + procedure ShowEditor(const sFileName: String; WaitData: TWaitData = nil); implementation @@ -234,29 +233,24 @@ uLng, uShowMsg, fEditSearch, uGlobs, fOptions, DCClassesUtf8, uOSUtils, uConvEncoding, fOptionsToolsEditor, uDCUtils, uClipboard; -function ShowEditor(const sFileName: String): TfrmEditor; +procedure ShowEditor(const sFileName: String; WaitData: TWaitData = nil); +var + Editor: TfrmEditor; begin - Result := TfrmEditor.Create(Application); + Editor := TfrmEditor.Create(Application); + Editor.FWaitData := WaitData; if sFileName = '' then - Result.cm_FileNew(['']) + Editor.cm_FileNew(['']) else begin - if not Result.OpenFile(sFileName) then + if not Editor.OpenFile(sFileName) then Exit; end; - Result.ShowOnTop; + Editor.ShowOnTop; end; -procedure ShowEditor(WaitData: TEditorWaitData); -var - Editor: TfrmEditor; -begin - Editor:= ShowEditor(WaitData.FileName); - Editor.FWaitData:= WaitData -end; - procedure TfrmEditor.FormCreate(Sender: TObject); var i:Integer; @@ -536,7 +530,7 @@ begin HotMan.UnRegister(Self); inherited Destroy; - if Assigned(FWaitData) then EditDone(FWaitData); + if Assigned(FWaitData) then FWaitData.Done; end; Index: src/ffileexecuteyourself.pas =================================================================== --- src/ffileexecuteyourself.pas (revision 7907) +++ src/ffileexecuteyourself.pas (working copy) @@ -46,13 +46,13 @@ procedure FormCreate(Sender: TObject); private FFileSource: IFileSource; - FWaitData: TEditorWaitData; + FWaitData: TWaitData; public constructor Create(TheOwner: TComponent; aFileSource: IFileSource; const FileName, FromPath: String); reintroduce; destructor Destroy; override; end; - procedure ShowFileEditExternal(aWaitData: TEditorWaitData); + procedure ShowFileEditExternal(const FileName, FromPath: string; aWaitData: TWaitData); function ShowFileExecuteYourSelf(aFileView: TFileView; aFile: TFile; bWithAll: Boolean): Boolean; implementation @@ -62,13 +62,10 @@ uses LCLProc, uTempFileSystemFileSource, uFileSourceOperation, uShellExecute, DCOSUtils; -procedure ShowFileEditExternal(aWaitData: TEditorWaitData); -var - APath: String; +procedure ShowFileEditExternal(const FileName, FromPath: string; aWaitData: TWaitData); begin - APath:= aWaitData.TargetFileSource.CurrentAddress + aWaitData.TargetPath; // Create wait window - with TfrmFileExecuteYourSelf.Create(Application, nil, ExtractFileName(aWaitData.FileName), APath) do + with TfrmFileExecuteYourSelf.Create(Application, nil, FileName, FromPath) do begin FWaitData:= aWaitData; // Show wait window @@ -155,7 +152,7 @@ // Delete the temporary file source and all files inside. FFileSource:= nil; inherited Destroy; - if Assigned(FWaitData) then EditDone(FWaitData); + if Assigned(FWaitData) then FWaitData.Done; end; end. Index: src/umaincommands.pas =================================================================== --- src/umaincommands.pas (revision 7907) +++ src/umaincommands.pas (working copy) @@ -505,31 +505,13 @@ procedure TMainCommands.OnEditCopyOutStateChanged(Operation: TFileSourceOperation; State: TFileSourceOperationState); var - aFile: TFile; WaitData: TEditorWaitData; - aFileSource: ITempFileSystemFileSource; - aCopyOutOperation: TFileSourceCopyOperation; begin if (State = fsosStopped) and (Operation.Result = fsorFinished) then begin - aCopyOutOperation := Operation as TFileSourceCopyOperation; - aFileSource := aCopyOutOperation.TargetFileSource as ITempFileSystemFileSource; - try - aFile := aCopyOutOperation.SourceFiles[0]; - - WaitData:= TEditorWaitData.Create; - + WaitData := TEditorWaitData.Create(Operation as TFileSourceCopyOperation); try - WaitData.TargetPath:= aCopyOutOperation.SourceFiles.Path; - - ChangeFileListRoot(aFileSource.FileSystemRoot, aCopyOutOperation.SourceFiles); - - WaitData.FileName:= aFile.FullPath; - WaitData.SourceFileSource:= aFileSource; - WaitData.FileTime:= mbFileAge(WaitData.FileName); - WaitData.TargetFileSource:= aCopyOutOperation.FileSource as IFileSource; - ShowEditorByGlob(WaitData); except WaitData.Free; @@ -1737,9 +1719,6 @@ ActiveFile: TFile = nil; AllFiles: TFiles = nil; SelectedFiles: TFiles = nil; - TempFiles: TFiles = nil; - TempFileSource: ITempFileSystemFileSource = nil; - Operation: TFileSourceOperation; aFileSource: IFileSource; sCmd: string = ''; sParams: string = ''; @@ -1767,44 +1746,8 @@ // Default to using the file source directly. aFileSource := ActiveFrame.FileSource; - // If files are links to local files - if (fspLinksToLocalFiles in ActiveFrame.FileSource.Properties) then - begin - for I := 0 to SelectedFiles.Count - 1 do - begin - aFile := SelectedFiles[I]; - ActiveFrame.FileSource.GetLocalName(aFile); - end; - end - // If files not directly accessible copy them to temp file source. - else if not (fspDirectAccess in ActiveFrame.FileSource.Properties) then - begin - if not (fsoCopyOut in ActiveFrame.FileSource.GetOperationsTypes) then - begin - msgWarning(rsMsgErrNotSupported); - Exit; - end; - - TempFiles := SelectedFiles.Clone; - - TempFileSource := TTempFileSystemFileSource.GetFileSource; - - Operation := ActiveFrame.FileSource.CreateCopyOutOperation( - TempFileSource, - TempFiles, - TempFileSource.FileSystemRoot); - - if Assigned(Operation) then - begin - Operation.AddStateChangedListener([fsosStopped], @OnCopyOutStateChanged); - OperationsManager.AddOperation(Operation); - end - else - begin - msgWarning(rsMsgErrNotSupported); - end; + if PrepareData(ActiveFrame.FileSource, SelectedFiles, @OnCopyOutStateChanged) <> pdrSynchronous then Exit; - end; try aFile := SelectedFiles[0]; @@ -1875,7 +1818,6 @@ FreeAndNil(sl); FreeAndNil(AllFiles); FreeAndNil(SelectedFiles); - FreeAndNil(TempFiles); FreeAndNil(ActiveFile); end; end; @@ -2026,10 +1968,7 @@ var i: Integer; aFile: TFile; - TempFiles: TFiles; SelectedFiles: TFiles = nil; - Operation: TFileSourceOperation; - TempFileSource: ITempFileSystemFileSource = nil; sCmd: string = ''; sParams: string = ''; sStartPath: string = ''; @@ -2037,44 +1976,9 @@ with frmMain do try SelectedFiles := ActiveFrame.CloneSelectedOrActiveFiles; - // If files are links to local files - if (fspLinksToLocalFiles in ActiveFrame.FileSource.Properties) then - begin - for I := 0 to SelectedFiles.Count - 1 do - begin - aFile := SelectedFiles[I]; - ActiveFrame.FileSource.GetLocalName(aFile); - end; - end - // If files not directly accessible copy them to temp file source. - else if not (fspDirectAccess in ActiveFrame.FileSource.Properties) then - begin - if not (fsoCopyOut in ActiveFrame.FileSource.GetOperationsTypes) then - begin - msgWarning(rsMsgErrNotSupported); - Exit; - end; - TempFiles := SelectedFiles.Clone; - - TempFileSource := TTempFileSystemFileSource.GetFileSource; - - Operation := ActiveFrame.FileSource.CreateCopyOutOperation( - TempFileSource, - TempFiles, - TempFileSource.FileSystemRoot); - - if Assigned(Operation) then - begin - Operation.AddStateChangedListener([fsosStopped], @OnEditCopyOutStateChanged); - OperationsManager.AddOperation(Operation); - end - else - begin - msgWarning(rsMsgErrNotSupported); - end; + if PrepareData(ActiveFrame.FileSource, SelectedFiles, @OnEditCopyOutStateChanged) <> pdrSynchronous then Exit; - end; try for i := 0 to SelectedFiles.Count - 1 do @@ -2655,15 +2559,18 @@ procedure TMainCommands.cm_CompareContents(const Params: array of string); var - FilesToCompare: TStringList = nil; - DirsToCompare: TStringList = nil; + FilesNumber: Integer = 0; + DirsNumber: Integer = 0; - procedure AddItem(const aFile: TFile); + procedure CountFiles(const Files: TFiles); + var I: Integer; begin - if not aFile.IsDirectory then - FilesToCompare.Add(aFile.FullPath) - else - DirsToCompare.Add(aFile.FullPath); + if Assigned(Files) then + for I := 0 to Files.Count - 1 do + if Files[I].IsDirectory then + Inc(DirsNumber) + else + Inc(FilesNumber); end; var @@ -2671,31 +2578,19 @@ Param: String; ActiveSelectedFiles: TFiles = nil; NotActiveSelectedFiles: TFiles = nil; + FirstFileSource: IFileSource = nil; + FirstFileSourceFiles: TFiles = nil; + SecondFileSource: IFileSource = nil; + SecondFileSourceFiles: TFiles = nil; begin with frmMain do begin - // For now works only for file source with direct access. - // Later use temporary file system for other file sources. - - try - FilesToCompare := TStringList.Create; - DirsToCompare := TStringList.Create; Param := GetDefaultParam(Params); if Param = 'dir' then - begin - DirsToCompare.Add(FrameLeft.CurrentPath); - DirsToCompare.Add(FrameRight.CurrentPath); - end + ShowDifferByGlob(FrameLeft.CurrentPath, FrameRight.CurrentPath) else begin - // For now works only for file source with direct access. - if not (fspDirectAccess in ActiveFrame.FileSource.Properties) then - begin - msgWarning(rsMsgNotImplemented); - Exit; - end; - try ActiveSelectedFiles := ActiveFrame.CloneSelectedOrActiveFiles; @@ -2719,31 +2614,31 @@ if NotActiveSelectedFiles.Count = 1 then begin - // For now works only for file source with direct access. - if not (fspDirectAccess in NotActiveFrame.FileSource.Properties) then - begin - msgWarning(rsMsgNotImplemented); - Exit; - end; { compare single selected files in both panels } case gResultingFramePositionAfterCompare of rfpacActiveOnLeft: - begin; - AddItem(ActiveSelectedFiles[0]); - AddItem(NotActiveSelectedFiles[0]); + begin + FirstFileSource := ActiveFrame.FileSource; + FirstFileSourceFiles := ActiveSelectedFiles; + SecondFileSource := NotActiveFrame.FileSource; + SecondFileSourceFiles := NotActiveSelectedFiles; end; rfpacLeftOnLeft: begin if ActiveFrame = FrameLeft then begin - AddItem(ActiveSelectedFiles[0]); - AddItem(NotActiveSelectedFiles[0]); + FirstFileSource := ActiveFrame.FileSource; + FirstFileSourceFiles := ActiveSelectedFiles; + SecondFileSource := NotActiveFrame.FileSource; + SecondFileSourceFiles := NotActiveSelectedFiles; end else begin - AddItem(NotActiveSelectedFiles[0]); - AddItem(ActiveSelectedFiles[0]); + FirstFileSource := NotActiveFrame.FileSource; + FirstFileSourceFiles := NotActiveSelectedFiles; + SecondFileSource := ActiveFrame.FileSource; + SecondFileSourceFiles := ActiveSelectedFiles; end; end; end; @@ -2760,45 +2655,39 @@ begin { compare all selected files in active frame } - for I := 0 to ActiveSelectedFiles.Count - 1 do - AddItem(ActiveSelectedFiles[I]); + FirstFileSource := ActiveFrame.FileSource; + FirstFileSourceFiles := ActiveSelectedFiles; end; + CountFiles(FirstFileSourceFiles); + CountFiles(SecondFileSourceFiles); + + if ((FilesNumber > 0) and (DirsNumber > 0)) + or ((FilesNumber = 1) or (DirsNumber = 1)) then + // Either files or directories must be selected and more than one. + MsgWarning(rsMsgInvalidSelection) + else if (FilesNumber = 0) and (DirsNumber = 0) then + MsgWarning(rsMsgNoFilesSelected) + else if (FilesNumber > 2) and not gExternalTools[etDiffer].Enabled then + MsgWarning(rsMsgTooManyFilesSelected) + else if (DirsNumber > 0) and not gExternalTools[etDiffer].Enabled then + MsgWarning(rsMsgNotImplemented) + else + begin + if not Assigned(SecondFileSource) then + PrepareToolData(FirstFileSource, FirstFileSourceFiles, + @ShowDifferByGlobList) + else + PrepareToolData(FirstFileSource, FirstFileSourceFiles, + SecondFileSource, SecondFileSourceFiles, + @ShowDifferByGlobList); + end; + finally FreeAndNil(ActiveSelectedFiles); FreeAndNil(NotActiveSelectedFiles); end; end; - - if ((FilesToCompare.Count > 0) and (DirsToCompare.Count > 0)) - or ((FilesToCompare.Count = 1) or (DirsToCompare.Count = 1)) then - begin - // Either files or directories must be selected and more than one. - MsgWarning(rsMsgInvalidSelection); - end - else if FilesToCompare.Count > 0 then - begin - if gExternalTools[etDiffer].Enabled then - RunExtDiffer(FilesToCompare) - else if FilesToCompare.Count = 2 then - ShowDiffer(FilesToCompare.Strings[0], FilesToCompare.Strings[1]) - else - MsgWarning(rsMsgTooManyFilesSelected); - end - else if DirsToCompare.Count > 0 then - begin - if gExternalTools[etDiffer].Enabled then - RunExtDiffer(DirsToCompare) - else - MsgWarning(rsMsgNotImplemented); - end - else - msgWarning(rsMsgNoFilesSelected); - - finally - FreeAndNil(FilesToCompare); - FreeAndNil(DirsToCompare); - end; end; end; Index: src/ushowform.pas =================================================================== --- src/ushowform.pas (revision 7907) +++ src/ushowform.pas (working copy) @@ -19,27 +19,57 @@ interface uses - Classes, DCBasicTypes, uFileSource, uFileSourceOperation; + Classes, DCBasicTypes, uFileSource, uFileSourceOperation, uFile, + uFileSourceCopyOperation; type + { TWaitData } + + TWaitData = class + public + procedure ShowWaitForm; virtual; abstract; + procedure Done; virtual; abstract; + end; + { TEditorWaitData } - TEditorWaitData = class + TEditorWaitData = class(TWaitData) public - FileName: String; + Files: TFiles; + function GetFileList: TStringList; + protected + FileTimes: array of TFileTime; TargetPath: String; - FileTime: TFileTime; SourceFileSource: IFileSource; TargetFileSource: IFileSource; + function GetRelativeFileName(const FullPath: string): string; + function GetRelativeFileNames: string; + function GetFromPath: string; public + constructor Create(aCopyOutOperation: TFileSourceCopyOperation); destructor Destroy; override; + procedure ShowWaitForm; override; + procedure Done; override; protected procedure OnCopyInStateChanged(Operation: TFileSourceOperation; State: TFileSourceOperationState); end; -procedure EditDone(WaitData: TEditorWaitData); + TToolDataPreparedProc = procedure(const FileList: TStringList; WaitData: TWaitData); + + TPrepareDataResult = (pdrFailed, pdrSynchronous, pdrAsynchronous); + +function PrepareData(FileSource: IFileSource; var SelectedFiles: TFiles; + FunctionToCall: TFileSourceOperationStateChangedNotify): TPrepareDataResult; + +procedure PrepareToolData(FileSource: IFileSource; var SelectedFiles: TFiles; + FunctionToCall: TToolDataPreparedProc); overload; + +procedure PrepareToolData(FileSource1: IFileSource; var SelectedFiles1: TFiles; + FileSource2: IFileSource; var SelectedFiles2: TFiles; + FunctionToCall: TToolDataPreparedProc); overload; + procedure RunExtDiffer(CompareList: TStringList); procedure ShowEditorByGlob(const sFileName: String); @@ -46,6 +76,7 @@ procedure ShowEditorByGlob(WaitData: TEditorWaitData); overload; procedure ShowDifferByGlob(const LeftName, RightName: String); +procedure ShowDifferByGlobList(const CompareList: TStringList; WaitData: TWaitData); procedure ShowViewerByGlob(const sFileName: String); procedure ShowViewerByGlobList(const FilesToView: TStringList; @@ -57,12 +88,24 @@ SysUtils, Process, DCProcessUtf8, Dialogs, LCLIntf, uShellExecute, uGlobs, uOSUtils, fEditor, fViewer, uDCUtils, uTempFileSystemFileSource, uLng, fDiffer, uDebug, DCOSUtils, uShowMsg, - uFile, uFileSourceCopyOperation, uFileSystemFileSource, + DCStrUtils, uFileSourceProperty, uFileSourceOperationOptions, uOperationsManager, uFileSourceOperationTypes, uMultiArchiveFileSource, fFileExecuteYourSelf; type + { TWaitDataDouble } + + TWaitDataDouble = class(TWaitData) + private + FWaitData1, FWaitData2: TEditorWaitData; + public + constructor Create(WaitData1: TEditorWaitData; WaitData2: TEditorWaitData); + procedure ShowWaitForm; override; + procedure Done; override; + destructor Destroy; override; + end; + { TViewerWaitThread } TViewerWaitThread = class(TThread) @@ -76,11 +119,13 @@ destructor Destroy; override; end; - { TEditorWaitThread } + { TExtToolWaitThread } - TEditorWaitThread = class(TThread) + TExtToolWaitThread = class(TThread) private - FWaitData: TEditorWaitData; + FExternalTool: TExternalTool; + FFileList: TStringList; + FWaitData: TWaitData; private procedure RunEditDone; procedure ShowWaitForm; @@ -87,7 +132,10 @@ protected procedure Execute; override; public - constructor Create(WaitData: TEditorWaitData); + constructor Create(ExternalTool: TExternalTool; + const FileList: TStringList; + WaitData: TWaitData); + destructor Destroy; override; end; procedure RunExtTool(const ExtTool: TExternalToolOptions; sFileName: String); @@ -147,11 +195,21 @@ end; procedure ShowEditorByGlob(WaitData: TEditorWaitData); +var + FileList: TStringList; begin if gExternalTools[etEditor].Enabled then - with TEditorWaitThread.Create(WaitData) do Start + begin + FileList := TStringList.Create; + try + FileList.Add(WaitData.Files[0].FullPath); + with TExtToolWaitThread.Create(etEditor, FileList, WaitData) do Start; + finally + FileList.Free + end; + end else begin - ShowEditor(WaitData); + ShowEditor(WaitData.Files[0].FullPath, WaitData); end; end; @@ -201,6 +259,19 @@ ShowDiffer(LeftName, RightName); end; +procedure ShowDifferByGlobList(const CompareList: TStringList; WaitData: TWaitData); +begin + if gExternalTools[etDiffer].Enabled then + begin + if Assigned(WaitData) then + with TExtToolWaitThread.Create(etDiffer, CompareList, WaitData) do Start + else + RunExtDiffer(CompareList); + end + else + ShowDiffer(CompareList[0], CompareList[1], WaitData); +end; + procedure ShowViewerByGlobList(const FilesToView : TStringList; const aFileSource: IFileSource); var @@ -227,51 +298,147 @@ ShowViewer(FilesToView, aFileSource); end; -procedure EditDone(WaitData: TEditorWaitData); +procedure TEditorWaitData.Done; var - Files: TFiles; + I: Integer; Operation: TFileSourceCopyOperation; + DoNotFreeYet: Boolean = False; begin - with WaitData do try - // File was modified - if mbFileAge(FileName) <> FileTime then + for I := Files.Count - 1 downto 0 do + if (mbFileAge(Files[I].FullPath) = FileTimes[I]) or + not msgYesNo(Format(rsMsgCopyBackward, [GetRelativeFileName(Files[I].FullPath)]) + LineEnding + LineEnding + GetFromPath) then + Files.Delete(I); + + // Files were modified + if Files.Count > 0 then begin - if not msgYesNo(Format(rsMsgCopyBackward, [ExtractFileName(FileName)])) then Exit; if (fsoCopyIn in TargetFileSource.GetOperationsTypes) and (not (TargetFileSource is TMultiArchiveFileSource)) then begin - Files:= TFiles.Create(SourceFileSource.GetRootDir); - Files.Add(TFileSystemFileSource.CreateFileFromFile(FileName)); Operation:= TargetFileSource.CreateCopyInOperation(SourceFileSource, Files, TargetPath) as TFileSourceCopyOperation; - // Copy file back + // Copy files back if Assigned(Operation) then begin Operation.AddStateChangedListener([fsosStopped], @OnCopyInStateChanged); Operation.FileExistsOption:= fsoofeOverwrite; OperationsManager.AddOperation(Operation); - WaitData:= nil; // Will be free in operation + DoNotFreeYet:= True; // Will be free in operation end; end - else if msgYesNo(rsMsgCouldNotCopyBackward + LineEnding + FileName) then + else if msgYesNo(rsMsgCouldNotCopyBackward + LineEnding + GetRelativeFileNames) then begin (SourceFileSource as ITempFileSystemFileSource).DeleteOnDestroy:= False; end; end; finally - WaitData.Free; + if not DoNotFreeYet then + Free; end; end; +{ TWaitDataDouble } + +constructor TWaitDataDouble.Create(WaitData1: TEditorWaitData; WaitData2: TEditorWaitData); +begin + FWaitData1 := WaitData1; + FWaitData2 := WaitData2; +end; + +procedure TWaitDataDouble.ShowWaitForm; +begin + try + FWaitData1.ShowWaitForm; + finally + FWaitData2.ShowWaitForm; + end; +end; + +procedure TWaitDataDouble.Done; +begin + try + if Assigned(FWaitData1) then + FWaitData1.Done; + finally + FWaitData1 := nil; + try + if Assigned(FWaitData2) then + FWaitData2.Done; + finally + FWaitData2 := nil; + Free; + end; + end; +end; + +destructor TWaitDataDouble.Destroy; +begin + inherited Destroy; + if Assigned(FWaitData1) then + FWaitData1.Free; + if Assigned(FWaitData2) then + FWaitData2.Free; +end; + { TEditorWaitData } +constructor TEditorWaitData.Create(aCopyOutOperation: TFileSourceCopyOperation); +var + I: Integer; + aFileSource: ITempFileSystemFileSource; +begin + aFileSource := aCopyOutOperation.TargetFileSource as ITempFileSystemFileSource; + TargetPath := aCopyOutOperation.SourceFiles.Path; + Files := aCopyOutOperation.SourceFiles.Clone; + ChangeFileListRoot(aFileSource.FileSystemRoot, Files); + SetLength(FileTimes, Files.Count); + for I := 0 to Files.Count - 1 do + FileTimes[I] := mbFileAge(Files[I].FullPath); + SourceFileSource := aFileSource; + TargetFileSource := aCopyOutOperation.FileSource as IFileSource; +end; + destructor TEditorWaitData.Destroy; begin inherited Destroy; + Files.Free; SourceFileSource:= nil; TargetFileSource:= nil; end; +function TEditorWaitData.GetRelativeFileName(const FullPath: string): string; +begin + Result := ExtractDirLevel(IncludeTrailingPathDelimiter(Files.Path), FullPath); +end; + +function TEditorWaitData.GetRelativeFileNames: string; +var + I: Integer; +begin + Result := GetRelativeFileName(Files[0].FullPath); + for I := 1 to Files.Count - 1 do + Result := Result + ', ' + GetRelativeFileName(Files[I].FullPath); +end; + +function TEditorWaitData.GetFromPath: string; +begin + Result := TargetFileSource.CurrentAddress + TargetPath; +end; + +procedure TEditorWaitData.ShowWaitForm; +begin + ShowFileEditExternal(GetRelativeFileNames, GetFromPath, Self); +end; + +function TEditorWaitData.GetFileList: TStringList; +var + I: Integer; +begin + Result := TStringList.Create; + for I := 0 to Files.Count - 1 do + Result.Add(Files[I].FullPath); +end; + procedure TEditorWaitData.OnCopyInStateChanged(Operation: TFileSourceOperation; State: TFileSourceOperationState); var @@ -296,27 +463,29 @@ end; end; -{ TEditorWaitThread } +{ TExtToolWaitThread } -procedure TEditorWaitThread.RunEditDone; +procedure TExtToolWaitThread.RunEditDone; begin - EditDone(FWaitData); + FWaitData.Done; end; -procedure TEditorWaitThread.ShowWaitForm; +procedure TExtToolWaitThread.ShowWaitForm; begin - ShowFileEditExternal(FWaitData); + FWaitData.ShowWaitForm; end; -procedure TEditorWaitThread.Execute; +procedure TExtToolWaitThread.Execute; var + I: Integer; StartTime: QWord; Process : TProcessUTF8; sCmd, sSecureEmptyStr: String; begin + try Process := TProcessUTF8.Create(nil); - - with gExternalTools[etEditor] do + try + with gExternalTools[FExternalTool] do begin sCmd := ReplaceEnvVars(Path); // TProcess arguments must be enclosed with double quotes and not escaped. @@ -325,7 +494,8 @@ sCmd := QuoteStr(sCmd); if Parameters <> EmptyStr then sCmd := sCmd + ' ' + Parameters; - sCmd := sCmd + ' ' + QuoteStr(FWaitData.FileName); + for I := 0 to FFileList.Count - 1 do + sCmd := sCmd + ' ' + QuoteStr(FFileList[I]); sSecureEmptyStr := EmptyStr; // Let's play safe and don't let EmptyStr being passed as "VAR" parameter of "FormatTerminal" FormatTerminal(sCmd, sSecureEmptyStr, False); end @@ -334,7 +504,8 @@ sCmd := '"' + sCmd + '"'; if Parameters <> EmptyStr then sCmd := sCmd + ' ' + Parameters; - sCmd := sCmd + ' "' + FWaitData.FileName + '"'; + for I := 0 to FFileList.Count - 1 do + sCmd := sCmd + ' "' + FFileList[I] + '"'; end; end; @@ -342,7 +513,6 @@ Process.Options := [poWaitOnExit]; StartTime:= GetTickCount64; Process.Execute; - Process.Free; // If an editor closes within gEditWaitTime amount of milliseconds, // assume that it's a multiple document editor and show dialog where @@ -354,17 +524,38 @@ else begin Synchronize(@RunEditDone); end; + + finally + Process.Free; + end; + except + FWaitData.Free; + end; end; -constructor TEditorWaitThread.Create(WaitData: TEditorWaitData); +constructor TExtToolWaitThread.Create(ExternalTool: TExternalTool; + const FileList: TStringList; + WaitData: TWaitData); begin inherited Create(True); FreeOnTerminate := True; + FExternalTool := ExternalTool; + + FFileList := TStringList.Create; + // Make a copy of list elements. + FFileList.Assign(FileList); + FWaitData := WaitData; end; +destructor TExtToolWaitThread.Destroy; +begin + FFileList.Free; + inherited Destroy; +end; + { TViewerWaitThread } constructor TViewerWaitThread.Create(const FilesToView: TStringList; const aFileSource: IFileSource); @@ -425,4 +616,306 @@ Process.Free; end; +{ PrepareData } + +function PrepareData(FileSource: IFileSource; var SelectedFiles: TFiles; + FunctionToCall: TFileSourceOperationStateChangedNotify): TPrepareDataResult; +var + aFile: TFile; + I: Integer; + TempFiles: TFiles = nil; + TempFileSource: ITempFileSystemFileSource = nil; + Operation: TFileSourceOperation; +begin + // If files are links to local files + if (fspLinksToLocalFiles in FileSource.Properties) then + begin + for I := 0 to SelectedFiles.Count - 1 do + begin + aFile := SelectedFiles[I]; + FileSource.GetLocalName(aFile); + end; + end + // If files not directly accessible copy them to temp file source. + else if not (fspDirectAccess in FileSource.Properties) then + begin + if not (fsoCopyOut in FileSource.GetOperationsTypes) then + begin + msgWarning(rsMsgErrNotSupported); + Exit(pdrFailed); + end; + + TempFileSource := TTempFileSystemFileSource.GetFileSource; + + TempFiles := SelectedFiles.Clone; + try + Operation := FileSource.CreateCopyOutOperation( + TempFileSource, + TempFiles, + TempFileSource.FileSystemRoot); + finally + TempFiles.Free; + end; + + if not Assigned(Operation) then + begin + msgWarning(rsMsgErrNotSupported); + Exit(pdrFailed); + end; + + Operation.AddStateChangedListener([fsosStopped], FunctionToCall); + + OperationsManager.AddOperation(Operation); + + Exit(pdrAsynchronous); + end; + Exit(pdrSynchronous); +end; + +{ TToolDataPreparator } + +type + TToolDataPreparator = class + protected + FFunc: TToolDataPreparedProc; + FCallOnFail: Boolean; + procedure OnCopyOutStateChanged(Operation: TFileSourceOperation; + State: TFileSourceOperationState); + public + constructor Create(FunctionToCall: TToolDataPreparedProc; CallOnFail: Boolean = False); + procedure Prepare(FileSource: IFileSource; var SelectedFiles: TFiles); + end; + +constructor TToolDataPreparator.Create(FunctionToCall: TToolDataPreparedProc; CallOnFail: Boolean = False); +begin + FFunc := FunctionToCall; + FCallOnFail := CallOnFail; +end; + +procedure TToolDataPreparator.Prepare(FileSource: IFileSource; var SelectedFiles: TFiles); +var + I: Integer; + FileList: TStringList; +begin + case PrepareData(FileSource, SelectedFiles, @OnCopyOutStateChanged) of + pdrSynchronous: + try + FileList := TStringList.Create; + for I := 0 to SelectedFiles.Count - 1 do + FileList.Add(SelectedFiles[i].FullPath); + FFunc(FileList, nil); + finally + Free; + end; + pdrFailed: + try + if FCallOnFail then + FFunc(nil, nil); + finally + Free; + end; + end; +end; + +procedure TToolDataPreparator.OnCopyOutStateChanged( + Operation: TFileSourceOperation; State: TFileSourceOperationState); +var WaitData: TEditorWaitData; +begin + if (State <> fsosStopped) then + Exit; + try + if Operation.Result = fsorFinished then + begin + WaitData := TEditorWaitData.Create(Operation as TFileSourceCopyOperation); + FFunc(WaitData.GetFileList, WaitData); + end + else + begin + if FCallOnFail then + FFunc(nil, nil); + end; + finally + Free; + end; +end; + +{ TToolDataPreparator2 } + +type + TToolDataPreparator2 = class + protected + FFunc: TToolDataPreparedProc; + FCallOnFail: Boolean; + FFailed: Boolean; + FFileList1: TStringList; + FFileList2: TStringList; + FPrepared1: Boolean; + FPrepared2: Boolean; + FWaitData1: TEditorWaitData; + FWaitData2: TEditorWaitData; + procedure OnCopyOutStateChanged1(Operation: TFileSourceOperation; + State: TFileSourceOperationState); + procedure OnCopyOutStateChanged2(Operation: TFileSourceOperation; + State: TFileSourceOperationState); + procedure TryFinish; + public + constructor Create(FunctionToCall: TToolDataPreparedProc; CallOnFail: Boolean = False); + procedure Prepare(FileSource1: IFileSource; var SelectedFiles1: TFiles; + FileSource2: IFileSource; var SelectedFiles2: TFiles); + destructor Destroy; override; + end; + +constructor TToolDataPreparator2.Create(FunctionToCall: TToolDataPreparedProc; CallOnFail: Boolean = False); +begin + FFunc := FunctionToCall; + FCallOnFail := CallOnFail; +end; + +procedure TToolDataPreparator2.Prepare(FileSource1: IFileSource; var SelectedFiles1: TFiles; + FileSource2: IFileSource; var SelectedFiles2: TFiles); +var + I: Integer; +begin + case PrepareData(FileSource1, SelectedFiles1, @OnCopyOutStateChanged1) of + pdrSynchronous: + begin + FFileList1 := TStringList.Create; + for I := 0 to SelectedFiles1.Count - 1 do + FFileList1.Add(SelectedFiles1[I].FullPath); + FPrepared1 := True; + end; + pdrFailed: + begin + try + if FCallOnFail then + FFunc(nil, nil); + finally + Free; + end; + Exit; + end; + end; + + case PrepareData(FileSource2, SelectedFiles2, @OnCopyOutStateChanged2) of + pdrSynchronous: + begin + FFileList2 := TStringList.Create; + for I := 0 to SelectedFiles2.Count - 1 do + FFileList2.Add(SelectedFiles2[I].FullPath); + FPrepared2 := True; + end; + pdrFailed: + begin + FPrepared2 := True; + FFailed := True; + end; + end; + + TryFinish; +end; + +procedure TToolDataPreparator2.OnCopyOutStateChanged1( + Operation: TFileSourceOperation; State: TFileSourceOperationState); +begin + if (State <> fsosStopped) then + Exit; + FPrepared1 := True; + if not FFailed then + begin + if Operation.Result = fsorFinished then + begin + FWaitData1 := TEditorWaitData.Create(Operation as TFileSourceCopyOperation); + FFileList1 := FWaitData1.GetFileList; + end + else + begin + FFailed := True; +// if not FPrepared2 and Assigned(FOperation2) then +// FOperation2.Stop(); + end; + end; + TryFinish; +end; + +procedure TToolDataPreparator2.OnCopyOutStateChanged2( + Operation: TFileSourceOperation; State: TFileSourceOperationState); +begin + if (State <> fsosStopped) then + Exit; + FPrepared2 := True; + if not FFailed then + begin + if Operation.Result = fsorFinished then + begin + FWaitData2 := TEditorWaitData.Create(Operation as TFileSourceCopyOperation); + FFileList2 := FWaitData2.GetFileList; + end + else + begin + FFailed := True; +// if not FPrepared1 and Assigned(FOperation1) then +// FOperation1.Stop(); + end; + end; + TryFinish; +end; + +procedure TToolDataPreparator2.TryFinish; +var + s: string; + WaitData: TWaitDataDouble; +begin + if FPrepared1 and FPrepared2 then + try + if FFailed then + begin + if FCallOnFail then + FFunc(nil, nil); + Exit; + end; + if Assigned(FFileList2) then + for s in FFileList2 do + FFileList1.Append(s); + if Assigned(FWaitData1) or Assigned(FWaitData2) then + begin + WaitData := TWaitDataDouble.Create(FWaitData1, FWaitData2); + FWaitData1 := nil; + FWaitData2 := nil; + FFunc(FFileList1, WaitData); + end + else + FFunc(FFileList1, nil); + finally + Free; + end; +end; + +destructor TToolDataPreparator2.Destroy; +begin + inherited Destroy; + if Assigned(FFileList1) then + FFileList1.Free; + if Assigned(FFileList2) then + FFileList2.Free; + if Assigned(FWaitData1) then + FWaitData1.Free; + if Assigned(FWaitData2) then + FWaitData2.Free; +end; + +procedure PrepareToolData(FileSource: IFileSource; var SelectedFiles: TFiles; + FunctionToCall: TToolDataPreparedProc); +begin + with TToolDataPreparator.Create(FunctionToCall) do + Prepare(FileSource, SelectedFiles); +end; + +procedure PrepareToolData(FileSource1: IFileSource; var SelectedFiles1: TFiles; + FileSource2: IFileSource; var SelectedFiles2: TFiles; + FunctionToCall: TToolDataPreparedProc); +begin + with TToolDataPreparator2.Create(FunctionToCall) do + Prepare(FileSource1, SelectedFiles1, FileSource2, SelectedFiles2); +end; + end. | ||||
Fixed in Revision | 7834,7935 | ||||
Operating system | Windows, Linux | ||||
Widgetset | |||||
Architecture | 64-bit | ||||
|
To compare files from search result no extra code is needed, just rewrite condition to allow it. |
|
In this case is better to check "fspDirectAccess" property. Done (revision 7834). |
|
Patch for supporting archives and other indirect filesystems, such as FTP. The code to handle such cases was taken from the viewer and editor openers, but the inability to wait for two operations required either additional variables and complex logic directly in the TMainCommands class or creating a separate class for files preparation, which I did. Apart from implementing the feature, the effort resulted in complex refactoring, its goal was to unify files preparation for viewer, editor and differ. This part was not fully met, but the foundations are laid. |
|
Also I intentionally did not make some changes for the purpose of patch readability: - Almost all of the cm_CompareContents procedure should be indented by two spaces less. - TEditorWaitData.Done (former EditDone) should be moved to the place of other class members. - TExtToolWaitThread.Execute (former TEditorWaitThread) could be indented more to take into account two try clauses that it is now wrapped in. |
|
I've done the corrections mentioned in the previous note, refactored the main procedure a bit to reduce nesting, changed some messages, added support for launching it from directory synchronization tool, and commited. |
Date Modified | Username | Field | Change |
---|---|---|---|
2017-10-06 17:39 | CryHam | New Issue | |
2017-10-07 02:06 | accorp | Note Added: 0002370 | |
2017-10-07 02:06 | accorp | File Added: compare-search-result.diff | |
2017-10-08 12:02 | Alexx2000 | Fixed in Revision | => 7834 |
2017-10-08 12:02 | Alexx2000 | Note Added: 0002371 | |
2017-10-08 12:02 | Alexx2000 | Status | new => acknowledged |
2017-12-17 01:15 | cordylus | File Added: bug1927.patch | |
2017-12-17 01:53 | cordylus | Note Added: 0002429 | |
2017-12-17 02:29 | cordylus | Note Added: 0002430 | |
2017-12-17 02:31 | cordylus | Note Edited: 0002430 | |
2017-12-17 03:17 | cordylus | Note Edited: 0002430 | |
2017-12-17 03:19 | cordylus | Note Edited: 0002430 | |
2017-12-20 19:55 | cordylus | Note Added: 0002442 | |
2017-12-20 19:56 | cordylus | Fixed in Revision | 7834 => 7834,7935 |
2017-12-20 19:56 | cordylus | Assigned To | => cordylus |
2017-12-20 19:56 | cordylus | Status | acknowledged => resolved |
2020-11-30 07:44 | Alexx2000 | Status | resolved => closed |