summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--UConfig.pas4
-rw-r--r--UCore.pas244
-rw-r--r--UCoreUtils.pas1
-rw-r--r--UEngines.pas6
-rw-r--r--UGnome.pas316
-rw-r--r--ULibc.pas4
-rw-r--r--UMain.pas50
-rw-r--r--USearch.pas4
-rw-r--r--USetPassword.pas4
-rw-r--r--libgtk_kylix/GTKForms.pas9
-rw-r--r--vfs/UVFSCore.pas32
-rw-r--r--vfs/uVFSprototypes.pas36
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}
diff --git a/UCore.pas b/UCore.pas
index b2ac837..5770ef7 100644
--- a/UCore.pas
+++ b/UCore.pas
@@ -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
diff --git a/UGnome.pas b/UGnome.pas
index b4070d2..c843e84 100644
--- a/UGnome.pas
+++ b/UGnome.pas
@@ -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;
diff --git a/ULibc.pas b/ULibc.pas
index ba60e47..a01b573 100644
--- a/ULibc.pas
+++ b/ULibc.pas
@@ -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
diff --git a/UMain.pas b/UMain.pas
index 89a5b2c..c01b01f 100644
--- a/UMain.pas
+++ b/UMain.pas
@@ -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.