diff options
| -rw-r--r-- | UConfig.pas | 26 | ||||
| -rw-r--r-- | UConnectionManager.pas | 237 | ||||
| -rw-r--r-- | UCore.pas | 104 | ||||
| -rw-r--r-- | UMain.pas | 116 | ||||
| -rw-r--r-- | UQuickConnect.pas | 349 | ||||
| -rw-r--r-- | libgtk_kylix/GTKStdCtrls.pas | 3 | ||||
| -rw-r--r-- | tuxcmd.dpr | 1 | ||||
| -rw-r--r-- | vfs/UVFSCore.pas | 15 | ||||
| -rw-r--r-- | vfs/uVFSprototypes.pas | 3 |
9 files changed, 725 insertions, 129 deletions
diff --git a/UConfig.pas b/UConfig.pas index ce0ca82..cfd79d3 100644 --- a/UConfig.pas +++ b/UConfig.pas @@ -25,8 +25,8 @@ uses Classes, ULocale; resourcestring ConstAppTitle = 'Tux Commander'; - ConstAboutVersion = '0.6.59-dev'; - ConstAboutBuildDate = '2008-11-16'; + ConstAboutVersion = '0.6.60-dev'; + ConstAboutBuildDate = '2008-11-17'; {$IFDEF FPC} {$INCLUDE fpcver.inc} @@ -114,6 +114,7 @@ var ConfPanelSep, ConfRowHeight, ConfRowHeightReal, ConfNumHistoryItems, ApplicationShuttingDown: boolean; ConfConnMgrActiveItem: integer; ConfConnMgrDoNotSavePasswords, ConfConnMgrDoNotSynchronizeKeyring: boolean; + ConfQuickConnectPluginID: string; @@ -258,6 +259,7 @@ begin ConfRightClickSelect := False; ConfConnMgrDoNotSavePasswords := False; ConfConnMgrDoNotSynchronizeKeyring := False; + ConfQuickConnectPluginID := ''; // Setup default values for colors @@ -915,7 +917,7 @@ procedure ReadConnections; var s: string; IniFile: TMyIniFile; Sections: TStringList; - i: integer; + i, j, k: integer; Item: TConnMgrItem; begin try @@ -942,6 +944,16 @@ begin ConfConnMgrActiveItem := IniFile.ReadInteger('__General', 'ConnMgrActiveItem', ConfConnMgrActiveItem); ConfConnMgrDoNotSavePasswords := IniFile.ReadBool('__General', 'ConnMgrDoNotSavePasswords', ConfConnMgrDoNotSavePasswords); ConfConnMgrDoNotSynchronizeKeyring := IniFile.ReadBool('__General', 'ConnMgrDoNotSynchronizeKeyring', ConfConnMgrDoNotSynchronizeKeyring); + ConfQuickConnectPluginID := IniFile.ReadString('__General', 'QuickConnectPluginID', ConfQuickConnectPluginID); + end else + if Sections[i] = '__QuickConnectHistory' then begin + QuickConnectHistory.Clear; + if not InternalDeleteHistory then begin + j := IniFile.ReadInteger('__QuickConnectHistory', 'NumItems', 0); + if j > 0 then + for k := 0 to j - 1 do + QuickConnectHistory.Add(IniFile.ReadString('__QuickConnectHistory', Format('Item%d', [k]), '')); + end; end else begin Item := TConnMgrItem.Create; with Item do begin @@ -984,6 +996,11 @@ begin IniFile.WriteInteger('__General', 'ConnMgrActiveItem', ConfConnMgrActiveItem); IniFile.WriteBool('__General', 'ConnMgrDoNotSavePasswords', ConfConnMgrDoNotSavePasswords); IniFile.WriteBool('__General', 'ConnMgrDoNotSynchronizeKeyring', ConfConnMgrDoNotSynchronizeKeyring); + IniFile.WriteString('__General', 'QuickConnectPluginID', ConfQuickConnectPluginID); + IniFile.WriteInteger('__QuickConnectHistory', 'NumItems', QuickConnectHistory.Count); + if QuickConnectHistory.Count > 0 then + for i := 0 to QuickConnectHistory.Count - 1 do + IniFile.WriteString('__QuickConnectHistory', Format('Item%d', [i]), QuickConnectHistory[i]); if ConnectionMgrList.Count > 0 then for i := 0 to ConnectionMgrList.Count - 1 do with TConnMgrItem(ConnectionMgrList[i]) do begin @@ -1159,6 +1176,8 @@ initialization CommandLineHistory.CaseSensitive := True; Bookmarks := TStringList.Create; Bookmarks.CaseSensitive := True; + QuickConnectHistory := TStringList.Create; + QuickConnectHistory.CaseSensitive := True; // Initialize the modules DoInitPlugins; // Initialize locales @@ -1186,5 +1205,6 @@ finalization LeftLocalEngine.Free; RightLocalEngine.Free; CommandLineHistory.Free; + QuickConnectHistory.Free; Bookmarks.Free; end. diff --git a/UConnectionManager.pas b/UConnectionManager.pas index 3703b02..2b39a51 100644 --- a/UConnectionManager.pas +++ b/UConnectionManager.pas @@ -24,7 +24,7 @@ interface uses glib2, gdk2, gtk2, pango, SysUtils, Types, Classes, GTKControls, GTKForms, GTKStdCtrls, GTKExtCtrls, GTKConsts, GTKView, GTKUtils, GTKDialogs, GTKPixbuf, GTKClasses, - UCore, UCoreClasses, UVFSCore, UEngines, URemoteWait; + UCore, UCoreClasses, UVFSCore, UEngines; type TFConnectionManager = class(TGTKDialog) @@ -37,11 +37,14 @@ type ListViewScrolledWindow: TGTKScrolledWindow; ListViewTable: TGTKTable; AddConnectionButton, EditButton, RemoveButton: TGTKImageButton; + QuickConnectButton: TGTKImageButton; ButtonBox: TGTKVButtonBox; ActionButtonBox: TGTKHButtonBox; - ConnectButton, CloseButton: TGTKButton; + ConnectButton, StopButton, CloseButton: TGTKButton; DoNotSavePasswordsCheckBox, DoNotSynchronizeKeyringCheckBox: TGTKCheckButton; procedure FormCreate(Sender: TObject); override; + procedure FormClose(Sender: TObject; var Action: TCloseAction); + procedure FormResponse(Sender: TObject; const ResponseID: integer); procedure ListViewSelectionChanged(Sender: TObject); procedure FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); procedure AddConnectionButtonClick(Sender: TObject); @@ -51,16 +54,17 @@ type procedure ListViewKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); procedure CloseButtonClick(Sender: TObject); procedure ConnectButtonClick(Sender: TObject); + procedure StopButtonClick(Sender: TObject); + procedure QuickConnectButtonClick(Sender: TObject); private - FSilenceError: boolean; - AFRemoteWait: TFRemoteWait; - FActiveConnInfo: TConnMgrItem; - FVFSAskPasswordTry: integer; procedure FillList; procedure DoConnect; public + FSilenceError: boolean; + Thread: TOpenConnectionThread; SourcePanelEngine: TPanelEngine; ConnectedEngine: TVFSEngine; + function DoConnectInternal(const URI: string; Engine: TVFSEngine; VFSDialogsParentWindow: PGtkWidget): boolean; end; var @@ -68,12 +72,13 @@ var implementation -uses ULocale, UCoreUtils, UConfig, UConnectionProperties, UGnome, uVFSprototypes; +uses ULocale, UCoreUtils, UConfig, UConnectionProperties, UGnome, UQuickConnect; procedure TFConnectionManager.FormCreate(Sender: TObject); var Column: TGTKTreeViewColumn; begin + Thread := nil; ConnectedEngine := nil; SetDefaultSize(650, 500); Caption := LANGConnMgr_Caption; @@ -84,6 +89,9 @@ begin ConnectButton.Caption := LANGConnMgr_ConnectButton; // ConnectButton.Default := True; CloseButton := TGTKButton.CreateFromStock(Self, GTK_STOCK_CLOSE); + StopButton := TGTKButton.CreateFromStock(Self, GTK_STOCK_STOP); + StopButton.Visible := False; + StopButton.Enabled := False; // Default := ConnectButton; ActionButtonBox := TGTKHButtonBox.Create(Self); ActionButtonBox.Layout := blEnd; @@ -91,6 +99,7 @@ begin ActionButtonBox.BorderWidth := 0; ActionButtonBox.AddControlEnd(CloseButton); ActionButtonBox.AddControlEnd(ConnectButton); + ActionButtonBox.AddControlEnd(StopButton); ActionArea.AddControlEx(ActionButtonBox, False, False, 0); @@ -159,26 +168,30 @@ begin RemoveButton.Caption := LANGConnMgr_RemoveButtonCaption; RemoveButton.UseUnderline := True; RemoveButton.Tooltip := LANGConnMgr_RemoveButtonTooltip; - + + QuickConnectButton := TGTKImageButton.Create(Self); + QuickConnectButton.SetFromStock('gtk-connect', isSmallToolbar); + QuickConnectButton.Caption := LANGmiQuickConnectCaption; + QuickConnectButton.UseUnderline := True; + ButtonBox := TGTKVButtonBox.Create(Self); ButtonBox.Spacing := 10; + ButtonBox.AddControl(QuickConnectButton); +// ButtonBox.AddControl(TGTKHSeparator.Create(Self)); + ButtonBox.AddControl(TGTKEventBox.Create(Self)); ButtonBox.AddControl(AddConnectionButton); ButtonBox.AddControl(EditButton); ButtonBox.AddControl(RemoveButton); DoNotSavePasswordsCheckBox := TGTKCheckButton.CreateWithLabel(Self, '_Do not store passwords internally (but still use gnome-keyring)'); DoNotSavePasswordsCheckBox.Tooltip := 'By checking this option on, Tux Commander will never save passwords into its connection list. Due to the GVFS nature, gnome-keyring will still be used to retrieve stored passwords from your desktop session.'; - DoNotSynchronizeKeyringCheckBox := TGTKCheckButton.CreateWithLabel(Self, 'Do not synchronize passwords to _gnome-keyring'); + DoNotSynchronizeKeyringCheckBox := TGTKCheckButton.CreateWithLabel(Self, 'Do not synchronize passwords to gnome-_keyring'); DoNotSynchronizeKeyringCheckBox.Tooltip := 'Don''t tell gnome-keyring to save any passwords.'; ListViewTable.AddControlEx(0, 1, 3, 5, ListViewScrolledWindow, [taoExpand, taoFill], [taoExpand, taoFill], 0, 5); ListViewTable.AddControlEx(0, 6, 3, 1, DoNotSavePasswordsCheckBox, [taoExpand, taoFill], [taoShrink], 10, 0); ListViewTable.AddControlEx(0, 7, 3, 1, DoNotSynchronizeKeyringCheckBox, [taoExpand, taoFill], [taoShrink], 10, 2); ListViewTable.AddControlEx(3, 2, 1, 3, ButtonBox, [taoShrink, taoFill], [taoShrink], 5, 5); -{ ListViewTable.AddControlEx(3, 2, 1, 1, AddConnectionButton, [taoShrink, taoFill], [taoShrink], 5, 5); - ListViewTable.AddControlEx(3, 3, 1, 1, EditButton, [taoShrink, taoFill], [taoShrink], 5, 5); - ListViewTable.AddControlEx(3, 4, 1, 1, RemoveButton, [taoShrink, taoFill], [taoShrink], 5, 5); } -// ListViewTable.AddControlEx(3, 1, 1, 1, TGTKLabel.Create(Self), [taoShrink, taoFill], [taoExpand, taoFill], 0, 2); ListViewTable.AddControlEx(3, 5, 1, 1, TGTKLabel.Create(Self), [taoShrink, taoFill], [taoExpand, taoFill], 0, 2); @@ -193,7 +206,11 @@ begin RemoveButton.OnClick := RemoveButtonClick; CloseButton.OnClick := CloseButtonClick; ConnectButton.OnClick := ConnectButtonClick; + StopButton.OnClick := StopButtonClick; + QuickConnectButton.OnClick := QuickConnectButtonClick; OnKeyDown := FormKeyDown; + OnClose := FormClose; + OnResponse := FormResponse; end; procedure TFConnectionManager.ListViewSelectionChanged(Sender: TObject); @@ -208,8 +225,13 @@ end; procedure TFConnectionManager.FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); begin case Key of - GDK_RETURN, GDK_KP_ENTER: if Assigned(ListView.Selected) then DoConnect; - GDK_ESCAPE: ModalResult := mbCancel; + GDK_RETURN, GDK_KP_ENTER: if StopButton.Visible then StopButtonClick(Sender) else + if Assigned(ListView.Selected) then DoConnect; + GDK_ESCAPE: begin + Accept := False; + if StopButton.Visible then StopButtonClick(Sender) + else ModalResult := mbCancel; + end; end; end; @@ -225,6 +247,20 @@ begin end; end; +procedure TFConnectionManager.FormClose(Sender: TObject; var Action: TCloseAction); +begin + if ListView.Selected <> nil then ConfConnMgrActiveItem := ListView.Selected.Index; + ConfConnMgrDoNotSavePasswords := DoNotSavePasswordsCheckBox.Checked; + ConfConnMgrDoNotSynchronizeKeyring := DoNotSynchronizeKeyringCheckBox.Checked; +end; + +procedure TFConnectionManager.FormResponse(Sender: TObject; const ResponseID: integer); +var Action: TCloseAction; +begin + FormClose(Sender, Action); +end; + + (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFConnectionManager.AddConnectionButtonClick(Sender: TObject); @@ -324,8 +360,6 @@ var i: integer; Item: TGTKListItem; ConnMgrItem: TConnMgrItem; begin - ReadConnections; - if ConnectionMgrList.Count > 0 then for i := 0 to ConnectionMgrList.Count - 1 do begin ConnMgrItem := ConnectionMgrList[i]; @@ -353,88 +387,36 @@ begin DoConnect; end; -procedure vfs_ask_question_callback(const AMessage: PChar; const Choices: PPChar; choice: PInteger; cancel_choice: Integer; user_data: Pointer); cdecl; -var ConnMgr: TFConnectionManager; - DialogParent: PGtkWidget; +procedure TFConnectionManager.StopButtonClick(Sender: TObject); begin - ConnMgr := user_data; - DialogParent := nil; -// DebugMsg(['******* vfs_ask_question_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16)]); - if ConnMgr is TFConnectionManager then begin - if ConnMgr.AFRemoteWait <> nil then DialogParent := ConnMgr.AFRemoteWait.FWidget - else DialogParent := ConnMgr.FWidget; - end; - HandleVFSAskQuestionCallback(DialogParent, AMessage, Choices, choice); - if ConnMgr is TFConnectionManager then begin - if choice <> nil then ConnMgr.FSilenceError := (choice^ < 0) or (choice^ = cancel_choice); - end; + if Thread <> nil then Thread.FCancelRequested := True; end; -function vfs_ask_password_callback(const AMessage: PChar; const default_user: PChar; const default_domain: PChar; const default_password: PChar; flags: TVFSAskPasswordFlags; - username, password: PPChar; anonymous: PInteger; domain: PPChar; password_save: PVFSPasswordSave; - user_data: Pointer): LongBool; cdecl; -var ConnMgr: TFConnectionManager; - DialogParent: PGtkWidget; +procedure TFConnectionManager.QuickConnectButtonClick(Sender: TObject); +var AFQuickConnect: TFQuickConnect; begin - ConnMgr := user_data; - DialogParent := nil; -// DebugMsg(['******* vfs_ask_password_callback spawned, user_data = 0x', IntToHex(QWord(user_data), 16), ', ThreadID = 0x', IntToHex(pthread_self, 16)]); - if ConnMgr is TFConnectionManager then begin - Inc(ConnMgr.FVFSAskPasswordTry); - if ConnMgr.AFRemoteWait <> nil then DialogParent := ConnMgr.AFRemoteWait.FWidget - else DialogParent := ConnMgr.FWidget; - - if not ConnMgr.DoNotSavePasswordsCheckBox.Checked then - flags := flags or VFS_ASK_PASSWORD_SAVE_INTERNAL; - - // Disable password saving if requested - if ConnMgr.DoNotSynchronizeKeyringCheckBox.Checked then begin - flags := flags and (not VFS_ASK_PASSWORD_SAVING_SUPPORTED); - if password_save <> nil then password_save^ := VFS_PASSWORD_SAVE_NEVER; - end; - end; - - // First pass, supply stored password if we have one - if (ConnMgr is TFConnectionManager) and (ConnMgr.FVFSAskPasswordTry = 1) and (password <> nil) and - ((flags and VFS_ASK_PASSWORD_NEED_PASSWORD) = VFS_ASK_PASSWORD_NEED_PASSWORD) and ((flags and VFS_ASK_PASSWORD_NEED_USERNAME) = 0) and - (ConnMgr.FActiveConnInfo <> nil) and (Length(ConnMgr.FActiveConnInfo.Password) > 0) then - begin - DebugMsg(['(II) vfs_ask_password_callback: Supplying stored password from Connection Manager']); - Result := True; - password^ := g_strdup(PChar(ConnMgr.FActiveConnInfo.Password)); - if password_save <> nil then - if ConnMgr.DoNotSynchronizeKeyringCheckBox.Checked - then password_save^ := VFS_PASSWORD_SAVE_NEVER - else password_save^ := VFS_PASSWORD_SAVE_PERMANENTLY; - end else begin - // Show password dialog and continue in loop - Result := HandleVFSAskPasswordCallback(DialogParent, AMessage, default_user, default_domain, default_password, flags, username, password, anonymous, domain, password_save); - if ConnMgr is TFConnectionManager then begin - ConnMgr.FSilenceError := Result = False; - // Save password back to Connection Manager - if Result and (password_save <> nil) and (password_save^ = VFS_PASSWORD_SAVE_PERMANENTLY) and - (ConnMgr.FActiveConnInfo <> nil) and (password <> nil) and (strlen(password^) > 0) and - (not ConnMgr.DoNotSavePasswordsCheckBox.Checked) then - begin - DebugMsg(['(II) vfs_ask_password_callback: Saving password to Connection Manager']); - ConnMgr.FActiveConnInfo.Password := password^; - end; + ConfConnMgrDoNotSavePasswords := DoNotSavePasswordsCheckBox.Checked; + ConfConnMgrDoNotSynchronizeKeyring := DoNotSynchronizeKeyringCheckBox.Checked; + try + AFQuickConnect := TFQuickConnect.Create(Self); + AFQuickConnect.SourcePanelEngine := SourcePanelEngine; + AFQuickConnect.ConnectionManager := Self; + if (AFQuickConnect.Run = mbOK) and (AFQuickConnect.ConnectedEngine <> nil) then begin + ConnectedEngine := AFQuickConnect.ConnectedEngine; + ModalResult := mbOK; end; + finally + AFQuickConnect.Free; end; - - // Strip password saving if requested - if (ConnMgr is TFConnectionManager) and ConnMgr.DoNotSynchronizeKeyringCheckBox.Checked and (password_save <> nil) then - password_save^ := VFS_PASSWORD_SAVE_NEVER; end; +(********************************************************************************************************************************) procedure TFConnectionManager.DoConnect; var Engine: TVFSEngine; i, j: integer; VFSPlugin: TVFSPlugin; - b: boolean; + FActiveConnInfo: TConnMgrItem; begin - FSilenceError := False; - FActiveConnInfo := nil; if ListView.Selected <> nil then FActiveConnInfo := ListView.Selected.AsPointer(0); if FActiveConnInfo = nil then Exit; @@ -467,30 +449,91 @@ begin if (SourcePanelEngine is TVFSEngine) and (Application.MessageBox(PGtkWindow(FWidget), LANGCloseOpenConnection, [mbYes, mbNo], mbWarning, mbYes, mbNo) <> mbYes) then Exit; + ListViewTable.Enabled := False; + CloseButton.Enabled := False; + ConnectButton.Enabled := False; + ConnectButton.Visible := False; + StopButton.Enabled := True; + StopButton.Visible := True; + StopButton.Default := True; + StopButton.SetFocus; + + ConfConnMgrDoNotSavePasswords := DoNotSavePasswordsCheckBox.Checked; + ConfConnMgrDoNotSynchronizeKeyring := DoNotSynchronizeKeyringCheckBox.Checked; + // Construct the VFS Engine and try to open the connection Engine := TVFSEngine.Create(VFSPlugin); Engine.ParentEngine := SourcePanelEngine; Engine.SavePath := SourcePanelEngine.Path; + Engine.Password := FActiveConnInfo.Password; + Engine.PasswordUsed := False; - AFRemoteWait := TFRemoteWait.Create(Application); - AFRemoteWait.ParentForm := FConnectionManager; - AFRemoteWait.ShowModal; - Application.ProcessMessages; - - FVFSAskPasswordTry := 0; - b := Engine.VFSOpenURI(UTF8ToStr(FActiveConnInfo.GetURI(False)), @vfs_ask_question_callback, @vfs_ask_password_callback, nil, Self); - - AFRemoteWait.Free; - AFRemoteWait := nil; - if not b then begin + if not DoConnectInternal(UTF8ToStr(FActiveConnInfo.GetURI(False)), Engine, FWidget) then begin if not FSilenceError then Application.MessageBox(PGtkWindow(FWidget), LANGCouldntOpenURI, [mbOK], mbError, mbOK, mbOK); + ListViewTable.Enabled := True; + CloseButton.Enabled := True; + ConnectButton.Enabled := True; + ConnectButton.Visible := True; + StopButton.Visible := False; + StopButton.Enabled := False; + ListView.SetFocus; + Engine.Free; Exit; end; + if not DoNotSavePasswordsCheckBox.Checked then FActiveConnInfo.Password := Engine.Password; + Engine.Password := ''; // Security precaution + StopButton.Enabled := False; ConnectedEngine := Engine; ModalResult := mbOK; end; +function TFConnectionManager.DoConnectInternal(const URI: string; Engine: TVFSEngine; VFSDialogsParentWindow: PGtkWidget): boolean; +begin + Result := False; + FSilenceError := False; + Thread := TOpenConnectionThread.Create; + try + Thread.VFSConnectionManagerMode := VFSDialogsParentWindow = FWidget; + Thread.VFSDialogsParentWindow := VFSDialogsParentWindow; + Thread.AEngine := Engine; + Thread.URI := URI; + Thread.Resume; + repeat + Sleep(ConstInternalProgressTimer); + Application.ProcessMessages; + if Thread.VFSAskQuestion_Display then begin + Thread.VFSAskQuestion_Display := False; + DebugMsg(['Main thread: displaying question dialog']); + HandleVFSAskQuestionCallback(FWidget, Thread.VFSAskQuestion_Message, Thread.VFSAskQuestion_Choices, Thread.VFSAskQuestion_Choice); + Thread.VFSCallbackEvent.SetEvent; + end; + if Thread.VFSAskPassword_Display then begin + Thread.VFSAskPassword_Display := False; + DebugMsg(['Main thread: displaying password prompt']); + Thread.VFSAskPassword_Result := HandleVFSAskPasswordCallback(FWidget, + Thread.VFSAskPassword_Message, + Thread.VFSAskPassword_default_user, + Thread.VFSAskPassword_default_domain, + Thread.VFSAskPassword_default_password, + Thread.VFSAskPassword_flags, + Thread.VFSAskPassword_username, + Thread.VFSAskPassword_password, + Thread.VFSAskPassword_anonymous, + Thread.VFSAskPassword_domain, + Thread.VFSAskPassword_password_save); + Thread.VFSCallbackEvent.SetEvent; + end; + until Thread.Finished; + Result := Thread.OpenResult; + FSilenceError := Thread.VFSCallbackCancelled; + Thread.Free; + Thread := nil; + except + on E: Exception do DebugMsg(['*** Exception raised in TFConnectionManager.DoConnect (', E.ClassName, '): ', E.Message]); + end; +end; + end. @@ -20,7 +20,7 @@ unit UCore; interface -uses glib2, SyncObjs, Classes, GTKForms, GTKView, ULibc, UEngines, UCoreUtils, UProgress, UVFSCore, uVFSprototypes; +uses glib2, gtk2, SyncObjs, Classes, GTKForms, GTKView, ULibc, UEngines, UCoreUtils, UProgress, UVFSCore, uVFSprototypes; function FillPanel(List: TList; ListView: TGTKListView; Engine: TPanelEngine; LeftPanel: boolean): boolean; @@ -59,6 +59,11 @@ type TVFSCallbackThread = class(TThread) VFSCallbackCancelled: boolean; + VFSConnectionManagerMode: boolean; + VFSDialogsParentWindow: PGtkWidget; + + FCancelRequested: boolean; + constructor Create(CreateSuspended: boolean); destructor Destroy; override; end; @@ -100,7 +105,6 @@ type TVFSCallbackThread = class(TThread) FCallbackLockEvent: TSimpleEvent; - // Parameters ProgressForm: TFProgress; Engine, SrcEngine, DestEngine: TPanelEngine; @@ -169,11 +173,23 @@ type TVFSCallbackThread = class(TThread) Finished, CancelIt: boolean; RunningTime: Int64; AFullPath, AHighlightItem: string; + constructor Create; + destructor Destroy; override; + end; + TOpenConnectionThread = class(TVFSCallbackThread) + private + protected + procedure Execute; override; + public + URI: string; + Finished: boolean; + OpenResult: boolean; constructor Create; destructor Destroy; override; end; + // Thread aware functions (also half-thread-safe) without any piece of GTK code procedure DeleteFilesWorker(SenderThread: TWorkerThread); procedure CopyFilesWorker(SenderThread: TWorkerThread); @@ -237,6 +253,7 @@ var LeftLocalEngine, RightLocalEngine: TPanelEngine; FMainEscPressed: boolean; UsedTempPaths: TStringList; SelectHistory, SearchHistory: TStringList; + QuickConnectHistory: TStringList; (********************************************************************************************************************************) implementation @@ -258,6 +275,9 @@ begin VFSAskQuestion_Display := False; VFSAskPassword_Display := False; VFSCallbackCancelled := False; + VFSConnectionManagerMode := False; + VFSDialogsParentWindow := FMain.FWidget; + FCancelRequested := False; end; destructor TVFSCallbackThread.Destroy; @@ -282,9 +302,16 @@ begin Exit; end; + if Thread.FCancelRequested then begin + DebugMsg(['!! (WARNING): vfs_ask_question_callback: FCancelRequested.']); + if (choice <> nil) then choice^ := -1; + Thread.VFSCallbackCancelled := True; + Exit; + end; + if pthread_self = Application.ThreadID then begin - DebugMsg(['!! (WARNING): vfs_ask_question_callback called from the main thread, expected spawn from a OpenDirThread']); - HandleVFSAskQuestionCallback(FMain.FWidget, AMessage, Choices, choice); + DebugMsg(['!! (WARNING): vfs_ask_question_callback called from the main thread, expected spawn from a TVFSCallbackThread']); + HandleVFSAskQuestionCallback(Thread.VFSDialogsParentWindow, AMessage, Choices, choice); if (choice <> nil) then Thread.VFSCallbackCancelled := (choice^ < 0) or (choice^ = cancel_choice); Exit; end; @@ -302,8 +329,8 @@ begin Exit; end; - DebugMsg(['!! (ERROR): vfs_ask_question_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']); - DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); + DebugMsg(['!! (ERROR): vfs_ask_question_callback spawned neither from the main thread nor from active TVFSCallbackThread, dropping the callback to prevent data corruption.']); + DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', TVFSCallbackThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); end; function vfs_ask_password_callback(const AMessage: PChar; const default_user: PChar; const default_domain: PChar; const default_password: PChar; flags: TVFSAskPasswordFlags; @@ -319,6 +346,13 @@ begin Exit; end; + if Thread.FCancelRequested then begin + DebugMsg(['!! (WARNING): vfs_ask_password_callback: FCancelRequested.']); + Result := False; + Thread.VFSCallbackCancelled := True; + Exit; + end; + def_pass := default_password; // Disable password saving if requested @@ -326,27 +360,33 @@ begin flags := flags and (not VFS_ASK_PASSWORD_SAVING_SUPPORTED); if password_save <> nil then password_save^ := VFS_PASSWORD_SAVE_NEVER; end; - if ConfConnMgrDoNotSavePasswords then - flags := flags and (not VFS_ASK_PASSWORD_SAVE_INTERNAL); + if ConfConnMgrDoNotSavePasswords then flags := flags and (not VFS_ASK_PASSWORD_SAVE_INTERNAL) else + if Thread.VFSConnectionManagerMode then flags := flags or VFS_ASK_PASSWORD_SAVE_INTERNAL; + // Use stored password, if previously set - if ((flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE) and (password <> nil) and - (Thread.AEngine is TVFSEngine) and (Length((Thread.AEngine as TVFSEngine).Password) > 0) then + if (((flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE) or Thread.VFSConnectionManagerMode) and + (password <> nil) and (Thread.AEngine is TVFSEngine) and (Length((Thread.AEngine as TVFSEngine).Password) > 0) then begin if not (Thread.AEngine as TVFSEngine).PasswordUsed then begin DebugMsg([' (II) vfs_ask_password_callback: reusing manually set password']); password^ := g_strdup(PChar((Thread.AEngine as TVFSEngine).Password)); (Thread.AEngine as TVFSEngine).PasswordUsed := True; - Result := True; + if (password_save <> nil) and Thread.VFSConnectionManagerMode then + if ConfConnMgrDoNotSynchronizeKeyring then password_save^ := VFS_PASSWORD_SAVE_NEVER + else password_save^ := VFS_PASSWORD_SAVE_PERMANENTLY; Thread.VFSCallbackCancelled := False; + Result := True; Exit; - end else def_pass := PChar((Thread.AEngine as TVFSEngine).Password); + end else + if (flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE then + def_pass := PChar((Thread.AEngine as TVFSEngine).Password); end; // Ask for password if pthread_self = Application.ThreadID then begin - DebugMsg(['!! (WARNING): vfs_ask_password_callback called from the main thread, expected spawn from a OpenDirThread']); - Result := HandleVFSAskPasswordCallback(FMain.FWidget, AMessage, default_user, default_domain, def_pass, flags, username, password, anonymous, domain, password_save); + DebugMsg(['!! (WARNING): vfs_ask_password_callback called from the main thread, expected spawn from a TVFSCallbackThread']); + Result := HandleVFSAskPasswordCallback(Thread.VFSDialogsParentWindow, AMessage, default_user, default_domain, def_pass, flags, username, password, anonymous, domain, password_save); Thread.VFSCallbackCancelled := Result = False; end else if pthread_self = Thread.FThreadID then begin @@ -370,14 +410,14 @@ begin Thread.VFSCallbackCancelled := Result = False; end else begin - DebugMsg(['!! (ERROR): vfs_ask_password_callback spawned neither from the main thread nor from active OpenDirThread, dropping the callback to prevent data corruption.']); - DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', OpenDirThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); + DebugMsg(['!! (ERROR): vfs_ask_password_callback spawned neither from the main thread nor from active TVFSCallbackThread, dropping the callback to prevent data corruption.']); + DebugMsg([' ThreadID = 0x', IntToHex(pthread_self, 16), ', TVFSCallbackThread ID = 0x', IntToHex(Thread.FThreadID, 16), ', Application.ThreadID = 0x', IntToHex(Application.ThreadID, 16)]); end; // Save password back to the engine - if ((flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE) and - Result and (password <> nil) and (strlen(password^) > 0) and - (Thread.AEngine is TVFSEngine) then + if Result and (password <> nil) and (strlen(password^) > 0) and (Thread.AEngine is TVFSEngine) and + (((flags and VFS_ASK_PASSWORD_ARCHIVE_MODE) = VFS_ASK_PASSWORD_ARCHIVE_MODE) or + (Thread.VFSConnectionManagerMode and (password_save <> nil) and (password_save^ = VFS_PASSWORD_SAVE_PERMANENTLY))) then begin (Thread.AEngine as TVFSEngine).Password := string(password^); (Thread.AEngine as TVFSEngine).PasswordUsed := True; @@ -2903,6 +2943,32 @@ begin end; end; + +(********************************************************************************************************************************) +(********************************************************************************************************************************) +constructor TOpenConnectionThread.Create; +begin + inherited Create(True); + FreeOnTerminate := False; + Finished := False; + OpenResult := False; +end; + +destructor TOpenConnectionThread.Destroy; +begin + inherited Destroy; +end; + +procedure TOpenConnectionThread.Execute; +begin + PrepareExecute; + try + OpenResult := (AEngine as TVFSEngine).VFSOpenURI(URI, @vfs_ask_question_callback, @vfs_ask_password_callback, nil, Self); + finally + Finished := True; + end; +end; + (********************************************************************************************************************************) (********************************************************************************************************************************) function PurgeDirectory(APath: string): boolean; @@ -182,6 +182,7 @@ type procedure miPathBoxCopyPathClick(Sender: TObject); procedure miCopyNamesClick(Sender: TObject); procedure RightMouseSelectPopupTimerTimer(Sender: TObject); + procedure miQuickConnectClick(Sender: TObject); private LeftLastFocused, Editing, QuickFind, RedrawLeftInactive, RedrawRightInactive, StartUp, LeftTabPopup: boolean; LastWidth, RunningEscSensitive: integer; @@ -242,6 +243,7 @@ type function HandleKey(Key: Word; Shift: TShiftState; LeftPanel: boolean): boolean; function IsEditing(AListView: TGTKListView): boolean; function PanelFindEditableWidget(AListView: TGTKListView): PGtkWidget; + function CheckForUnsavedConnection(Engine: TVFSEngine; AllowCancel: boolean): boolean; // Returns False to Cancel public LeftPanelEngine, RightPanelEngine : TPanelEngine; ColumnSortIDs: array[1..ConstNumPanelColumns] of integer; @@ -260,7 +262,8 @@ uses ULibc, UFileTypeSettings, UFileAssoc, UChmod, UChown, USymlink, UPreferences, UViewer, UToolTips, UMounterPrefs, UColumns, UTestPlugin, UConnectionManager, USearch, UProperties, - URemoteWait, URunFromVFS, uVFSprototypes; + URemoteWait, URunFromVFS, uVFSprototypes, UQuickConnect, + UConnectionProperties; @@ -901,7 +904,7 @@ begin mnuNetwork.Add(miOpenConnection); miQuickConnect := TGTKMenuItem.CreateTyped(Self, itImageText); miQuickConnect.Caption := LANGmiQuickConnectCaption; - miQuickConnect.Enabled := False; + miQuickConnect.OnClick := miQuickConnectClick; miQuickConnect.ShortCuts.AddName('<Control>N'); mnuNetwork.Add(miQuickConnect); mnuNetwork.Add(TGTKMenuItem.CreateTyped(Self, itSeparator)); @@ -1118,6 +1121,7 @@ procedure TFMain.FormClose(Sender: TObject; var Action: TCloseAction); Result := FallbackEngine; if not Assigned(Engine.ParentEngine) or (not (Engine is TVFSEngine)) then Exit; Result := Engine.ParentEngine; + CheckForUnsavedConnection(Engine as TVFSEngine, False); if not TVFSEngine(Engine).VFSClose then DebugMsg(['Error closing the engine...']); Engine.Free; end; @@ -1170,8 +1174,10 @@ begin LeftPanelTabs[i] := s; end else while Assigned(LeftTabEngines[i]) and (TPanelEngine(LeftTabEngines[i]) is TVFSEngine) do begin - LeftPanelTabs[i] := TPanelEngine(LeftTabEngines[i]).SavePath; + s := TPanelEngine(LeftTabEngines[i]).SavePath; LeftTabEngines[i] := InternalCloseEngine(LeftTabEngines[i], LeftLocalEngine); + if s <> '' then LeftPanelTabs[i] := s + else LeftPanelTabs[i] := TPanelEngine(LeftTabEngines[i]).Path; end; except end; @@ -1187,8 +1193,10 @@ begin RightPanelTabs[i] := s; end else while Assigned(RightTabEngines[i]) and (TPanelEngine(RightTabEngines[i]) is TVFSEngine) do begin - RightPanelTabs[i] := TPanelEngine(RightTabEngines[i]).SavePath; + s := TPanelEngine(RightTabEngines[i]).SavePath; RightTabEngines[i] := InternalCloseEngine(RightTabEngines[i], RightLocalEngine); + if s <> '' then RightPanelTabs[i] := s + else RightPanelTabs[i] := TPanelEngine(RightTabEngines[i]).Path; end; except end; end; @@ -5627,6 +5635,12 @@ begin DebugMsg(['Couldn''t close tab: wrong TabNo']); Exit; end; + + if TabNo > 0 then begin + Engine := TabEngines[TabNo]; + if (Engine is TVFSEngine) and (not CheckForUnsavedConnection(Engine as TVFSEngine, True)) then Exit; + end; + if (ANotebook.ChildrenCount > 2) and (TabNo >= 0) then begin // Close one tab, leave tab bar visible Engine := TabEngines[TabNo]; @@ -5673,6 +5687,7 @@ begin Engine := TabEngines[i]; while Engine is TVFSEngine do try + if (i <> TabNo) and (not CheckForUnsavedConnection(Engine as TVFSEngine, False)) then Exit; xEngine := Engine; Engine := xEngine.ParentEngine; if not TVFSEngine(xEngine).VFSClose then DebugMsg(['Error closing the engine...']); @@ -5966,7 +5981,19 @@ var Engine: TPanelEngine; begin if LeftPanel then Engine := LeftPanelEngine else Engine := RightPanelEngine; - if not Assigned(Engine.ParentEngine) or (not (Engine is TVFSEngine)) then Exit; + if (not Assigned(Engine.ParentEngine)) or (not (Engine is TVFSEngine)) then begin + if LeftPanel then begin + LeftPanelEngine := LeftLocalEngine; + Result := LeftPanelEngine.Path; + end else begin + RightPanelEngine := RightLocalEngine; + Result := RightPanelEngine.Path; + end; + Exit; + end; + + if not CheckForUnsavedConnection(Engine as TVFSEngine, (not SuppressRefresh) and (not ApplicationShuttingDown)) then Exit; + if LeftPanel then LeftPanelEngine := Engine.ParentEngine else RightPanelEngine := Engine.ParentEngine; @@ -5978,6 +6005,55 @@ begin Engine.Free; end; +function TFMain.CheckForUnsavedConnection(Engine: TVFSEngine; AllowCancel: boolean): boolean; // Returns False to Cancel +var Buttons: TMessageButtons; + CancelButton: TMessageButton; + AFConnectionProperties: TFConnectionProperties; + URI: string; + ConnMgrItem: TConnMgrItem; +begin + Result := True; + URI := Engine.GetPathURI; + if Engine.OpenedFromQuickConnect and (Length(Trim(URI)) > 0) then begin + Buttons := [mbYes, mbNo]; + CancelButton := mbNo; + if AllowCancel then begin + Include(Buttons, mbCancel); + CancelButton := mbCancel; + end; + case Application.MessageBox('The active connection has not been saved. Do you want to save it to Connection Manager?', Buttons, mbWarning, mbYes, CancelButton) of + mbYes: begin + AFConnectionProperties := TFConnectionProperties.Create(Self); + try + AFConnectionProperties.URIEntry.Text := URI; + if AFConnectionProperties.Run = mbOK then begin + ReadConnections; + ConnMgrItem := TConnMgrItem.Create; + ConnMgrItem.ConnectionName := AFConnectionProperties.NameEntry.Text; + ConnMgrItem.ServiceType := AFConnectionProperties.GetService; + ConnMgrItem.Server := AFConnectionProperties.ServerEntry.Text; + ConnMgrItem.Username := AFConnectionProperties.UserNameEntry.Text; + ConnMgrItem.Password := AFConnectionProperties.PasswordEntry.Text; + ConnMgrItem.TargetDir := AFConnectionProperties.TargetDirEntry.Text; + ConnMgrItem.PluginID := ''; + if AFConnectionProperties.PluginOptionMenu.ItemIndex <> 0 then + ConnMgrItem.PluginID := TVFSPlugin(PluginList[AFConnectionProperties.PluginOptionMenu.ItemIndex - 1]).VFSName; + ConnectionMgrList.Add(ConnMgrItem); + WriteConnections; + end; + finally + AFConnectionProperties.Free; + end; + Result := True; + end; + mbNo: Result := True; + mbCancel: Result := False; + else {Cancel?} Result := not AllowCancel; + end; + end; +end; + + (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFMain.ShowBookmarkQuick(LeftPanel: boolean); @@ -6061,14 +6137,11 @@ var b: boolean; begin try InternalLock; - + ReadConnections; FConnectionManager := TFConnectionManager.Create(Self); if LeftLastFocused then FConnectionManager.SourcePanelEngine := LeftPanelEngine else FConnectionManager.SourcePanelEngine := RightPanelEngine; b := FConnectionManager.Run = mbOK; - if FConnectionManager.ListView.Selected <> nil then ConfConnMgrActiveItem := FConnectionManager.ListView.Selected.Index; - ConfConnMgrDoNotSavePasswords := FConnectionManager.DoNotSavePasswordsCheckBox.Checked; - ConfConnMgrDoNotSynchronizeKeyring := FConnectionManager.DoNotSynchronizeKeyringCheckBox.Checked; WriteConnections; // Save connection manager data if b and (FConnectionManager.ConnectedEngine <> nil) then begin @@ -6084,6 +6157,31 @@ begin end; end; +procedure TFMain.miQuickConnectClick(Sender: TObject); +var b: boolean; +begin + try + InternalLock; + ReadConnections; + FQuickConnect := TFQuickConnect.Create(Self); + if LeftLastFocused then FQuickConnect.SourcePanelEngine := LeftPanelEngine + else FQuickConnect.SourcePanelEngine := RightPanelEngine; + b := FQuickConnect.Run = mbOK; + WriteConnections; // Save connection manager data + + if b and (FQuickConnect.ConnectedEngine <> nil) then begin + if FQuickConnect.SourcePanelEngine is TVFSEngine then CloseVFS(LeftLastFocused, True); + if LeftLastFocused then LeftPanelEngine := FQuickConnect.ConnectedEngine + else RightPanelEngine := FQuickConnect.ConnectedEngine; + DoRefresh(LeftLastFocused, False, False); + end; + finally + FQuickConnect.Free; + Application.ProcessMessages; + InternalLockInit(False); + end; +end; + procedure TFMain.miDisconnectClick(Sender: TObject); begin CloseVFS(LeftLastFocused, False); diff --git a/UQuickConnect.pas b/UQuickConnect.pas new file mode 100644 index 0000000..d92e335 --- /dev/null +++ b/UQuickConnect.pas @@ -0,0 +1,349 @@ +(* + Tux Commander - UQuickConnect - Quick connection dialog + Copyright (C) 2008 Tomas Bzatek <tbzatek@users.sourceforge.net> + Check for updates on tuxcmd.sourceforge.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*) +unit UQuickConnect; + +interface + +uses + glib2, gdk2, gtk2, pango, SysUtils, Types, Classes, GTKControls, GTKForms, GTKStdCtrls, GTKExtCtrls, GTKConsts, GTKView, + GTKUtils, GTKDialogs, GTKPixbuf, GTKClasses, GTKMenus, + UCore, UCoreClasses, UVFSCore, UEngines, UConnectionManager; + +type + TFQuickConnect = class(TGTKDialog) + TitleFrame: TGTKFrame; + TitleLabel: TGTKLabel; + TitleEventBox: TGTKEventBox; + TitleIcon: TGTKImage; + TitleHBox: TGTKHBox; + Table: TGTKTable; + ActionButtonBox: TGTKHButtonBox; + ConnectButton, StopButton, CloseButton: TGTKButton; + Label1, Label2: TGTKLabel; + URIComboBox: TGTKCombo; + PluginOptionMenu: TGTKOptionMenu; + procedure FormCreate(Sender: TObject); override; + procedure FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + procedure FormResponse(Sender: TObject; const ResponseID: integer); + procedure CloseButtonClick(Sender: TObject); + procedure ConnectButtonClick(Sender: TObject); + procedure StopButtonClick(Sender: TObject); + procedure ComboBoxKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); + procedure ComboBoxChanged(Sender: TObject); + private + AFConnectionManager: TFConnectionManager; + SavedData: string; + procedure DoConnect; + public + SourcePanelEngine: TPanelEngine; + ConnectedEngine: TVFSEngine; + ConnectionManager: TFConnectionManager; + end; + +var + FQuickConnect: TFQuickConnect; + +implementation + +uses ULocale, UCoreUtils, UConfig, UConnectionProperties, UGnome, uVFSprototypes; + + +procedure TFQuickConnect.FormCreate(Sender: TObject); +var i: integer; + MenuItem: TGTKMenuItem; +begin + SavedData := ''; + ConnectedEngine := nil; + ConnectionManager := nil; + AFConnectionManager := nil; + SetDefaultSize(435, 200); + Caption := 'Quick connect'; + Buttons := []; + ShowSeparator := False; + + ConnectButton := TGTKButton.Create(Self); + ConnectButton.Caption := LANGConnMgr_ConnectButton; +// ConnectButton.Default := True; + CloseButton := TGTKButton.CreateFromStock(Self, GTK_STOCK_CLOSE); + StopButton := TGTKButton.CreateFromStock(Self, GTK_STOCK_STOP); + StopButton.Visible := False; + StopButton.Enabled := False; +// Default := ConnectButton; + ActionButtonBox := TGTKHButtonBox.Create(Self); + ActionButtonBox.Layout := blEnd; + ActionButtonBox.Spacing := 10; + ActionButtonBox.BorderWidth := 0; + ActionButtonBox.AddControlEnd(CloseButton); + ActionButtonBox.AddControlEnd(ConnectButton); + ActionButtonBox.AddControlEnd(StopButton); + + ActionArea.AddControlEx(ActionButtonBox, False, False, 0); + + TitleEventBox := TGTKEventBox.Create(Self); + TitleLabel := TGTKLabel.Create(Self); + TitleLabel.Caption := Format('<span size="x-large" weight="ultrabold">%s</span>', ['Quick connect']); + TitleLabel.UseMarkup := True; + TitleLabel.XAlign := 0; + TitleLabel.XPadding := 0; + TitleLabel.YPadding := 3; + TitleEventBox.ControlState := csPrelight; + TitleFrame := TGTKFrame.CreateWithoutLabel(Self); + TitleFrame.ShadowType := stShadowOut; + TitleIcon := TGTKImage.Create(Self); + TitleIcon.SetFromStock('gtk-connect', isLargeToolbar); + TitleHBox := TGTKHBox.Create(Self); + TitleHBox.Homogeneous := False; + TitleHBox.AddControlEx(TGTKEventBox.Create(Self), False, False, 5); + TitleHBox.AddControlEx(TitleIcon, False, False, 0); + TitleHBox.AddControlEx(TitleLabel, True, True, 10); + TitleEventBox.AddControl(TitleHBox); + TitleFrame.AddControl(TitleEventBox); + ClientArea.AddControlEx(TitleFrame, False, True, 0); + + Table := TGTKTable.Create(Self); + Table.BorderWidth := 25; + ClientArea.AddControlEx(Table, True, True, 0); + + URIComboBox := TGtkCombo.Create(Self); + URIComboBox.DisableActivate; + PluginOptionMenu := TGTKOptionMenu.Create(Self); + + Label1 := TGTKLabel.Create(Self); + Label1.Caption := Format('<span weight="ultrabold">%s</span>', ['C_onnect to URI:']); + Label1.FocusControl := URIComboBox.Entry; + Label1.UseMarkup := True; + Label1.UseUnderline := True; + Label1.XAlign := 0; + Label2 := TGTKLabel.Create(Self); + Label2.Caption := LANGConnProp_VFSModule; + Label2.FocusControl := PluginOptionMenu; + Label2.UseUnderline := True; + Label2.XAlign := 0; + + if QuickConnectHistory.Count > 0 then + for i := 0 to QuickConnectHistory.Count - 1 do + URIComboBox.Items.Append(QuickConnectHistory[i]); + URIComboBox.Entry.Text := ''; + + // Fill the plugins menu + MenuItem := TGTKMenuItem.CreateTyped(Self, itLabel); + MenuItem.Caption := LANGConnProp_MenuItemCaption; + PluginOptionMenu.Items.Add(MenuItem); + for i := 0 to PluginList.Count - 1 do begin + MenuItem := TGTKMenuItem.CreateTyped(Self, itImageText); + MenuItem.SetCaptionPlain(Format('%s [%s]', [TVFSPlugin(PluginList[i]).VFSName, + ExtractFileName(TVFSPlugin(PluginList[i]).FullPath)])); + PluginOptionMenu.Items.Add(MenuItem); + end; + + // Find last used plugin + if Length(Trim(ConfQuickConnectPluginID)) > 0 then + for i := 0 to PluginList.Count - 1 do + if TVFSPlugin(PluginList[i]).VFSName = ConfQuickConnectPluginID then begin + PluginOptionMenu.ItemIndex := i + 1; + Break; + end; + +// Table.AddControlEx(1, 1, 2, 1, TGTKEventBox.Create(Self), [taoExpand, taoFill], [taoShrink], 0, 2); + Table.AddControlEx(0, 0, 2, 1, Label1, [taoShrink, taoFill], [taoShrink], 0, 2); + Table.AddControlEx(0, 1, 2, 1, URIComboBox, [taoExpand, taoFill], [taoShrink], 0, 5); + Table.AddControlEx(0, 2, 1, 1, Label2, [taoShrink, taoFill], [taoShrink], 10, 5); + Table.AddControlEx(1, 2, 1, 1, PluginOptionMenu, [taoExpand, taoFill], [taoShrink], 0, 5); + + CloseButton.OnClick := CloseButtonClick; + ConnectButton.OnClick := ConnectButtonClick; + StopButton.OnClick := StopButtonClick; + OnKeyDown := FormKeyDown; + OnClose := FormClose; + OnResponse := FormResponse; + URIComboBox.Entry.OnKeyDown := ComboBoxKeyDown; + URIComboBox.Entry.OnChanged := ComboBoxChanged; + ComboBoxChanged(Sender); +end; + +procedure TFQuickConnect.FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); +begin + case Key of + GDK_RETURN, GDK_KP_ENTER: if StopButton.Visible then StopButtonClick(Sender) + else DoConnect; + GDK_ESCAPE: begin + Accept := False; + if StopButton.Visible then StopButtonClick(Sender) + else ModalResult := mbCancel; + end; + end; +end; + +procedure TFQuickConnect.ComboBoxKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); +var Orig, s: string; + i: integer; +begin + case Key of + GDK_UP, GDK_DOWN: if Shift = [] then begin + Accept := False; + if QuickConnectHistory.Count > 0 then begin + Orig := Trim(URIComboBox.Entry.Text); + i := QuickConnectHistory.IndexOf(Orig); + + if Key = GDK_DOWN then begin + if i < 0 then begin + SavedData := Orig; + i := 0; + end else + if QuickConnectHistory.Count > i + 1 then Inc(i); + s := QuickConnectHistory[i]; + end else begin + if i < 0 then Exit else + if i = 0 then begin + s := SavedData; + SavedData := ''; + end else + if QuickConnectHistory.Count > i then s := QuickConnectHistory[i - 1]; + end; + + URIComboBox.Entry.Text := s; + URIComboBox.Entry.SetFocus; + URIComboBox.Entry.SelectAll; + end; + end; + end; +end; + +procedure TFQuickConnect.ComboBoxChanged(Sender: TObject); +begin + ConnectButton.Enabled := Length(Trim(URIComboBox.Entry.Text)) > 0; +end; + +procedure TFQuickConnect.FormClose(Sender: TObject; var Action: TCloseAction); +begin + if Length(Trim(URIComboBox.Entry.Text)) > 0 then + SaveItemToHistory(URIComboBox.Entry.Text, QuickConnectHistory); + if PluginOptionMenu.ItemIndex <> 0 + then ConfQuickConnectPluginID := TVFSPlugin(PluginList[PluginOptionMenu.ItemIndex - 1]).VFSName + else ConfQuickConnectPluginID := ''; +end; + +procedure TFQuickConnect.FormResponse(Sender: TObject; const ResponseID: integer); +var Action: TCloseAction; +begin + FormClose(Sender, Action); +end; + +(********************************************************************************************************************************) +procedure TFQuickConnect.CloseButtonClick(Sender: TObject); +begin + ModalResult := mbCancel; +end; + +procedure TFQuickConnect.ConnectButtonClick(Sender: TObject); +begin + DoConnect; +end; + +procedure TFQuickConnect.StopButtonClick(Sender: TObject); +begin + if (AFConnectionManager <> nil) and (AFConnectionManager.Thread <> nil) then + AFConnectionManager.Thread.FCancelRequested := True; +end; + +procedure TFQuickConnect.DoConnect; +var Engine: TVFSEngine; + i, j: integer; + VFSPlugin: TVFSPlugin; + Scheme: string; +begin + if ConnectionManager <> nil then AFConnectionManager := ConnectionManager + else AFConnectionManager := TFConnectionManager.Create(Self); + + if PluginList.Count = 0 then begin + Application.MessageBox(PGtkWindow(FWidget), LANGThereIsNoModuleAvailable, [mbOK], mbError, mbOK, mbOK); + Exit; + end; + + // Find VFS module to use for this connection + VFSPlugin := nil; + if PluginOptionMenu.ItemIndex <> 0 then ConfQuickConnectPluginID := TVFSPlugin(PluginList[PluginOptionMenu.ItemIndex - 1]).VFSName + else ConfQuickConnectPluginID := ''; + if Length(ConfQuickConnectPluginID) > 0 then + for i := 0 to PluginList.Count - 1 do + if TVFSPlugin(PluginList[i]).VFSName = ConfQuickConnectPluginID then begin + VFSPlugin := PluginList[i]; + Break; + end; + if VFSPlugin = nil then begin + Scheme := ''; + if Pos('://', URIComboBox.Entry.Text) > 0 then + Scheme := Copy(URIComboBox.Entry.Text, 1, Pos('://', URIComboBox.Entry.Text) - 1); + for i := 0 to PluginList.Count - 1 do begin + if Length(TVFSPlugin(PluginList[i]).Services) > 0 then + for j := 0 to Length(TVFSPlugin(PluginList[i]).Services) - 1 do + if WideCompareText(TVFSPlugin(PluginList[i]).Services[j], Scheme) = 0 then begin + VFSPlugin := PluginList[i]; + Break; + end; + if VFSPlugin <> nil then Break; + end; + if VFSPlugin = nil then VFSPlugin := PluginList[0]; // Fallback in hope some other plugin can handle it + end; + + if (SourcePanelEngine is TVFSEngine) and (Application.MessageBox(PGtkWindow(FWidget), LANGCloseOpenConnection, [mbYes, mbNo], mbWarning, mbYes, mbNo) <> mbYes) then Exit; + + Table.Enabled := False; + CloseButton.Enabled := False; + ConnectButton.Enabled := False; + ConnectButton.Visible := False; + StopButton.Enabled := True; + StopButton.Visible := True; + StopButton.Default := True; + StopButton.SetFocus; + + // Construct the VFS Engine and try to open the connection + Engine := TVFSEngine.Create(VFSPlugin); + Engine.ParentEngine := SourcePanelEngine; + Engine.SavePath := SourcePanelEngine.Path; + Engine.OpenedFromQuickConnect := True; + + if not AFConnectionManager.DoConnectInternal(URIComboBox.Entry.Text, Engine, FWidget) then begin + if not AFConnectionManager.FSilenceError then Application.MessageBox(PGtkWindow(FWidget), LANGCouldntOpenURI, [mbOK], mbError, mbOK, mbOK); + Table.Enabled := True; + CloseButton.Enabled := True; + ConnectButton.Enabled := True; + ConnectButton.Visible := True; + StopButton.Visible := False; + StopButton.Enabled := False; + URIComboBox.Entry.SetFocus; + Engine.Free; + Exit; + end; + + Engine.Password := ''; // Security precaution + StopButton.Enabled := False; + ConnectedEngine := Engine; + + if ConnectionManager = nil then AFConnectionManager.Free; + ModalResult := mbOK; +end; + + + +end. + + diff --git a/libgtk_kylix/GTKStdCtrls.pas b/libgtk_kylix/GTKStdCtrls.pas index 1cccdf8..f61560f 100644 --- a/libgtk_kylix/GTKStdCtrls.pas +++ b/libgtk_kylix/GTKStdCtrls.pas @@ -1,6 +1,6 @@ (* GTK-Kylix Library: GTKStdCtrls - Standard visual controls (such as buttons, labels, entry) - Version 0.6.23 (last updated 2007-12-08) + Version 0.6.24 (last updated 2008-11-17) Copyright (C) 2007 Tomas Bzatek <tbzatek@users.sourceforge.net> This library is free software; you can redistribute it and/or @@ -797,6 +797,7 @@ begin inherited Create(AOwner); FLinked := True; FWidget := Widget; + g_signal_connect(PGtkObject(FWidget), 'changed', G_CALLBACK(@TGTKEditable_Changed), Self); end; destructor TGTKEntry.Destroy; @@ -65,6 +65,7 @@ uses UGlibThreads in 'UGlibThreads.pas', URunFromVFS in 'URunFromVFS.pas', ULibc in 'ULibc.pas', + UQuickConnect in 'UQuickConnect.pas', UTranslation_EN in 'translations/UTranslation_EN.pas', UTranslation_CZ in 'translations/UTranslation_CZ.pas', UTranslation_RU in 'translations/UTranslation_RU.pas', diff --git a/vfs/UVFSCore.pas b/vfs/UVFSCore.pas index 7531e9c..6cd1114 100644 --- a/vfs/UVFSCore.pas +++ b/vfs/UVFSCore.pas @@ -38,6 +38,7 @@ type FVFSListClose: TVFSListClose; FVFSChangeDir: TVFSChangeDir; FVFSGetPath: TVFSGetPath; + FVFSGetPathURI: TVFSGetPathURI; FVFSGetPrefix: TVFSGetPrefix; FVFSGetFileSystemSize: TVFSGetFileSystemSize; FVFSGetFileSystemFree: TVFSGetFileSystemFree; @@ -93,6 +94,7 @@ type Password: string; PasswordUsed: boolean; RemoveFileOnClose: string; + OpenedFromQuickConnect: boolean; constructor Create(SourcePlugin: TVFSPlugin); function VFSOpenURI(URI: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; function VFSOpenEx(OpenFile: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): TVFSResult; @@ -132,6 +134,7 @@ type function GetPath: string; override; procedure SetPath(Value: string); override; function GetPrefix: string; override; + function GetPathURI: string; function GetBlockSize: Cardinal; override; procedure SetBlockSize(Value: Cardinal); override; @@ -195,6 +198,7 @@ begin @FVFSListNext := dlsym(ModuleHandle, 'VFSListNext'); @FVFSListClose := dlsym(ModuleHandle, 'VFSListClose'); @FVFSGetPath := dlsym(ModuleHandle, 'VFSGetPath'); + @FVFSGetPathURI := dlsym(ModuleHandle, 'VFSGetPathURI'); @FVFSChangeDir := dlsym(ModuleHandle, 'VFSChangeDir'); @FVFSGetPrefix := dlsym(ModuleHandle, 'VFSGetPrefix'); @FVFSGetFileSystemSize := dlsym(ModuleHandle, 'VFSGetFileSystemSize'); @@ -296,6 +300,7 @@ begin Password := ''; PasswordUsed := False; RemoveFileOnClose := ''; + OpenedFromQuickConnect := False; if @FSourcePlugin.FVFSNew <> nil then FGlobs := FSourcePlugin.FVFSNew(@VFSLogFunc); end; @@ -506,6 +511,16 @@ begin else Result := '/'; end; +function TVFSEngine.GetPathURI: string; +var x: PChar; +begin + Result := ''; + if (FGlobs <> nil) and (@FSourcePlugin.FVFSGetPathURI <> nil) then begin + x := FSourcePlugin.FVFSGetPathURI(FGlobs); + if x <> nil then Result := string(x); + end; +end; + function TVFSEngine.ChangeDir(const NewPath: string): integer; begin DebugMsg(['^^VFS (II): ChangeDir begin']); diff --git a/vfs/uVFSprototypes.pas b/vfs/uVFSprototypes.pas index 35d6d8f..a67b303 100644 --- a/vfs/uVFSprototypes.pas +++ b/vfs/uVFSprototypes.pas @@ -232,6 +232,9 @@ type // Try to change the directory when correct permissions TVFSGetPath = function (g:TVFSGlobs): PChar; cdecl; // Returns the current working path (not all plugins can support this; just return '/' in this case) + TVFSGetPathURI = function (g:TVFSGlobs): PChar; cdecl; + // Returns the current working path in the URI form + // !!!!!!!!!!! k temhle (a vsem ostatnim) funkcim pridat komentar, kdo ma vubec pamet dealokovat + udelat review pluginus TVFSGetFileSystemSize = function (g:TVFSGlobs; const APath: PChar): Int64; cdecl; // Gets the size of filesystem; the path is optional, specified to recognize various mounted filesystems in the tree TVFSGetFileSystemFree = function (g:TVFSGlobs; const APath: PChar): Int64; cdecl; |
