summaryrefslogtreecommitdiff
path: root/UCore.pas
diff options
context:
space:
mode:
Diffstat (limited to 'UCore.pas')
-rw-r--r--UCore.pas503
1 files changed, 293 insertions, 210 deletions
diff --git a/UCore.pas b/UCore.pas
index 5415535..86e896b 100644
--- a/UCore.pas
+++ b/UCore.pas
@@ -28,7 +28,42 @@ function MakeDirectory(ListView: TGTKListView; Engine: TPanelEngine; LeftPanel:
procedure FindNextSelected(ListView: TGTKListView; DataList: TList; var Item1, Item2: string);
procedure UnselectAll(ListView: TGTKListView; DataList: TList);
-type TWorkerThread = class(TThread)
+
+type TVFSCallbackThread = class(TThread)
+ private
+ FThreadID: __pthread_t;
+ FCopyProgressFunc: TEngineProgressFunc;
+ procedure PrepareExecute; // Call this right after thread has been started
+ public
+ AEngine: TPanelEngine;
+ APlugin: TVFSPlugin;
+
+ VFSCallbackEvent: TSimpleEvent;
+ VFSAskQuestion_Message: PChar;
+ VFSAskQuestion_Choices: PPChar;
+ VFSAskQuestion_Choice: PInteger;
+ VFSAskQuestion_Display: boolean;
+
+ VFSAskPassword_Message: PChar;
+ VFSAskPassword_default_user: PChar;
+ VFSAskPassword_default_domain: PChar;
+ VFSAskPassword_default_password: PChar;
+ VFSAskPassword_flags: TVFSAskPasswordFlags;
+ VFSAskPassword_username: PPChar;
+ VFSAskPassword_password: PPChar;
+ VFSAskPassword_anonymous: PInteger;
+ VFSAskPassword_domain: PPChar;
+ VFSAskPassword_password_save: PVFSPasswordSave;
+ VFSAskPassword_Display: boolean;
+ VFSAskPassword_Result: LongBool;
+
+ VFSCallbackCancelled: boolean;
+
+ constructor Create(CreateSuspended: boolean);
+ destructor Destroy; override;
+ end;
+
+ TWorkerThread = class(TVFSCallbackThread)
private
FCancelled: boolean;
GUIMutex: TCriticalSection;
@@ -119,15 +154,12 @@ type TWorkerThread = class(TThread)
procedure CancelIt;
end;
- TOpenDirThread = class(TThread)
+ TOpenDirThread = class(TVFSCallbackThread)
private
- FThreadID: __pthread_t;
function ChangeDir(Engine: TPanelEngine; Path: string; var SelItem: string; const AutoFallBack: boolean): integer;
protected
procedure Execute; override;
public
- AEngine: TPanelEngine;
- xEngine: TVFSEngine;
APath: string;
ASelItem: string;
AAutoFallBack: boolean;
@@ -135,35 +167,12 @@ type TWorkerThread = class(TThread)
ChDirResult, ListingResult, VFSOpenResult: integer;
Finished, CancelIt: boolean;
RunningTime: Int64;
- APlugin: TVFSPlugin;
AFullPath, AHighlightItem: string;
- Password: string;
-
- VFSCallbackEvent: TSimpleEvent;
- VFSAskQuestion_Message: PChar;
- VFSAskQuestion_Choices: PPChar;
- VFSAskQuestion_Choice: PInteger;
- VFSAskQuestion_Display: boolean;
-
- VFSAskPassword_Message: PChar;
- VFSAskPassword_default_user: PChar;
- VFSAskPassword_default_domain: PChar;
- VFSAskPassword_flags: TVFSAskPasswordFlags;
- VFSAskPassword_username: PPChar;
- VFSAskPassword_password: PPChar;
- VFSAskPassword_anonymous: PInteger;
- VFSAskPassword_domain: PPChar;
- VFSAskPassword_password_save: PVFSPasswordSave;
- VFSAskPassword_Display: boolean;
- VFSAskPassword_Result: LongBool;
-
- VFSCallbackCancelled: boolean;
constructor Create;
destructor Destroy; override;
end;
-
// Thread aware functions (also half-thread-safe) without any piece of GTK code
procedure DeleteFilesWorker(SenderThread: TWorkerThread);
procedure CopyFilesWorker(SenderThread: TWorkerThread);
@@ -176,7 +185,6 @@ procedure DummyThreadWorker(SenderThread: TWorkerThread);
// Classic functions - don't need progress window
function CreateSymlink(const FileName, PossibleNewName: string; Engine: TPanelEngine) : boolean;
function EditSymlink(const FileName: string; Engine: TPanelEngine) : boolean;
-function HandleLogin(Parent: TComponent; Engine: TPanelEngine; UserName, Password: string): boolean;
procedure GetDirSize(AListView: TGTKListView; Engine: TPanelEngine; DataList: TList; AllItems: boolean);
@@ -233,9 +241,152 @@ var LeftLocalEngine, RightLocalEngine: TPanelEngine;
implementation
(********************************************************************************************************************************)
uses SysUtils, DateUtils, StrUtils, UConfig, UDirDelete, UOverwrite, ULocale,
- UNewDir, UFileAssoc, USymlink, UCoreClasses, ULogin, URemoteWait, UMain, UGnome;
+ UNewDir, UFileAssoc, USymlink, UCoreClasses, URemoteWait, UMain, UGnome;
+
+
+
+
+(********************************************************************************************************************************)
+constructor TVFSCallbackThread.Create(CreateSuspended: boolean);
+begin
+ inherited Create(CreateSuspended);
+ APlugin := nil;
+ VFSCallbackEvent := TSimpleEvent.Create;
+ VFSAskQuestion_Display := False;
+ VFSAskPassword_Display := False;
+ VFSCallbackCancelled := False;
+end;
+destructor TVFSCallbackThread.Destroy;
+begin
+ VFSCallbackEvent.Free;
+ inherited Destroy;
+end;
+
+procedure TVFSCallbackThread.PrepareExecute;
+begin
+ FThreadID := pthread_self;
+ VFSCallbackCancelled := False;
+end;
+
+(********************************************************************************************************************************)
+procedure vfs_ask_question_callback(const AMessage: PChar; const Choices: PPChar; choice: PInteger; cancel_choice: Integer; user_data: Pointer); cdecl;
+var Thread: TVFSCallbackThread;
+begin
+ Thread := user_data;
+ if (Thread = nil) { or (not (Thread is TVFSCallbackThread))} then begin
+ DebugMsg(['(ERROR): vfs_ask_question_callback: user_data is not TVFSCallbackThread, exiting.']);
+ Exit;
+ end;
+
+ if pthread_self = Application.ThreadID then begin
+ DebugMsg(['!! (WARNING): vfs_ask_question_callback called from the main thread, expected spawn from a OpenDirThread']);
+ HandleVFSAskQuestionCallback(FMain.FWidget, AMessage, Choices, choice);
+ if (choice <> nil) then Thread.VFSCallbackCancelled := (choice^ < 0) or (choice^ = cancel_choice);
+ Exit;
+ end;
+
+ if pthread_self = Thread.FThreadID then begin
+ DebugMsg(['******* vfs_ask_question_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16)]);
+ Thread.VFSAskQuestion_Message := AMessage;
+ Thread.VFSAskQuestion_Choices := Choices;
+ Thread.VFSAskQuestion_Choice := choice;
+ Thread.VFSAskQuestion_Display := True;
+ Thread.VFSCallbackEvent.ResetEvent;
+ Thread.VFSCallbackEvent.WaitFor(INFINITE);
+ DebugMsg(['******* thread: resuming...']);
+ if (choice <> nil) then Thread.VFSCallbackCancelled := (choice^ < 0) or (choice^ = cancel_choice);
+ Exit;
+ end;
+
+ DebugMsg(['!! (ERROR): vfs_ask_question_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']);
+ DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]);
+end;
+
+function vfs_ask_password_callback(const AMessage: PChar; const default_user: PChar; const default_domain: PChar; const default_password: PChar; flags: TVFSAskPasswordFlags;
+ username, password: PPChar; anonymous: PInteger; domain: PPChar; password_save: PVFSPasswordSave;
+ user_data: Pointer): LongBool; cdecl;
+var Thread: TVFSCallbackThread;
+ def_pass: PChar;
+begin
+ Result := False;
+ Thread := user_data;
+ if (Thread = nil) { or (not (Thread is TVFSCallbackThread))} then begin
+ DebugMsg(['(ERROR): vfs_ask_question_callback: user_data is not TVFSCallbackThread, exiting.']);
+ Exit;
+ end;
+
+ def_pass := default_password;
+
+ // Use stored password, if previously set
+ if ((flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE) and (password <> nil) and
+ (Thread.AEngine is TVFSEngine) and (Length((Thread.AEngine as TVFSEngine).Password) > 0) then
+ begin
+ if not (Thread.AEngine as TVFSEngine).PasswordUsed then begin
+ DebugMsg([' (II) vfs_ask_password_callback: reusing manually set password']);
+ password^ := g_strdup(PChar((Thread.AEngine as TVFSEngine).Password));
+ (Thread.AEngine as TVFSEngine).PasswordUsed := True;
+ Result := True;
+ Thread.VFSCallbackCancelled := False;
+ Exit;
+ end else def_pass := PChar((Thread.AEngine as TVFSEngine).Password);
+ end;
+
+ // Ask for password
+ if pthread_self = Application.ThreadID then begin
+ DebugMsg(['!! (WARNING): vfs_ask_password_callback called from the main thread, expected spawn from a OpenDirThread']);
+ Result := HandleVFSAskPasswordCallback(FMain.FWidget, AMessage, default_user, default_domain, def_pass, flags, username, password, anonymous, domain, password_save);
+ Thread.VFSCallbackCancelled := Result = False;
+ end else
+ if pthread_self = Thread.FThreadID then begin
+ DebugMsg(['******* vfs_ask_password_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]);
+ Thread.VFSAskPassword_Message := AMessage;
+ Thread.VFSAskPassword_default_user := default_user;
+ Thread.VFSAskPassword_default_domain := default_domain;
+ Thread.VFSAskPassword_default_password := def_pass;
+ Thread.VFSAskPassword_flags := flags;
+ Thread.VFSAskPassword_username := username;
+ Thread.VFSAskPassword_password := password;
+ Thread.VFSAskPassword_anonymous := anonymous;
+ Thread.VFSAskPassword_domain := domain;
+ Thread.VFSAskPassword_password_save := password_save;
+ Thread.VFSAskPassword_Display := True;
+ Thread.VFSAskPassword_Result := False;
+ Thread.VFSCallbackEvent.ResetEvent;
+ Thread.VFSCallbackEvent.WaitFor(INFINITE);
+ DebugMsg(['******* thread: resuming...']);
+ Result := Thread.VFSAskPassword_Result;
+ Thread.VFSCallbackCancelled := Result = False;
+ end else
+ begin
+ DebugMsg(['!! (ERROR): vfs_ask_password_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']);
+ DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]);
+ end;
+
+ // Save password back to the engine
+ if ((flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE) and
+ Result and (password <> nil) and (strlen(password^) > 0) and
+ (Thread.AEngine is TVFSEngine) then
+ begin
+ (Thread.AEngine as TVFSEngine).Password := string(password^);
+ (Thread.AEngine as TVFSEngine).PasswordUsed := True;
+ end;
+end;
+
+function vfs_progress_callback(position, max: Int64; user_data: Pointer): LongBool; cdecl;
+begin
+// DebugMsg(['VFSCopyCallBackFunc called (iPos = ', iPos, ', iMax = ', iMax, ')']);
+ Result := True;
+ if not Assigned(user_data) then Exit;
+
+ if Assigned(TVFSCallbackThread(user_data).FCopyProgressFunc) then
+ try
+ Result := TVFSCallbackThread(user_data).FCopyProgressFunc(user_data, position);
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in vfs_progress_callback(position=', position, ', max=', max, ', user_data=', user_data, '): (', E.ClassName, '): ', E.Message]);
+ end;
+end;
(********************************************************************************************************************************)
procedure ClearListData(List: TList);
@@ -586,6 +737,10 @@ end;
Exit;
end;
case ErrorType of
+ 0 : begin
+ CancelIt;
+ Exit;
+ end;
1 : s := LANGMemoryAllocationFailed;
2 : s := LANGCannotOpenSourceFile;
3 : s := LANGCannotOpenDestinationFile;
@@ -598,8 +753,9 @@ end;
else s2 := LANGMoveError;
if ErrorType <> 1 then s3 := StrToUTF8(FileName)
else s3 := '';
+
case ShowDirDeleteDialog(3, s, s3, GetErrorString(ErrorNum), s2) of
- 0 : begin // Cancel button
+ 0, 252 : begin // Cancel button, Escape
Result := False;
CancelIt;
end;
@@ -704,28 +860,48 @@ var DefResponse: integer; // Global variables for this function
begin
Result := False;
try
- with SenderThread do begin
- if ((SrcEngine is TLocalTreeEngine) and (DestEngine is TLocalTreeEngine)) or
- ((SrcEngine is TLocalTreeEngine) and (not (DestEngine is TLocalTreeEngine)))
- then Result := DestEngine.CopyFileIn(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ProgressFunc, @CopyFilesWorker_ErrorFunc, Append) else
-// DebugMsg(['2 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']);
- if (not (SrcEngine is TLocalTreeEngine)) and (DestEngine is TLocalTreeEngine)
- then Result := SrcEngine.CopyFileOut(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ProgressFunc, @CopyFilesWorker_ErrorFunc, Append)
- // both files are on different engines, we will have to handle the copy process ourselves
- else Result := ManualCopyFile(SourceFile, DestFile, Append);
-// DebugMsg(['3 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']);
-
- // If size differs, then delete target file
- if (not Append) and (not Result) then begin
-// DebugMsg(['4 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']);
- DataSrc := SrcEngine.GetFileInfoSL(SourceFile);
- if DataSrc = nil then Exit;
- DataDest := DestEngine.GetFileInfoSL(DestFile);
- if DataDest = nil then Exit;
- if DataSrc^.Size <> DataDest^.Size then DestEngine.Remove(DestFile);
+ with SenderThread do begin
+ AEngine := nil;
+ FCopyProgressFunc := CopyFilesWorker_ProgressFunc;
+
+ // local -> local
+ if (SrcEngine is TLocalTreeEngine) and (DestEngine is TLocalTreeEngine)
+ then Result := DestEngine.CopyFileIn(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ProgressFunc, @CopyFilesWorker_ErrorFunc, Append)
+ else
+
+ // from local engine to VFS engine
+ if (SrcEngine is TLocalTreeEngine) and (DestEngine is TVFSEngine) then
+ begin
+ AEngine := DestEngine;
+ Result := (DestEngine as TVFSEngine).CopyFileInEx(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ErrorFunc, Append,
+ @vfs_ask_question_callback, @vfs_ask_password_callback, @vfs_progress_callback, SenderThread);
+ end else
+
+ // from VFS engine to local (most common use)
+ if (SrcEngine is TVFSEngine) and (DestEngine is TLocalTreeEngine) then
+ begin
+ AEngine := SrcEngine;
+ Result := (SrcEngine as TVFSEngine).CopyFileOutEx(SenderThread, SourceFile, DestFile, @CopyFilesWorker_ErrorFunc, Append,
+ @vfs_ask_question_callback, @vfs_ask_password_callback, @vfs_progress_callback, SenderThread);
+ end
+
+ // VFS to VFS (not supported yet)
+ else
+ begin
+ AEngine := SrcEngine;
+ Result := ManualCopyFile(SourceFile, DestFile, Append);
+ end;
+ AEngine := nil;
+
+ // Copy OK? (check size, otherwise delete target file)
+ if (not Append) and (not Result) then begin
+ DataSrc := SrcEngine.GetFileInfoSL(SourceFile);
+ if DataSrc = nil then Exit;
+ DataDest := DestEngine.GetFileInfoSL(DestFile);
+ if DataDest = nil then Exit;
+ if DataSrc^.Size <> DataDest^.Size then DestEngine.Remove(DestFile);
+ end;
end;
- end;
-// DebugMsg(['(II) CopyFilesWorker.LocalCopyFile: finished']);
except
on E: Exception do DebugMsg(['*** Exception raised in LocalCopyFile(SourceFile=', SourceFile, ', DestFile=', DestFile, ', Append=', Append, '): (', E.ClassName, '): ', E.Message]);
end;
@@ -2090,6 +2266,32 @@ begin
Application.ProcessMessages;
// DebugMsg(['After refresh']);
+
+ // VFS callbacks
+ if SenderThread.VFSAskQuestion_Display then begin
+ SenderThread.VFSAskQuestion_Display := False;
+ DebugMsg(['ProcessProgressThread - Main thread: displaying question dialog']);
+ HandleVFSAskQuestionCallback(ProgressForm.FWidget, SenderThread.VFSAskQuestion_Message, SenderThread.VFSAskQuestion_Choices, SenderThread.VFSAskQuestion_Choice);
+ SenderThread.VFSCallbackEvent.SetEvent;
+ end;
+ if SenderThread.VFSAskPassword_Display then begin
+ SenderThread.VFSAskPassword_Display := False;
+ DebugMsg(['ProcessProgressThread - Main thread: displaying password prompt']);
+ SenderThread.VFSAskPassword_Result := HandleVFSAskPasswordCallback(ProgressForm.FWidget,
+ SenderThread.VFSAskPassword_Message,
+ SenderThread.VFSAskPassword_default_user,
+ SenderThread.VFSAskPassword_default_domain,
+ SenderThread.VFSAskPassword_default_password,
+ SenderThread.VFSAskPassword_flags,
+ SenderThread.VFSAskPassword_username,
+ SenderThread.VFSAskPassword_password,
+ SenderThread.VFSAskPassword_anonymous,
+ SenderThread.VFSAskPassword_domain,
+ SenderThread.VFSAskPassword_password_save);
+ SenderThread.VFSCallbackEvent.SetEvent;
+ end;
+
+
try
if SenderThread.FDialogShowDirDelete then begin
AFDirDelete := nil;
@@ -2182,6 +2384,7 @@ end;
(********************************************************************************************************************************)
procedure TWorkerThread.Execute;
begin
+ PrepareExecute;
if Assigned(WorkerProcedure) then WorkerProcedure(Self);
end;
@@ -2295,6 +2498,7 @@ begin
if DirDeleteCaption = '' then FDirDeleteCaption := LANGRemoveDirectory
else FDirDeleteCaption := DirDeleteCaption;
FDialogShowDirDelete := True;
+ FCallbackLockEvent.ResetEvent;
FCallbackLockEvent.WaitFor(INFINITE);
Result := FDialogResultDirDelete;
end;
@@ -2311,6 +2515,7 @@ begin
FOverwriteSourceFile := SourceFile;
FOverwriteDestFile := DestFile;
FDialogShowOverwrite := True;
+ FCallbackLockEvent.ResetEvent;
FCallbackLockEvent.WaitFor(INFINITE);
Result := FDialogResultOverwrite;
end;
@@ -2321,6 +2526,7 @@ begin
FNewDirLabel := LabelCaption;
FNewDirEdit := Edit;
FDialogShowNewDir := True;
+ FCallbackLockEvent.ResetEvent;
FCallbackLockEvent.WaitFor(INFINITE);
Result := FDialogResultNewDir;
end;
@@ -2333,6 +2539,7 @@ begin
FMsgBoxDefault := Default;
FMsgBoxEscape := Escape;
FDialogShowMsgBox := True;
+ FCallbackLockEvent.ResetEvent;
FCallbackLockEvent.WaitFor(INFINITE);
Result := FDialogResultMsgBox;
end;
@@ -2468,29 +2675,6 @@ end;
(********************************************************************************************************************************)
(********************************************************************************************************************************)
-function HandleLogin(Parent: TComponent; Engine: TPanelEngine; UserName, Password: string): boolean;
-var b: boolean;
-begin
- Result := False;
- b := Engine.Login(UserName, Password) = cVFS_OK;
- if not b then
- repeat
- try
- FLogin := TFLogin.Create(Parent);
- b := FLogin.Run = mbOK;
- UserName := FLogin.UserEntry.Text;
- Password := FLogin.PasswordEntry.Text;
- finally
- FLogin.Free;
- end;
- if not b then Exit;
- if b then b := Engine.Login(UserName, Password) = cVFS_OK;
- until b;
- Result := True;
-end;
-
-(********************************************************************************************************************************)
-(********************************************************************************************************************************)
procedure TGetDirSizeThread.Execute;
begin
Result := Engine.GetDirSize(Path);
@@ -2606,147 +2790,14 @@ begin
ListingResult := 0;
VFSOpenResult := 0;
RunningTime := 0;
- APlugin := nil;
- xEngine := nil;
- Password := '';
- VFSCallbackEvent := TSimpleEvent.Create;
- VFSAskQuestion_Display := False;
- VFSAskPassword_Display := False;
- VFSCallbackCancelled := False;
end;
destructor TOpenDirThread.Destroy;
begin
-{
- if (APlugin <> nil) and (xEngine <> nil) then
- try
- xEngine.Free;
- except
- on E: Exception do DebugMsg(['*** TOpenDirThread.Destroy -Exception: ', E.Message]);
- end;
-}
- VFSCallbackEvent.Free;
inherited Destroy;
end;
-procedure TOpenDirThread.Execute;
-var tt: TDateTime;
-begin
- FThreadID := pthread_self;
- VFSCallbackCancelled := False;
- try
-// Writeln('execute.');
- tt := Now;
- try
-// WriteLn('x1');
- if APlugin <> nil then begin
- xEngine := TVFSEngine.Create(APlugin);
- xEngine.ParentEngine := AEngine;
- xEngine.ArchiveMode := True;
- AEngine.LastHighlightItem := AHighlightItem;
- xEngine.SavePath := AEngine.Path;
- AEngine := xEngine;
- if Length(Password) > 0 then (xEngine as TVFSEngine).Password := Password;
- VFSOpenResult := (xEngine as TVFSEngine).VFSOpenEx(AFullPath);
- end else VFSOpenResult := 0;
-// WriteLn('x2');
-
- if (VFSOpenResult = 0) and (not CancelIt) then begin
-// WriteLn('x3');
- ChDirResult := ChangeDir(AEngine, APath, ASelItem, AAutoFallBack);
-// WriteLn('x4');
-
- if (ChDirResult = 0) and (not CancelIt) then begin
-// WriteLn('x5');
- ListingResult := AEngine.GetListing(ADirList, ConfShowDotFiles);
-// WriteLn('x6');
- end;
-// WriteLn('x7');
- end;
- except
- on E: Exception do DebugMsg(['*** TOpenDirThread.Execute -Exception: ', E.Message]);
- end;
- RunningTime := MilliSecondsBetween(tt, Now);
-// WriteLn('x8');
- finally
- Finished := True;
- end;
-{ except
- on E: Exception do DebugMsg(['*** Exception raised in TOpenDirThread.Execute (', E.ClassName, '): ', E.Message]);
- end; }
-end;
-
(********************************************************************************************************************************)
-procedure vfs_ask_question_callback(const AMessage: PChar; const Choices: PPChar; choice: PInteger; cancel_choice: Integer; user_data: Pointer); cdecl;
-var Thread: TOpenDirThread;
-begin
- Thread := user_data;
-
- if pthread_self = Application.ThreadID then begin
- DebugMsg(['!! (WARNING): vfs_ask_question_callback called from the main thread, expected spawn from a OpenDirThread']);
- HandleVFSAskQuestionCallback(FMain.FWidget, AMessage, Choices, choice);
- if (choice <> nil) and (Thread <> nil) and (Thread is TOpenDirThread) then Thread.VFSCallbackCancelled := (choice^ < 0) or (choice^ = cancel_choice);
- Exit;
- end;
-
- if (Thread <> nil) and (Thread is TOpenDirThread) and (pthread_self = Thread.FThreadID) then begin
- DebugMsg(['******* vfs_ask_question_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16)]);
- Thread.VFSAskQuestion_Message := AMessage;
- Thread.VFSAskQuestion_Choices := Choices;
- Thread.VFSAskQuestion_Choice := choice;
- Thread.VFSAskQuestion_Display := True;
- Thread.VFSCallbackEvent.WaitFor(INFINITE);
- DebugMsg(['******* thread: resuming...']);
- if (choice <> nil) then Thread.VFSCallbackCancelled := (choice^ < 0) or (choice^ = cancel_choice);
- Exit;
- end;
-
- DebugMsg(['!! (ERROR): vfs_ask_question_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']);
- if (Thread <> nil) and (Thread is TOpenDirThread)
- then DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]);
-end;
-
-function vfs_ask_password_callback(const AMessage: PChar; const default_user: PChar; const default_domain: PChar; flags: TVFSAskPasswordFlags;
- username, password: PPChar; anonymous: PInteger; domain: PPChar; password_save: PVFSPasswordSave;
- user_data: Pointer): LongBool; cdecl;
-var Thread: TOpenDirThread;
-begin
- Thread := user_data;
-
- if pthread_self = Application.ThreadID then begin
- DebugMsg(['!! (WARNING): vfs_ask_password_callback called from the main thread, expected spawn from a OpenDirThread']);
- Result := HandleVFSAskPasswordCallback(FMain.FWidget, AMessage, default_user, default_domain, flags, username, password, anonymous, domain, password_save);
- if (Thread <> nil) and (Thread is TOpenDirThread) then Thread.VFSCallbackCancelled := Result = False;
- Exit;
- end;
-
- if (Thread <> nil) and (Thread is TOpenDirThread) and (pthread_self = Thread.FThreadID) then begin
- DebugMsg(['******* vfs_ask_password_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]);
- Thread.VFSAskPassword_Message := AMessage;
- Thread.VFSAskPassword_default_user := default_user;
- Thread.VFSAskPassword_default_domain := default_domain;
- Thread.VFSAskPassword_flags := flags;
- Thread.VFSAskPassword_username := username;
- Thread.VFSAskPassword_password := password;
- Thread.VFSAskPassword_anonymous := anonymous;
- Thread.VFSAskPassword_domain := domain;
- Thread.VFSAskPassword_password_save := password_save;
- Thread.VFSAskPassword_Display := True;
- Thread.VFSAskPassword_Result := False;
- Thread.VFSCallbackEvent.WaitFor(INFINITE);
- DebugMsg(['******* thread: resuming...']);
- Result := Thread.VFSAskPassword_Result;
- Thread.VFSCallbackCancelled := Result = False;
- Exit;
- end;
-
- DebugMsg(['!! (ERROR): vfs_ask_password_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']);
- if (Thread <> nil) and (Thread is TOpenDirThread)
- then DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]);
- Result := False;
-end;
-
-
function TOpenDirThread.ChangeDir(Engine: TPanelEngine; Path: string; var SelItem: string; const AutoFallBack: boolean): integer;
procedure GoUp(var NewPath: string);
@@ -2778,13 +2829,13 @@ begin
// AutoFallback loop
if Engine is TVFSEngine
- then Error := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, Self)
+ then Error := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, nil, Self)
else Error := Engine.ChangeDir(APath);
while AutoFallback and (Error <> 0) and (APath <> '/') do begin
GoUp(APath);
if Engine is TVFSEngine
- then Error := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, Self)
+ then Error := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, nil, Self)
else Error := Engine.ChangeDir(APath);
end;
// Going on...
@@ -2803,6 +2854,38 @@ begin
end;
end;
+procedure TOpenDirThread.Execute;
+var tt: TDateTime;
+ xEngine: TVFSEngine;
+begin
+ PrepareExecute;
+ try
+ tt := Now;
+ try
+ if APlugin <> nil then begin
+ xEngine := TVFSEngine.Create(APlugin);
+ xEngine.ParentEngine := AEngine;
+ xEngine.ArchiveMode := True;
+ AEngine.LastHighlightItem := AHighlightItem;
+ xEngine.SavePath := AEngine.Path;
+ // AEngine must be set here since VFSOpenEx callbacks will reference it
+ AEngine := xEngine;
+ VFSOpenResult := (AEngine as TVFSEngine).VFSOpenEx(AFullPath, @vfs_ask_question_callback, @vfs_ask_password_callback, nil, Self);
+ end else VFSOpenResult := 0;
+
+ if (VFSOpenResult = 0) and (not CancelIt) then begin
+ ChDirResult := ChangeDir(AEngine, APath, ASelItem, AAutoFallBack);
+ if (ChDirResult = 0) and (not CancelIt) then
+ ListingResult := AEngine.GetListing(ADirList, ConfShowDotFiles);
+ end;
+ except
+ on E: Exception do DebugMsg(['*** Exception raised in TOpenDirThread.Execute (', E.ClassName, '): ', E.Message]);
+ end;
+ RunningTime := MilliSecondsBetween(tt, Now);
+ finally
+ Finished := True;
+ end;
+end;
(********************************************************************************************************************************)
(********************************************************************************************************************************)