diff options
| -rw-r--r-- | UConfig.pas | 4 | ||||
| -rw-r--r-- | UCore.pas | 244 | ||||
| -rw-r--r-- | UCoreUtils.pas | 1 | ||||
| -rw-r--r-- | UEngines.pas | 6 | ||||
| -rw-r--r-- | UGnome.pas | 316 | ||||
| -rw-r--r-- | ULibc.pas | 4 | ||||
| -rw-r--r-- | UMain.pas | 50 | ||||
| -rw-r--r-- | USearch.pas | 4 | ||||
| -rw-r--r-- | USetPassword.pas | 4 | ||||
| -rw-r--r-- | libgtk_kylix/GTKForms.pas | 9 | ||||
| -rw-r--r-- | vfs/UVFSCore.pas | 32 | ||||
| -rw-r--r-- | vfs/uVFSprototypes.pas | 36 |
12 files changed, 614 insertions, 96 deletions
diff --git a/UConfig.pas b/UConfig.pas index 9da4f91..6da4faa 100644 --- a/UConfig.pas +++ b/UConfig.pas @@ -25,8 +25,8 @@ uses Classes, ULocale; resourcestring ConstAppTitle = 'Tux Commander'; - ConstAboutVersion = '0.6.52-dev'; - ConstAboutBuildDate = '2008-10-05'; + ConstAboutVersion = '0.6.53-dev'; + ConstAboutBuildDate = '2008-10-12'; {$IFDEF FPC} {$INCLUDE fpcver.inc} @@ -20,11 +20,10 @@ unit UCore; interface -uses glib2, SyncObjs, Classes, GTKForms, GTKView, ULibc, UEngines, UCoreUtils, UProgress, UVFSCore; +uses glib2, SyncObjs, Classes, GTKForms, GTKView, ULibc, UEngines, UCoreUtils, UProgress, UVFSCore, uVFSprototypes; function FillPanel(List: TList; ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: boolean): boolean; -function ChangeDir(Engine: TPanelEngine; Path: string; var SelItem: string; const AutoFallBack: boolean): integer; function MakeDirectory(ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: boolean; NewDir: string): boolean; procedure FindNextSelected(ListView: TGTKListView; DataList: TList; var Item1, Item2: string); procedure UnselectAll(ListView: TGTKListView; DataList: TList); @@ -120,6 +119,9 @@ type TWorkerThread = class(TThread) end; TOpenDirThread = class(TThread) + private + FThreadID: __pthread_t; + function ChangeDir(Engine: TPanelEngine; Path: string; var SelItem: string; const AutoFallBack: boolean): integer; protected procedure Execute; override; public @@ -135,8 +137,27 @@ type TWorkerThread = class(TThread) 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; + constructor Create; -{ destructor Destroy; override; } + destructor Destroy; override; end; @@ -190,6 +211,11 @@ procedure CleanTempDirs; procedure DebugWriteListSL(List: TList); procedure DebugWriteList(List: TList); +{$IFDEF KYLIX} +const INFINITE = Cardinal(-1); +{$ENDIF} + + var LeftLocalEngine, RightLocalEngine: TPanelEngine; LeftPanelData, RightPanelData, AssocList, MounterList, ConnectionMgrList: TList; CommandLineHistory, Bookmarks: TStringList; @@ -204,8 +230,7 @@ var LeftLocalEngine, RightLocalEngine: TPanelEngine; implementation (********************************************************************************************************************************) uses SysUtils, DateUtils, StrUtils, UConfig, UDirDelete, UOverwrite, ULocale, - UNewDir, UFileAssoc, USymlink, UCoreClasses, ULogin, uVFSprototypes, - URemoteWait, UMain; + UNewDir, UFileAssoc, USymlink, UCoreClasses, ULogin, URemoteWait, UMain, UGnome; @@ -393,58 +418,6 @@ begin end; (********************************************************************************************************************************) -function ChangeDir(Engine: TPanelEngine; Path: string; var SelItem: string; const AutoFallBack: boolean): integer; - - procedure GoUp(var NewPath: string); - var x: integer; - begin - if NewPath = PathDelim then Exit; - NewPath := ExcludeTrailingPathDelimiter(NewPath); - if Length(Trim(NewPath)) < 2 then Exit; - x := PosEnd(PathDelim, NewPath); - SelItem := Copy(NewPath, x + 1, Length(NewPath) - x); - NewPath := Copy(NewPath, 1, x); - NewPath := IncludeTrailingPathDelimiter(NewPath); - end; - -var APath: string; - Error : integer; -begin - try - APath := Engine.Path; - if Path = '..' then GoUp(APath) - else begin - APath := IncludeTrailingPathDelimiter(APath); - Path := IncludeTrailingPathDelimiter(Path); - if (Length(Path) > 0) and (Path[1] <> '/') - then APath := APath + Path - else APath := Path; - APath := IncludeTrailingPathDelimiter(APath); - end; - - // AutoFallback loop - Error := Engine.ChangeDir(APath); - while AutoFallback and (Error <> 0) and (APath <> '/') do begin - GoUp(APath); - Error := Engine.ChangeDir(APath); - end; - // Going on... - if Error <> 0 then begin - Result := Error; - DebugMsg(['*** UCore.ChangeDir: error during Engine.ChangeDir: ', GetErrorString(Error)]); - Exit; - end; - Engine.Path := APath; - Result := 0; - except - on E: Exception do begin - DebugMsg(['*** Exception raised in UCore.ChangeDir (', E.ClassName, '): ', E.Message]); - Result := 1; - end; - end; -end; - -(********************************************************************************************************************************) function MakeDirectory(ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: boolean; NewDir: string): boolean; var Error: integer; begin @@ -518,7 +491,7 @@ begin if IsDir and (not IsLnk) then Engine.FillDirFiles(CurrPath + String(FName), AList, 1) else AList.Add(Engine.GetFileInfoSL(CurrPath + String(FName))); - if Engine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']); + if Engine.ChangeDir(CurrPath) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']); Engine.ExplicitChDir('/'); SetProgress1Params(AList.Count); @@ -566,7 +539,7 @@ begin for i := AList.Count - 1 downto 0 do FreeDataItem(PDataItemSL(AList[i])); AList.Clear; AList.Free; - if Engine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']); + if Engine.ChangeDir(CurrPath) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']); end; SenderThread.FDoneThread := True; end; @@ -1072,8 +1045,8 @@ begin end; end; -{ if DestEngine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour may occur.']); - if SrcEngine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour may occur.']); } +{ if DestEngine.ChangeDir(CurrPath) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour may occur.']); + if SrcEngine.ChangeDir(CurrPath) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour may occur.']); } // DebugWriteListSL(List); __chdir('/'); @@ -1139,9 +1112,9 @@ begin for i := List.Count - 1 downto 0 do FreeDataItem(PDataItemSL(List[i])); List.Clear; List.Free; - if DestEngine.ChangeDir(SaveDestPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']); + if DestEngine.ChangeDir(SaveDestPath) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']); if SaveSrcPath <> '' then CurrPath := SaveSrcPath; - if SrcEngine.ChangeDir(CurrPath, False) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']); + if SrcEngine.ChangeDir(CurrPath) <> 0 then DebugMsg(['*** WARNING: Cannot change to the origin location, strange behaviour might occur.']); end; SenderThread.FDoneThread := True; DebugMsg(['(II) CopyFilesWorker: finished']); @@ -2639,11 +2612,29 @@ begin APlugin := nil; xEngine := nil; Password := ''; + VFSCallbackEvent := TSimpleEvent.Create; + VFSAskQuestion_Display := False; + VFSAskPassword_Display := 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; try // Writeln('execute.'); tt := Now; @@ -2686,16 +2677,127 @@ begin end; } end; -{ destructor TOpenDirThread.Destroy; +(********************************************************************************************************************************) +procedure vfs_ask_question_callback(const AMessage: PChar; const Choices: PPChar; choice: PInteger; user_data: Pointer); cdecl; +var Thread: TOpenDirThread; begin - if (APlugin <> nil) and (xEngine <> nil) then - try - xEngine.Free; - except - on E: Exception do DebugMsg(['*** TOpenDirThread.Destroy -Exception: ', E.Message]); + if pthread_self = Application.ThreadID then begin + DebugMsg(['!! (WARNING): vfs_ask_question_callback called from the main thread, expected spawn from a OpenDirThread']); + HandleVFSAskQuestionCallback(nil, AMessage, Choices, choice); + Exit; + end; + + Thread := user_data; + 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...']); + 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 + 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(nil, AMessage, default_user, default_domain, flags, username, password, anonymous, domain, password_save); + Exit; + end; + + Thread := user_data; + 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; + 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); + var x: integer; + begin + if NewPath = PathDelim then Exit; + NewPath := ExcludeTrailingPathDelimiter(NewPath); + if Length(Trim(NewPath)) < 2 then Exit; + x := PosEnd(PathDelim, NewPath); + SelItem := Copy(NewPath, x + 1, Length(NewPath) - x); + NewPath := Copy(NewPath, 1, x); + NewPath := IncludeTrailingPathDelimiter(NewPath); + end; + +var APath: string; + Error : integer; +begin + try + APath := Engine.Path; + if Path = '..' then GoUp(APath) + else begin + APath := IncludeTrailingPathDelimiter(APath); + Path := IncludeTrailingPathDelimiter(Path); + if (Length(Path) > 0) and (Path[1] <> '/') + then APath := APath + Path + else APath := Path; + APath := IncludeTrailingPathDelimiter(APath); end; - inherited Destroy; -end; } + + // AutoFallback loop + if Engine is TVFSEngine + then Error := (Engine as TVFSEngine).ChangeDirEx(APath, @vfs_ask_question_callback, @vfs_ask_password_callback, 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) + else Error := Engine.ChangeDir(APath); + end; + // Going on... + if Error <> 0 then begin + Result := Error; + DebugMsg(['*** UCore.ChangeDir: error during Engine.ChangeDir: ', GetErrorString(Error)]); + Exit; + end; + Engine.Path := APath; + Result := 0; + except + on E: Exception do begin + DebugMsg(['*** Exception raised in UCore.ChangeDir (', E.ClassName, '): ', E.Message]); + Result := 1; + end; + end; +end; + (********************************************************************************************************************************) (********************************************************************************************************************************) diff --git a/UCoreUtils.pas b/UCoreUtils.pas index 9a4d2d5..87232d7 100644 --- a/UCoreUtils.pas +++ b/UCoreUtils.pas @@ -31,6 +31,7 @@ type TIntArray = array[0..1023] of LongWord; TOpenStringArray = array of string; + TOpenPCharArray = array of PChar; THash_MD5 = class private diff --git a/UEngines.pas b/UEngines.pas index 5121947..8e8a0b8 100644 --- a/UEngines.pas +++ b/UEngines.pas @@ -83,7 +83,7 @@ type destructor Destroy; override; function GetListing(var List: TList; const AddDotFiles: boolean): integer; overload; virtual; abstract; // Returns errorcode function GetListing(var List: TList; const AddDotFiles: boolean; APath: string): integer; overload; virtual; abstract; // Returns errorcode - function ChangeDir(const NewPath: string; const ShowProgress: boolean = True): integer; virtual; abstract; // Returns errorcode + function ChangeDir(const NewPath: string): integer; virtual; abstract; // Returns errorcode function ExplicitChDir(const NewPath: string): integer; virtual; abstract; // Returns errorcode function GetFileSystemSize: Int64; overload; virtual; abstract; function GetFileSystemSize(const APath: string): Int64; overload; virtual; abstract; @@ -136,7 +136,7 @@ type destructor Destroy; override; function GetListing(var List: TList; const AddDotFiles: boolean): integer; override; function GetListing(var List: TList; const AddDotFiles: boolean; APath: string): integer; override; - function ChangeDir(const NewPath: string; const ShowProgress: boolean = True): integer; override; + function ChangeDir(const NewPath: string): integer; override; function ExplicitChDir(const NewPath: string): integer; override; function GetFileSystemSize: Int64; override; function GetFileSystemSize(const APath: string): Int64; override; @@ -360,7 +360,7 @@ begin Result := GetListing(List, AddDotFiles, FPath); end; -function TLocalTreeEngine.ChangeDir(const NewPath: string; const ShowProgress: boolean = True): integer; +function TLocalTreeEngine.ChangeDir(const NewPath: string): integer; var APath: string; Handle : PDIR; begin @@ -22,7 +22,8 @@ unit UGnome; interface uses glib2, gdk2, gdk2pixbuf, gtk2, Classes, ULibc, - GTKForms, GTKControls, GTKStdCtrls, GTKExtCtrls, GTKClasses, GTKDialogs, GTKUtils, GTKConsts; + GTKForms, GTKControls, GTKStdCtrls, GTKExtCtrls, GTKClasses, GTKDialogs, GTKUtils, GTKConsts, + uVFSprototypes; type TGnomeColorButton = class(TGTKButton) private @@ -147,6 +148,22 @@ function MessageBoxShowOnce(const Text: string; const DontShowAgainText: string; Buttons: TMessageButtons = [mbOK]; Style: TMessageStyle = mbInfo; Default: TMessageButton = mbNone; Escape: TMessageButton = mbNone): TMessageButton; +procedure HandleVFSAskQuestionCallback(DialogParent: PGtkWidget; + const AMessage: PChar; + const Choices: PPChar; + choice: PInteger); + +function HandleVFSAskPasswordCallback(DialogParent: PGtkWidget; + const AMessage: PChar; + const default_user: PChar; + const default_domain: PChar; + flags: TVFSAskPasswordFlags; + username: PPChar; + password: PPChar; + anonymous: PInteger; + domain: PPChar; + password_save: PVFSPasswordSave): LongBool; + type PGnomeColorPicker = PGtkWidget; @@ -177,9 +194,20 @@ var libGlib2Handle, libGtk2Handle, libGnome2Handle, libGnomeUI2Handle: Pointer; gnome_date_edit_new: function (the_time: time_t; show_time, use_24_format: gboolean): PGtkWidget; cdecl; gnome_date_edit_set_time: procedure (gde: PGnomeDateEdit; the_time: time_t); cdecl; gnome_date_edit_get_time: function (gde: PGnomeDateEdit): time_t; cdecl; + gtk_event_box_set_visible_window: procedure (event_box: PGtkEventBox; visible_window: gboolean); cdecl; gtk_icon_size_lookup_for_settings: function (settings: PGtkSettings; size: TGtkIconSize; width, height: Pgint): gboolean; cdecl; g_filename_display_name: function (const filename: PChar): PChar; cdecl; +{$IFDEF KYLIX} + gtk_message_dialog_new_with_markup: function (parent:PGtkWindow; flags:TGtkDialogFlags; + _type:TGtkMessageType; buttons:TGtkButtonsType; + message_format:Pgchar; const format1: PChar; const format2: PChar):PGtkWidget; cdecl; +{$ELSE} + gtk_message_dialog_new_with_markup: function (parent:PGtkWindow; flags:TGtkDialogFlags; + _type:TGtkMessageType; buttons:TGtkButtonsType; + message_format:Pgchar):PGtkWidget; varargs; cdecl; +{$ENDIF} + @@ -187,7 +215,7 @@ procedure LoadGnomeLibs; implementation -uses SysUtils, DateUtils, UConfig, UCoreUtils, ULocale; +uses SysUtils, DateUtils, UConfig, UCoreUtils, ULocale, UFileAssoc; (********************************************************************************************************************************) @@ -1194,6 +1222,289 @@ begin gtk_widget_destroy(Dialog); end; + +(********************************************************************************************************************************) +(********************************************************************************************************************************) +// The following code has been stolen from gtkmountoperation.c +// Copyright (C) Christian Kellner <gicmo@gnome.org> +procedure HandleVFSAskQuestionCallback(DialogParent: PGtkWidget; + const AMessage: PChar; + const Choices: PPChar; + choice: PInteger); +var dialog: PGtkWidget; + secondary: PChar; + primary: PChar; + count, len: integer; +begin + len := 0; + secondary := nil; + if (AMessage = nil) or (choices = nil) or Application.Terminated then Exit; + + primary := strstr (AMessage, #10); + if primary <> nil then begin + secondary := primary + 1; + primary := g_strndup (AMessage, primary - AMessage); + end; + + if (primary <> nil) and (secondary <> nil) and (@gtk_message_dialog_new_with_markup <> nil) + then dialog := gtk_message_dialog_new_with_markup (PGtkWindow(DialogParent), 0, + GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, + '<span size="large" weight="ultrabold">%s</span>'#10'%s', + primary, secondary) + else dialog := gtk_message_dialog_new (PGtkWindow(DialogParent), 0, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, '%s', AMessage); + if primary <> nil then g_free (primary); + + gtk_window_set_title (PGtkWindow(dialog), 'VFS Question'); + + // First count the items in the list then add the buttons in reverse order + while (TOpenPCharArray(choices)[len] <> nil) + do Inc(len); + + for count := len - 1 downto 0 do + gtk_dialog_add_button (PGtkDialog(dialog), TOpenPCharArray(choices)[count], count); + + choice^ := gtk_dialog_run(PGtkDialog(Dialog)); + + gtk_widget_destroy (PGtkWidget(Dialog)); +end; + +(********************************************************************************************************************************) +type PVFSAskPasswordCallbackPriv = ^TVFSAskPasswordCallbackPriv; + TVFSAskPasswordCallbackPriv = record + dialog: PGtkDialog; + entry_container: PGtkWidget; + choice_anon, choice_user: PGtkWidget; + username_entry, domain_entry, password_entry: PGtkEntry; + end; + +function entry_has_input(entry_widget: PGtkEntry): boolean; +var text: PChar; +begin + if entry_widget = nil then Result := True + else begin + text := gtk_entry_get_text (entry_widget); + Result := (text <> nil) and (strlen(text) > 0); + end; +end; + +function pw_dialog_input_is_valid (priv: PVFSAskPasswordCallbackPriv): boolean; +begin + Result := entry_has_input(priv^.username_entry) and + entry_has_input(priv^.domain_entry) and + entry_has_input(priv^.password_entry); +end; + +procedure pw_dialog_verify_input(editable: PGtkEditable; priv: PVFSAskPasswordCallbackPriv); cdecl; +begin + gtk_dialog_set_response_sensitive (GTK_DIALOG (priv^.dialog), + GTK_RESPONSE_OK, + pw_dialog_input_is_valid(priv)); +end; + +procedure pw_dialog_anonymous_toggled(widget: PGtkWidget; priv: PVFSAskPasswordCallbackPriv); cdecl; +var is_valid, anonymous: boolean; +begin + anonymous := widget = priv^.choice_anon; + if anonymous then is_valid := True + else is_valid := pw_dialog_input_is_valid(priv); + gtk_widget_set_sensitive (priv^.entry_container, not anonymous); + gtk_dialog_set_response_sensitive(GTK_DIALOG (priv^.dialog), GTK_RESPONSE_OK, is_valid); +end; + +procedure pw_dialog_cycle_focus (widget: PGtkWidget; priv: PVFSAskPasswordCallbackPriv); cdecl; +var next_widget: PGtkWidget; +begin + next_widget := nil; + if widget = PGtkWidget(priv^.username_entry) then begin + if priv^.domain_entry <> nil then next_widget := PGtkWidget(priv^.domain_entry) + else if priv^.password_entry <> nil then next_widget := PGtkWidget(priv^.password_entry); + end else if (widget = PGtkWidget(priv^.domain_entry)) and (priv^.password_entry <> nil) + then next_widget := PGtkWidget(priv^.password_entry); + if next_widget <> nil then gtk_widget_grab_focus(next_widget) + else if pw_dialog_input_is_valid(priv) + then gtk_window_activate_default(GTK_WINDOW(priv^.dialog)); +end; + + +// The following code has been stolen from gtkmountoperation.c +// Copyright (C) Christian Kellner <gicmo@gnome.org> +function HandleVFSAskPasswordCallback(DialogParent: PGtkWidget; + const AMessage: PChar; + const default_user: PChar; + const default_domain: PChar; + flags: TVFSAskPasswordFlags; + username: PPChar; + password: PPChar; + anonymous: PInteger; + domain: PPChar; + password_save: PVFSPasswordSave): LongBool; + +function table_add_entry(table: PGtkWidget; row: integer; const label_text: PChar; const value: PChar; user_data: PVFSAskPasswordCallbackPriv): PGtkEntry; +var entry: PGtkEntry; + alabel: PGtkWidget; +begin + alabel := gtk_label_new_with_mnemonic (label_text); + gtk_misc_set_alignment (GTK_MISC (alabel), 0.0, 0.5); + entry := PGtkEntry(gtk_entry_new ()); + if value <> nil then gtk_entry_set_text (PGtkEntry(entry), value); + gtk_table_attach (GTK_TABLE (table), alabel, 0, 1, row, row + 1, GTK_FILL, GTK_EXPAND or GTK_FILL, 0, 0); + gtk_table_attach_defaults (GTK_TABLE (table), PGtkWidget(entry), 1, 2, row, row + 1); + gtk_label_set_mnemonic_widget (PGtkLabel(alabel), PGtkWidget(entry)); + g_signal_connect (entry, 'changed', G_CALLBACK (@pw_dialog_verify_input), user_data); + g_signal_connect (entry, 'activate', G_CALLBACK (@pw_dialog_cycle_focus), user_data); + Result := entry; +end; + +var widget: PGtkWidget; + window: PGtkWindow; + hbox, main_vbox, vbox, icon: PGtkWidget; + table: PGtkWidget; + message_label: PGtkWidget; + can_anonymous: boolean; + Rows: integer; + anon_box: PGtkWidget; + radio_forget, radio_session, radio_remember: PGtkWidget; + group: PGSList; + remember_box: PGtkWidget; + priv: TVFSAskPasswordCallbackPriv; +begin + FillChar(priv, sizeof(priv), 0); + radio_forget := nil; + radio_session := nil; + radio_remember := nil; + + widget := gtk_dialog_new (); + priv.dialog := PGtkDialog(widget); + window := PGtkWindow(widget); + gtk_window_set_title (window, 'VFS Question'); + + // Set the dialog up with HIG properties + gtk_dialog_set_has_separator (priv.dialog, False); + gtk_container_set_border_width (GTK_CONTAINER (priv.dialog), 5); + gtk_box_set_spacing (GTK_BOX (priv.dialog^.vbox), 2); // 2 * 5 + 2 = 12 + gtk_container_set_border_width (GTK_CONTAINER (priv.dialog^.action_area), 5); + gtk_box_set_spacing (GTK_BOX (priv.dialog^.action_area), 6); + + gtk_window_set_resizable (window, False); +// gtk_window_set_icon_name (window, 'gtk-dialog-authentication'); + + gtk_dialog_add_buttons (priv.dialog, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + 'Co_nnect', GTK_RESPONSE_OK, + nil); + gtk_dialog_set_default_response (priv.dialog, GTK_RESPONSE_OK); + + // Build contents + hbox := gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + gtk_box_pack_start (GTK_BOX (priv.dialog^.vbox), hbox, TRUE, TRUE, 0); + + if gtk_style_lookup_icon_set(gtk_widget_get_style(PGtkWidget(priv.dialog)), 'gtk-dialog-authentication') <> nil + then icon := gtk_image_new_from_stock('gtk-dialog-authentication', GTK_ICON_SIZE_DIALOG) + else icon := gtk_image_new_from_pixbuf(StockLock48.FPixbuf); + + gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0); + gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0); + + main_vbox := gtk_vbox_new (FALSE, 18); + gtk_box_pack_start (GTK_BOX (hbox), main_vbox, TRUE, TRUE, 0); + + message_label := gtk_label_new (AMessage); + gtk_misc_set_alignment (GTK_MISC (message_label), 0.0, 0.5); + gtk_label_set_line_wrap (GTK_LABEL (message_label), TRUE); + gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (message_label), FALSE, FALSE, 0); + + vbox := gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0); + + can_anonymous := (Integer(flags) and Integer(VFS_ASK_PASSWORD_ANONYMOUS_SUPPORTED)) = Integer(VFS_ASK_PASSWORD_ANONYMOUS_SUPPORTED); + + if can_anonymous then begin + anon_box := gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), anon_box, FALSE, FALSE, 0); + + priv.choice_anon := gtk_radio_button_new_with_mnemonic (nil, 'Connect _anonymously'); + gtk_box_pack_start (GTK_BOX (anon_box), priv.choice_anon, FALSE, FALSE, 0); + g_signal_connect (priv.choice_anon, 'toggled', G_CALLBACK (@pw_dialog_anonymous_toggled), @priv); + + group := gtk_radio_button_get_group (GTK_RADIO_BUTTON (priv.choice_anon)); + priv.choice_user := gtk_radio_button_new_with_mnemonic (group, 'Connect as u_ser:'); + gtk_box_pack_start (GTK_BOX (anon_box), priv.choice_user, FALSE, FALSE, 0); + g_signal_connect (priv.choice_user, 'toggled', G_CALLBACK (@pw_dialog_anonymous_toggled), @priv); + end; + + rows := 0; + if (Integer(flags) and Integer(VFS_ASK_PASSWORD_NEED_PASSWORD)) = Integer(VFS_ASK_PASSWORD_NEED_PASSWORD) then Inc(rows); + if (Integer(flags) and Integer(VFS_ASK_PASSWORD_NEED_USERNAME)) = Integer(VFS_ASK_PASSWORD_NEED_USERNAME) then Inc(rows); + if (Integer(flags) and Integer(VFS_ASK_PASSWORD_NEED_DOMAIN)) = Integer(VFS_ASK_PASSWORD_NEED_DOMAIN) then Inc(rows); + + // The table that holds the entries + priv.entry_container := gtk_alignment_new (0.0, 0.0, 1.0, 1.0); + + if can_anonymous and Application.GTKVersion_2_4_0_Up then g_object_set (priv.entry_container, 'left-padding', 12, nil); + gtk_box_pack_start (GTK_BOX (vbox), priv.entry_container, FALSE, FALSE, 0); + + table := gtk_table_new (rows, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 12); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_container_add (GTK_CONTAINER (priv.entry_container), table); + + Rows := 0; + if (Integer(flags) and Integer(VFS_ASK_PASSWORD_NEED_USERNAME)) = Integer(VFS_ASK_PASSWORD_NEED_USERNAME) then begin + priv.username_entry := table_add_entry (table, rows, '_Username:', default_user, @priv); + Inc(Rows); + end; + if (Integer(flags) and Integer(VFS_ASK_PASSWORD_NEED_DOMAIN)) = Integer(VFS_ASK_PASSWORD_NEED_DOMAIN) then begin + priv.domain_entry := table_add_entry (table, rows, '_Domain:', default_domain, @priv); + Inc(Rows); + end; + if (Integer(flags) and Integer(VFS_ASK_PASSWORD_NEED_PASSWORD)) = Integer(VFS_ASK_PASSWORD_NEED_PASSWORD) then begin + priv.password_entry := table_add_entry (table, rows, '_Password:', nil, @priv); +// Inc(Rows); + gtk_entry_set_visibility (GTK_ENTRY (priv.password_entry), FALSE); + end; + + if (Integer(flags) and Integer(VFS_ASK_PASSWORD_SAVING_SUPPORTED)) = Integer(VFS_ASK_PASSWORD_SAVING_SUPPORTED) then begin + remember_box := gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), remember_box, FALSE, FALSE, 0); + + radio_forget := gtk_radio_button_new_with_mnemonic (nil, 'Forget password _immediately'); + gtk_box_pack_start (GTK_BOX (remember_box), radio_forget, FALSE, FALSE, 0); + + group := gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_forget)); + radio_session := gtk_radio_button_new_with_mnemonic (group, 'Remember password until you _logout'); + gtk_box_pack_start (GTK_BOX (remember_box), radio_session, FALSE, FALSE, 0); + + group := gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_session)); + radio_remember := gtk_radio_button_new_with_mnemonic (group, 'Remember _forever'); + gtk_box_pack_start (GTK_BOX (remember_box), radio_remember, FALSE, FALSE, 0); + end; + + // The anonymous option will be active by default, ensure the toggled signal is emitted for it. + if can_anonymous then gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (priv.choice_anon)) else + if not pw_dialog_input_is_valid(@priv) then gtk_dialog_set_response_sensitive(priv.dialog, GTK_RESPONSE_OK, False); + + gtk_widget_show_all (GTK_WIDGET (priv.dialog)); + gtk_window_set_transient_for (window, PGtkWindow(DialogParent)); + + Result := gtk_dialog_run(PGtkDialog(priv.dialog)) = GTK_RESPONSE_OK; + if Result then begin + if (priv.username_entry <> nil) and (username <> nil) then username^ := g_strdup(gtk_entry_get_text(priv.username_entry)); + if (priv.domain_entry <> nil) and (domain <> nil) then domain^ := g_strdup(gtk_entry_get_text(priv.domain_entry)); + if (priv.password_entry <> nil) and (password <> nil) then password^ := g_strdup(gtk_entry_get_text(priv.password_entry)); + if (priv.choice_anon <> nil) and (anonymous <> nil) then anonymous^ := Ord(gtk_toggle_button_get_active(PGtkToggleButton(priv.choice_anon))); + if (password_save <> nil) and (radio_forget <> nil) and (radio_session <> nil) and (radio_remember <> nil) then begin + if gtk_toggle_button_get_active(PGtkToggleButton(radio_forget)) then password_save^ := VFS_PASSWORD_SAVE_NEVER else + if gtk_toggle_button_get_active(PGtkToggleButton(radio_session)) then password_save^ := VFS_PASSWORD_SAVE_FOR_SESSION else + if gtk_toggle_button_get_active(PGtkToggleButton(radio_remember)) then password_save^ := VFS_PASSWORD_SAVE_PERMANENTLY; + end; + end; + gtk_widget_destroy (PGtkWidget(priv.dialog)); +end; + + (********************************************************************************************************************************) (********************************************************************************************************************************) (********************************************************************************************************************************) @@ -1245,6 +1556,7 @@ begin if libGtk2Handle <> nil then begin @gtk_event_box_set_visible_window := dlsym(libGtk2Handle, 'gtk_event_box_set_visible_window'); @gtk_icon_size_lookup_for_settings := dlsym(libGtk2Handle, 'gtk_icon_size_lookup_for_settings'); + @gtk_message_dialog_new_with_markup := dlsym(libGtk2Handle, 'gtk_message_dialog_new_with_markup'); DebugMsg(['libgtk-x11-2.0.so loaded, @gtk_event_box_set_visible_window = 0x', IntToHex(QWORD(@gtk_event_box_set_visible_window), 8), ', @gtk_icon_size_lookup_for_settings = 0x', IntToHex(QWORD(@gtk_icon_size_lookup_for_settings), 8)]); end; @@ -24,6 +24,7 @@ interface const GLIBC_LIB = 'libc.so.6'; DL_LIB = 'libdl.so.2'; + PTHREAD_LIB = 'libpthread.so.0'; type {$IFDEF KYLIX} @@ -850,6 +851,9 @@ function access(pathname: PChar; mode: Longint): Longint; cdecl; external GLIBC_ function euidaccess(pathname: PChar; mode: Longint): Longint; cdecl; external GLIBC_LIB name 'euidaccess'; +type pthread_t = {$ifdef cpu64}QWord{$else}DWord{$endif}; + +function pthread_self: pthread_t; cdecl; external PTHREAD_LIB name 'pthread_self'; implementation @@ -1743,21 +1743,40 @@ var ListView: TGTKListView; OpenDirThread.Resume; tt := Now; b := False; - FRemoteWait := nil; + FRemoteWait := TFRemoteWait.Create(Application); + FRemoteWait.ParentForm := FMain; repeat Sleep(ConstInternalProgressTimer); // DebugMsg([' (II) TFMain.ChangingDir: sleep.']); Application.ProcessMessages; if not b and (MilliSecondsBetween(tt, Now) >= ConstRemoteWaitDialogDelay) then begin - FRemoteWait := TFRemoteWait.Create(Application); - FRemoteWait.ParentForm := FMain; FRemoteWait.ShowModal; b := True; end; - if Assigned(FRemoteWait) then - if FRemoteWait.Cancelled then OpenDirThread.CancelIt := True; + if FRemoteWait.Cancelled then OpenDirThread.CancelIt := True; + if OpenDirThread.VFSAskQuestion_Display then begin + OpenDirThread.VFSAskQuestion_Display := False; + DebugMsg(['Main thread: displaying question dialog']); + HandleVFSAskQuestionCallback(FRemoteWait.FWidget, OpenDirThread.VFSAskQuestion_Message, OpenDirThread.VFSAskQuestion_Choices, OpenDirThread.VFSAskQuestion_Choice); + OpenDirThread.VFSCallbackEvent.SetEvent; + end; + if OpenDirThread.VFSAskPassword_Display then begin + OpenDirThread.VFSAskPassword_Display := False; + DebugMsg(['Main thread: displaying password prompt']); + OpenDirThread.VFSAskPassword_Result := HandleVFSAskPasswordCallback(FRemoteWait.FWidget, + OpenDirThread.VFSAskPassword_Message, + OpenDirThread.VFSAskPassword_default_user, + OpenDirThread.VFSAskPassword_default_domain, + OpenDirThread.VFSAskPassword_flags, + OpenDirThread.VFSAskPassword_username, + OpenDirThread.VFSAskPassword_password, + OpenDirThread.VFSAskPassword_anonymous, + OpenDirThread.VFSAskPassword_domain, + OpenDirThread.VFSAskPassword_password_save); + OpenDirThread.VFSCallbackEvent.SetEvent; + end; until OpenDirThread.Finished; - if FRemoteWait <> nil then FRemoteWait.Free; + FRemoteWait.Free; DebugMsg(['(II) TFMain.ChangingDir: end thread, running time = ', OpenDirThread.RunningTime, 'ms']); except on E: Exception do DebugMsg(['*** Exception raised in TFMain.ChangingDir:DoThread (', E.ClassName, '): ', E.Message]); @@ -5246,7 +5265,7 @@ begin FTestPlugin := TFTestPlugin.Create(Self); if (FTestPlugin.Run = mbOK) and (PluginList.Count > 0) then begin Engine := TVFSEngine.Create(PluginList[FTestPlugin.PluginOptionMenu.ItemIndex]); - if not Engine.VFSOpenURI(FTestPlugin.CommandEntry.Text) then begin + if not Engine.VFSOpenURI(FTestPlugin.CommandEntry.Text, nil, nil, nil) then begin Application.MessageBox(LANGCouldntOpenURI, [mbOK], mbError, mbOK, mbOK); Exit; end; @@ -5939,6 +5958,21 @@ end; (********************************************************************************************************************************) (********************************************************************************************************************************) +procedure vfs_ask_question_callback(const AMessage: PChar; const Choices: PPChar; choice: PInteger; user_data: Pointer); cdecl; +begin +// DebugMsg(['******* vfs_ask_question_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16)]); + HandleVFSAskQuestionCallback(user_data, AMessage, Choices, choice); +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; +begin +// DebugMsg(['******* vfs_ask_password_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16)]); + Result := HandleVFSAskPasswordCallback(user_data, AMessage, default_user, default_domain, flags, username, password, anonymous, domain, password_save); +// DebugMsg(['after vfs_ask_password_callback: password = ', password^]); +end; + procedure TFMain.miOpenConnectionClick(Sender: TObject); var Engine: TVFSEngine; SourceEngine: TPanelEngine; @@ -6004,7 +6038,7 @@ begin FRemoteWait.ParentForm := FConnectionManager; FRemoteWait.ShowModal; Application.ProcessMessages; - b := Engine.VFSOpenURI(UTF8ToStr(ConnInfo.URI)); + b := Engine.VFSOpenURI(UTF8ToStr(ConnInfo.URI), @vfs_ask_question_callback, @vfs_ask_password_callback, FRemoteWait.FWidget); FRemoteWait.Free; if not b then begin Application.MessageBox(LANGCouldntOpenURI, [mbOK], mbError, mbOK, mbOK); diff --git a/USearch.pas b/USearch.pas index d09c330..74c45d8 100644 --- a/USearch.pas +++ b/USearch.pas @@ -787,7 +787,7 @@ begin Processing := False; Stop := False; if FileList.Items.Count > 0 then FileList.SetFocus; - if Engine.ChangeDir(OldDir, False) <> 0 then DebugMsg(['DoSearch: cannot change back to saved directory']); + if Engine.ChangeDir(OldDir) <> 0 then DebugMsg(['DoSearch: cannot change back to saved directory']); // Enable the UI Table1.Enabled := True; @@ -873,7 +873,7 @@ begin CurrentDir := StartDir; GUIMutex.Release; - if FEngine.ChangeDir(StartDir, False) <> 0 then Exit; + if FEngine.ChangeDir(StartDir) <> 0 then Exit; LocalList := TList.Create; if FEngine.GetListing(LocalList, True, StartDir) = 0 then begin diff --git a/USetPassword.pas b/USetPassword.pas index b64d079..cdf7fee 100644 --- a/USetPassword.pas +++ b/USetPassword.pas @@ -53,7 +53,9 @@ begin Table := TGTKTable.Create(Self); DialogIcon := TGTKImage.Create(Self); - DialogIcon.CopyFromPixbuf(StockLock48); + if gtk_style_lookup_icon_set(gtk_widget_get_style(FWidget), 'gtk-dialog-authentication') <> nil + then DialogIcon.SetFromStock('gtk-dialog-authentication', isDialog) + else DialogIcon.CopyFromPixbuf(StockLock48); Label1 := TGTKLabel.Create(Self); Label1.Caption := Format('<span size="x-large" weight="bold">%s</span>', [LANGFSetPassword_Label1_Caption]); diff --git a/libgtk_kylix/GTKForms.pas b/libgtk_kylix/GTKForms.pas index 167db27..0f85652 100644 --- a/libgtk_kylix/GTKForms.pas +++ b/libgtk_kylix/GTKForms.pas @@ -1,6 +1,6 @@ (* GTK-Kylix Library: GTKForms - Basic windows (TGTKForm, TGTKDialog), TGTKApplication, TGDKScreen - Version 0.6.26 (last updated 2007-08-19) + Version 0.6.27 (last updated 2008-10-11) Copyright (C) 2007 Tomas Bzatek <tbzatek@users.sourceforge.net> This library is free software; you can redistribute it and/or @@ -31,6 +31,8 @@ uses gtk2, gdk2, glib2, Classes, SysUtils, GTKControls, GTKConsts; type // Some basic types PCharArray = array[0..0] of PChar; + __pthread_t = {$ifdef cpu64}QWord{$else}DWord{$endif}; + TCustomGTKForm = class; (****************************************** TGDKSCREEN **************************************************************************) @@ -178,6 +180,7 @@ type // Some basic types FOnException: TExceptionEvent; FMainForm: TCustomGTKForm; FMainFormSet, FGTK205Up, FGTK220Up, FGTK240Up, FGTK260Up, FGTK280Up: Boolean; + FThreadID: __pthread_t; procedure Quit; protected procedure CreateHandle; @@ -206,6 +209,7 @@ type // Some basic types property GTKVersion_2_4_0_Up: boolean read FGTK240Up; property GTKVersion_2_6_0_Up: boolean read FGTK260Up; property GTKVersion_2_8_0_Up: boolean read FGTK280Up; + property ThreadID: __pthread_t read FThreadID; end; @@ -525,6 +529,7 @@ begin Classes.ApplicationShowException := ShowException; CreateHandle; HookSynchronizeWakeup; + FThreadID := 0; end; procedure my_g_thread_init(vtable:Pointer);cdecl;external 'libgthread-2.0.so' name 'g_thread_init'; @@ -678,12 +683,14 @@ begin end; procedure _exit(status: longint); cdecl; external 'libc.so.6' name '_exit'; +function __pthread_self: __pthread_t; cdecl; external 'libpthread.so.0' name 'pthread_self'; procedure TGTKApplication.Run; begin repeat try // gdk_threads_enter; + FThreadID := __pthread_self; gtk_main; // gdk_threads_leave; except diff --git a/vfs/UVFSCore.pas b/vfs/UVFSCore.pas index 0237121..e884a09 100644 --- a/vfs/UVFSCore.pas +++ b/vfs/UVFSCore.pas @@ -67,6 +67,7 @@ type FVFSGetServices: TVFSGetServices; FVFSSetPassword: TVFSSetPassword; FVFSGetPasswordRequired: TVFSGetPasswordRequired; + FVFSSetCallbacks: TVFSSetCallbacks; public ModuleHandle: Pointer; FullPath: string; // module path @@ -95,14 +96,15 @@ type public ArchiveMode: boolean; constructor Create(SourcePlugin: TVFSPlugin); - function VFSOpenURI(OpenFile: string): boolean; + function VFSOpenURI(URI: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; CallbackData: Pointer): boolean; function VFSOpenEx(OpenFile: string): TVFSResult; function VFSClose: boolean; destructor Destroy; override; function GetListing(var List: TList; const AddDotFiles: boolean): integer; override; function GetListing(var List: TList; const AddDotFiles: boolean; APath: string): integer; override; - function ChangeDir(const NewPath: string; const ShowProgress: boolean = True): integer; override; + function ChangeDir(const NewPath: string): integer; override; + function ChangeDirEx(const NewPath: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; CallbackData: Pointer): integer; function ExplicitChDir(const NewPath: string): integer; override; function GetFileSystemSize: Int64; override; function GetFileSystemSize(const APath: string): Int64; override; @@ -223,6 +225,7 @@ begin @FVFSGetServices := dlsym(ModuleHandle, 'VFSGetServices'); @FVFSSetPassword := dlsym(ModuleHandle, 'VFSSetPassword'); @FVFSGetPasswordRequired := dlsym(ModuleHandle, 'VFSGetPasswordRequired'); + @FVFSSetCallbacks := dlsym(ModuleHandle, 'VFSSetCallbacks'); // Initialize the extensions list SetLength(Extensions, 0); SetLength(Services, 0); @@ -305,11 +308,16 @@ begin end; end; -function TVFSEngine.VFSOpenURI(OpenFile: string): boolean; +function TVFSEngine.VFSOpenURI(URI: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; CallbackData: Pointer): boolean; begin Result := False; - if (FGlobs <> nil) and (@FSourcePlugin.FVFSOpen <> nil) - then Result := FSourcePlugin.FVFSOpen(FGlobs, PChar(OpenFile)) = cVFS_OK; + if (FGlobs <> nil) and (@FSourcePlugin.FVFSOpen <> nil) then begin + if @FSourcePlugin.FVFSSetCallbacks <> nil then + FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, CallbackData); + Result := FSourcePlugin.FVFSOpen(FGlobs, PChar(URI)) = cVFS_OK; + if @FSourcePlugin.FVFSSetCallbacks <> nil then + FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil); + end; end; function TVFSEngine.VFSOpenEx(OpenFile: string): TVFSResult; @@ -489,7 +497,7 @@ begin else Result := '/'; end; -function TVFSEngine.ChangeDir(const NewPath: string; const ShowProgress: boolean = True): integer; +function TVFSEngine.ChangeDir(const NewPath: string): integer; begin DebugMsg(['^^VFS (II): ChangeDir begin']); Result := 0; @@ -502,6 +510,18 @@ begin DebugMsg(['^^VFS (II): ChangeDir end.']); end; +function TVFSEngine.ChangeDirEx(const NewPath: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; CallbackData: Pointer): integer; +begin + if (FGlobs <> nil) and (@FSourcePlugin.FVFSChangeDir <> nil) then begin + if @FSourcePlugin.FVFSSetCallbacks <> nil then + FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, CallbackData); + Result := ChangeDir(NewPath); + if @FSourcePlugin.FVFSSetCallbacks <> nil then + FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil); + end; +end; + + procedure TVFSEngine.SetPath(Value: string); begin ChangeDir(Value); diff --git a/vfs/uVFSprototypes.pas b/vfs/uVFSprototypes.pas index 6959a7b..18830aa 100644 --- a/vfs/uVFSprototypes.pas +++ b/vfs/uVFSprototypes.pas @@ -76,6 +76,17 @@ type // File descriptor for Open, Read, Write, Close, Seek operations TVFSFileDes = Pointer; + TVFSAskPasswordFlags = (VFS_ASK_PASSWORD_NEED_PASSWORD = 1 shl 0, + VFS_ASK_PASSWORD_NEED_USERNAME = 1 shl 1, + VFS_ASK_PASSWORD_NEED_DOMAIN = 1 shl 2, + VFS_ASK_PASSWORD_SAVING_SUPPORTED = 1 shl 3, + VFS_ASK_PASSWORD_ANONYMOUS_SUPPORTED = 1 shl 4); + + PVFSPasswordSave = ^TVFSPasswordSave; + TVFSPasswordSave = (VFS_PASSWORD_SAVE_NEVER, + VFS_PASSWORD_SAVE_FOR_SESSION, + VFS_PASSWORD_SAVE_PERMANENTLY); + TVFSItemType = (vRegular=0, vSymlink=1, vChardev=2, vBlockdev=3, vDirectory=4, vFifo=5, vSock=6, vOther=7); {$IFDEF KYLIX} @@ -245,9 +256,34 @@ type //// pridat typ pluginu - jestli archive nebo protocol - prip. jeste pridat ktery protokoly je to schopno handlovat + + // Return index of the choice selected or negative number when dialog has been cancelled + PVFSAskQuestionCallback = ^TVFSAskQuestionCallback; + TVFSAskQuestionCallback = procedure (const AMessage: PChar; + const Choices: PPChar; + choice: PInteger; + user_data: Pointer); cdecl; + + PVFSAskPasswordCallback = ^TVFSAskPasswordCallback; + TVFSAskPasswordCallback = function (const AMessage: PChar; + const default_user: PChar; + const default_domain: PChar; + flags: TVFSAskPasswordFlags; + username: PPChar; + password: PPChar; + anonymous: PInteger; + domain: PPChar; + password_save: PVFSPasswordSave; + user_data: Pointer): LongBool; cdecl; + + TVFSSetCallbacks = procedure (g: TVFSGlobs; ask_question_callback: PVFSAskQuestionCallback; ask_password_callback: PVFSAskPasswordCallback; Data: Pointer); cdecl; + + // TODO: some function to check the CRC of the archive - it should need also some progress feedback - the processed file and percentage progress +// Prekopat error logging - asi neco na zpusob GError, stringy se budou vracet i z pluginu + implementation end. |
