doublecmd/src/ushowform.pas

428 lines
11 KiB
ObjectPascal

{
Seksi Commander
----------------------------
Licence : GNU GPL v 2.0
Author : radek.cervinka@centrum.cz
showing editor or viewer by configuration dialog
contributors:
Copyright (C) 2006-2015 Alexander Koblov (alexx2000@mail.ru)
}
unit uShowForm;
{$mode objfpc}{$H+}
interface
uses
Classes, DCBasicTypes, uFileSource, uFileSourceOperation;
type
{ TEditorWaitData }
TEditorWaitData = class
public
FileName: String;
TargetPath: String;
FileTime: TFileTime;
SourceFileSource: IFileSource;
TargetFileSource: IFileSource;
public
destructor Destroy; override;
protected
procedure OnCopyInStateChanged(Operation: TFileSourceOperation;
State: TFileSourceOperationState);
end;
procedure EditDone(WaitData: TEditorWaitData);
procedure RunExtDiffer(CompareList: TStringList);
procedure ShowEditorByGlob(const sFileName: String);
procedure ShowEditorByGlob(WaitData: TEditorWaitData); overload;
procedure ShowDifferByGlob(const LeftName, RightName: String);
procedure ShowViewerByGlob(const sFileName: String);
procedure ShowViewerByGlobList(const FilesToView: TStringList;
const aFileSource: IFileSource);
implementation
uses
SysUtils, Process, DCProcessUtf8, Dialogs, LCLIntf,
uShellExecute, uGlobs, uOSUtils, fEditor, fViewer, uDCUtils,
uTempFileSystemFileSource, uLng, fDiffer, uDebug, DCOSUtils, uShowMsg,
uFile, uFileSourceCopyOperation, uFileSystemFileSource,
uFileSourceOperationOptions, uOperationsManager, uFileSourceOperationTypes,
uWcxArchiveFileSource, uWfxPluginFileSource, fFileExecuteYourSelf;
type
{ TViewerWaitThread }
TViewerWaitThread = class(TThread)
private
FFileList : TStringList;
FFileSource: IFileSource;
protected
procedure Execute; override;
public
constructor Create(const FilesToView: TStringList; const aFileSource: IFileSource);
destructor Destroy; override;
end;
{ TEditorWaitThread }
TEditorWaitThread = class(TThread)
private
FWaitData: TEditorWaitData;
private
procedure RunEditDone;
procedure ShowWaitForm;
protected
procedure Execute; override;
public
constructor Create(WaitData: TEditorWaitData);
end;
procedure RunExtTool(const ExtTool: TExternalToolOptions; sFileName: String);
var
sCmd: String;
sParams:string='';
begin
sCmd := ExtTool.Path;
sParams := ExtTool.Parameters;
//If there is %p already configured in the parameter, we assume user configured it the way he wants.
//This might be in non-common case where there are paramters AFTER the filename to open.
//If there is not %p, let's do thing like legacy was and let's add the filename received as paramter.
if pos('%p',sParams)=0 then
sParams := ConcatenateStrWithSpace(sParams,QuoteFilenameIfNecessary(sFileName));
ProcessExtCommandFork(sCmd, sParams, '', nil, ExtTool.RunInTerminal, ExtTool.KeepTerminalOpen);
end;
procedure RunExtDiffer(CompareList: TStringList);
var
i : Integer;
sCmd: String;
sParams:string='';
begin
with gExternalTools[etDiffer] do
begin
sCmd := QuoteStr(ReplaceEnvVars(Path));
if Parameters <> EmptyStr then
sParams := sParams + ' ' + Parameters;
for i := 0 to CompareList.Count - 1 do
sParams := sParams + ' ' + QuoteStr(CompareList.Strings[i]);
try
ProcessExtCommandFork(sCmd, sParams, '', nil, RunInTerminal, KeepTerminalOpen);
except
on e: EInvalidCommandLine do
MessageDlg(rsToolErrorOpeningDiffer,
rsMsgInvalidCommandLine + ' (' + rsToolDiffer + '):' + LineEnding + e.Message,
mtError, [mbOK], 0);
end;
end;
end;
procedure ShowEditorByGlob(const sFileName: String);
begin
if gExternalTools[etEditor].Enabled then
begin
try
RunExtTool(gExternalTools[etEditor], sFileName);
except
on e: EInvalidCommandLine do
MessageDlg(rsToolErrorOpeningEditor,
rsMsgInvalidCommandLine + ' (' + rsToolEditor + '):' + LineEnding + e.Message,
mtError, [mbOK], 0);
end;
end
else
ShowEditor(sFileName);
end;
procedure ShowEditorByGlob(WaitData: TEditorWaitData);
begin
if gExternalTools[etEditor].Enabled then
with TEditorWaitThread.Create(WaitData) do Start
else begin
ShowEditor(WaitData);
end;
end;
procedure ShowViewerByGlob(const sFileName: String);
var
sl:TStringList;
begin
if gExternalTools[etViewer].Enabled then
begin
try
RunExtTool(gExternalTools[etViewer], sFileName);
except
on e: EInvalidCommandLine do
MessageDlg(rsToolErrorOpeningViewer,
rsMsgInvalidCommandLine + ' (' + rsToolViewer + '):' + LineEnding + e.Message,
mtError, [mbOK], 0);
end;
end
else
begin
sl:=TStringList.Create;
try
sl.Add(sFileName);
ShowViewer(sl);
finally
FreeAndNil(sl);
end;
end;
end;
procedure ShowDifferByGlob(const LeftName, RightName: String);
var
sl: TStringList;
begin
if gExternalTools[etDiffer].Enabled then
begin
sl:= TStringList.Create;
try
sl.add(LeftName);
sl.add(RightName);
RunExtDiffer(sl);
finally
sl.free;
end;
end
else
ShowDiffer(LeftName, RightName);
end;
procedure ShowViewerByGlobList(const FilesToView : TStringList;
const aFileSource: IFileSource);
var
I : Integer;
WaitThread : TViewerWaitThread;
begin
if gExternalTools[etViewer].Enabled then
begin
DCDebug('ShowViewerByGlobList - Use ExtView');
if aFileSource.IsClass(TTempFileSystemFileSource) then
begin
WaitThread := TViewerWaitThread.Create(FilesToView, aFileSource);
WaitThread.Start;
end
else
begin
// TODO: If possible should run one instance of external viewer
// with multiple file names as parameters.
for i:=0 to FilesToView.Count-1 do
RunExtTool(gExternalTools[etViewer], FilesToView.Strings[i]);
end;
end // gUseExtView
else
ShowViewer(FilesToView, aFileSource);
end;
procedure EditDone(WaitData: TEditorWaitData);
var
Files: TFiles;
Operation: TFileSourceCopyOperation;
begin
with WaitData do
try
// File was modified
if mbFileAge(FileName) <> FileTime then
begin
if not msgYesNo(Format(rsMsgCopyBackward, [ExtractFileName(FileName)])) then Exit;
if (fsoCopyIn in TargetFileSource.GetOperationsTypes) and
((TargetFileSource is TWcxArchiveFileSource) or (TargetFileSource is TWfxPluginFileSource)) then
begin
Files:= TFiles.Create(SourceFileSource.GetRootDir);
Files.Add(TFileSystemFileSource.CreateFileFromFile(FileName));
Operation:= TargetFileSource.CreateCopyInOperation(SourceFileSource, Files, TargetPath) as TFileSourceCopyOperation;
// Copy file back
if Assigned(Operation) then
begin
Operation.AddStateChangedListener([fsosStopped], @OnCopyInStateChanged);
Operation.FileExistsOption:= fsoofeOverwrite;
OperationsManager.AddOperation(Operation);
WaitData:= nil; // Will be free in operation
end;
end
else if msgYesNo(rsMsgCouldNotCopyBackward + LineEnding + FileName) then
begin
(SourceFileSource as ITempFileSystemFileSource).DeleteOnDestroy:= False;
end;
end;
finally
WaitData.Free;
end;
end;
{ TEditorWaitData }
destructor TEditorWaitData.Destroy;
begin
inherited Destroy;
SourceFileSource:= nil;
TargetFileSource:= nil;
end;
procedure TEditorWaitData.OnCopyInStateChanged(Operation: TFileSourceOperation;
State: TFileSourceOperationState);
var
aFileSource: ITempFileSystemFileSource;
aCopyOperation: TFileSourceCopyOperation;
begin
if (State = fsosStopped) then
begin
aCopyOperation := Operation as TFileSourceCopyOperation;
aFileSource := aCopyOperation.SourceFileSource as ITempFileSystemFileSource;
with aCopyOperation.RetrieveStatistics do
begin
if DoneFiles <> TotalFiles then
begin
if msgYesNo(Operation.Thread, rsMsgCouldNotCopyBackward + LineEnding + aCopyOperation.SourceFiles[0].FullPath) then
begin
aFileSource.DeleteOnDestroy:= False;
end;
end;
end;
Free;
end;
end;
{ TEditorWaitThread }
procedure TEditorWaitThread.RunEditDone;
begin
EditDone(FWaitData);
end;
procedure TEditorWaitThread.ShowWaitForm;
begin
ShowFileEditExternal(FWaitData);
end;
procedure TEditorWaitThread.Execute;
var
StartTime: QWord;
Process : TProcessUTF8;
sCmd, sSecureEmptyStr: String;
begin
Process := TProcessUTF8.Create(nil);
with gExternalTools[etEditor] do
begin
sCmd := ReplaceEnvVars(Path);
// TProcess arguments must be enclosed with double quotes and not escaped.
if RunInTerminal then
begin
sCmd := QuoteStr(sCmd);
if Parameters <> EmptyStr then
sCmd := sCmd + ' ' + Parameters;
sCmd := sCmd + ' ' + QuoteStr(FWaitData.FileName);
sSecureEmptyStr := EmptyStr; // Let's play safe and don't let EmptyStr being passed as "VAR" parameter of "FormatTerminal"
FormatTerminal(sCmd, sSecureEmptyStr, False);
end
else
begin
sCmd := '"' + sCmd + '"';
if Parameters <> EmptyStr then
sCmd := sCmd + ' ' + Parameters;
sCmd := sCmd + ' "' + FWaitData.FileName + '"';
end;
end;
Process.CommandLine := sCmd;
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
// user can confirm when editing has ended.
if GetTickCount64 - StartTime < gEditWaitTime then
begin
Synchronize(@ShowWaitForm);
end
else begin
Synchronize(@RunEditDone);
end;
end;
constructor TEditorWaitThread.Create(WaitData: TEditorWaitData);
begin
inherited Create(True);
FreeOnTerminate := True;
FWaitData := WaitData;
end;
{ TViewerWaitThread }
constructor TViewerWaitThread.Create(const FilesToView: TStringList; const aFileSource: IFileSource);
begin
inherited Create(True);
FreeOnTerminate := True;
FFileList := TStringList.Create;
// Make a copy of list elements.
FFileList.Assign(FilesToView);
FFileSource := aFileSource;
end;
destructor TViewerWaitThread.Destroy;
begin
if Assigned(FFileList) then
FreeAndNil(FFileList);
// Delete the temporary file source and all files inside.
FFileSource := nil;
inherited Destroy;
end;
procedure TViewerWaitThread.Execute;
var
Process : TProcessUTF8;
sCmd, sSecureEmptyStr: String;
begin
Process := TProcessUTF8.Create(nil);
with gExternalTools[etViewer] do
begin
sCmd := ReplaceEnvVars(Path);
// TProcess arguments must be enclosed with double quotes and not escaped.
if RunInTerminal then
begin
sCmd := QuoteStr(sCmd);
if Parameters <> EmptyStr then
sCmd := sCmd + ' ' + Parameters;
sCmd := sCmd + ' ' + QuoteStr(FFileList.Strings[0]);
sSecureEmptyStr := EmptyStr; //Let's play safe and don't let EmptyStr being passed as "VAR" parameter of "FormatTerminal"
FormatTerminal(sCmd, sSecureEmptyStr, False);
end
else
begin
sCmd := '"' + sCmd + '"';
if Parameters <> EmptyStr then
sCmd := sCmd + ' ' + Parameters;
sCmd := sCmd + ' "' + FFileList.Strings[0] + '"';
end;
end;
Process.CommandLine := sCmd;
Process.Options := [poWaitOnExit];
Process.Execute;
Process.Free;
end;
end.