(* Tux Commander - UConnectionManager - Connection manager dialog Copyright (C) 2004 Tomas Bzatek 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 UConnectionManager; interface uses glib2, gdk2, gtk2, pango, SysUtils, Types, Classes, GTKControls, GTKForms, GTKStdCtrls, GTKExtCtrls, GTKConsts, GTKView, GTKUtils, GTKDialogs, GTKPixbuf, GTKClasses, UCore, UCoreClasses, UVFSCore, UEngines, URemoteWait; type TFConnectionManager = class(TGTKDialog) TitleFrame: TGTKFrame; TitleLabel: TGTKLabel; TitleEventBox: TGTKEventBox; TitleIcon: TGTKImage; TitleHBox: TGTKHBox; ListView: TGTKListView; ListViewScrolledWindow: TGTKScrolledWindow; ListViewTable: TGTKTable; AddConnectionButton, EditButton, RemoveButton: TGTKImageButton; ButtonBox: TGTKVButtonBox; ActionButtonBox: TGTKHButtonBox; ConnectButton, CloseButton: TGTKButton; procedure FormCreate(Sender: TObject); override; procedure ListViewSelectionChanged(Sender: TObject); procedure FormKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); procedure AddConnectionButtonClick(Sender: TObject); procedure EditButtonClick(Sender: TObject); procedure RemoveButtonClick(Sender: TObject); procedure ListViewDblClick(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); procedure ListViewKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); procedure CloseButtonClick(Sender: TObject); procedure ConnectButtonClick(Sender: TObject); private FSilenceError: boolean; AFRemoteWait: TFRemoteWait; FActiveConnInfo: TConnMgrItem; FVFSAskPasswordTry: integer; procedure FillList; procedure DoConnect; public SourcePanelEngine: TPanelEngine; ConnectedEngine: TVFSEngine; end; var FConnectionManager: TFConnectionManager; implementation uses ULocale, UCoreUtils, UConfig, UConnectionProperties, UGnome, uVFSprototypes; procedure TFConnectionManager.FormCreate(Sender: TObject); var Column: TGTKTreeViewColumn; begin ConnectedEngine := nil; SetDefaultSize(550, 450); Caption := LANGConnMgr_Caption; Buttons := []; ShowSeparator := False; ConnectButton := TGTKButton.Create(Self); ConnectButton.Caption := LANGConnMgr_ConnectButton; // ConnectButton.Default := True; CloseButton := TGTKButton.CreateFromStock(Self, GTK_STOCK_CLOSE); // Default := ConnectButton; ActionButtonBox := TGTKHButtonBox.Create(Self); ActionButtonBox.Layout := blEnd; ActionButtonBox.Spacing := 10; ActionButtonBox.BorderWidth := 0; ActionButtonBox.AddControlEnd(CloseButton); ActionButtonBox.AddControlEnd(ConnectButton); ActionArea.AddControlEx(ActionButtonBox, False, False, 0); TitleEventBox := TGTKEventBox.Create(Self); TitleLabel := TGTKLabel.Create(Self); TitleLabel.Caption := Format('%s', [LANGConnMgr_OpenConnection]); 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); ListViewTable := TGTKTable.Create(Self); ListViewTable.BorderWidth := 7; ClientArea.AddControlEx(ListViewTable, True, True, 0); ListView := TGTKListView.CreateTyped(Self, False, [lcPointer, lcText, lcText]); ListView.OnKeyDown := ListViewKeyDown; ListView.RulesHint := True; ListView.ShowHeaders := True; Column := ListView.Columns.Add; Column.AddAttribute('text', 1); Column.Resizable := True; Column.FixedWidth := 200; Column.SizingMode := smFixed; Column.Caption := LANGConnMgr_NameColumn; Column.Clickable := True; Column.SortID := 1; Column := ListView.Columns.Add; Column.AddAttribute('text', 2); Column.Resizable := True; Column.SizingMode := smFixed; Column.Caption := LANGConnMgr_URIColumn; Column.Clickable := True; Column.SortID := 2; ListViewScrolledWindow := TGTKScrolledWindow.Create(Self); ListViewScrolledWindow.AddControl(ListView); ListViewScrolledWindow.HorizScrollBarPolicy := sbAutomatic; ListViewScrolledWindow.VertScrollBarPolicy := sbAutomatic; ListViewScrolledWindow.ShadowType := stShadowIn; AddConnectionButton := TGTKImageButton.Create(Self); AddConnectionButton.SetFromStock('gtk-add', isSmallToolbar); AddConnectionButton.Caption := LANGConnMgr_AddConnectionButtonCaption; AddConnectionButton.UseUnderline := True; AddConnectionButton.Tooltip := LANGConnMgr_AddConnectionButtonTooltip; EditButton := TGTKImageButton.Create(Self); EditButton.Caption := LANGConnMgr_EditButtonCaption; EditButton.UseUnderline := True; EditButton.Tooltip := LANGConnMgr_EditButtonTooltip; RemoveButton := TGTKImageButton.Create(Self); RemoveButton.SetFromStock('gtk-remove', isSmallToolbar); RemoveButton.Caption := LANGConnMgr_RemoveButtonCaption; RemoveButton.UseUnderline := True; RemoveButton.Tooltip := LANGConnMgr_RemoveButtonTooltip; ButtonBox := TGTKVButtonBox.Create(Self); ButtonBox.Spacing := 10; ButtonBox.AddControl(AddConnectionButton); ButtonBox.AddControl(EditButton); ButtonBox.AddControl(RemoveButton); ListViewTable.AddControlEx(0, 1, 3, 5, ListViewScrolledWindow, [taoExpand, taoFill], [taoExpand, taoFill], 0, 5); 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); FillList; ListView.OnSelectionChanged := ListViewSelectionChanged; ListView.OnDblClick := ListViewDblClick; ListView.SetFocus; if ListView.Items.Count > 0 then ListView.Items[ConfConnMgrActiveItem].SetCursor(0, False, False, 0, 0); ListViewSelectionChanged(Self); AddConnectionButton.OnClick := AddConnectionButtonClick; EditButton.OnClick := EditButtonClick; RemoveButton.OnClick := RemoveButtonClick; CloseButton.OnClick := CloseButtonClick; ConnectButton.OnClick := ConnectButtonClick; OnKeyDown := FormKeyDown; end; procedure TFConnectionManager.ListViewSelectionChanged(Sender: TObject); begin try EditButton.Enabled := Assigned(ListView.Selected); RemoveButton.Enabled := Assigned(ListView.Selected); ConnectButton.Enabled := Assigned(ListView.Selected); except end; 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; end; end; procedure TFConnectionManager.ListViewDblClick(Sender: TObject; Button: TGDKMouseButton; Shift: TShiftState; X, Y: Integer; var Accept: boolean); begin if Assigned(ListView.Selected) then DoConnect; end; procedure TFConnectionManager.ListViewKeyDown(Sender: TObject; Key: Word; Shift: TShiftState; var Accept: boolean); begin case Key of GDK_Delete_Key: RemoveButtonClick(Sender); end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) procedure TFConnectionManager.AddConnectionButtonClick(Sender: TObject); var Item: TGTKListItem; ConnMgrItem: TConnMgrItem; begin try FConnectionProperties := TFConnectionProperties.Create(Self); if FConnectionProperties.Run = mbOK then begin ConnMgrItem := TConnMgrItem.Create; with FConnectionProperties do begin ConnMgrItem.ConnectionName := NameEntry.Text; ConnMgrItem.ServiceType := GetService; ConnMgrItem.Server := ServerEntry.Text; ConnMgrItem.Username := UserNameEntry.Text; ConnMgrItem.Password := PasswordEntry.Text; ConnMgrItem.TargetDir := TargetDirEntry.Text; ConnMgrItem.PluginID := ''; if PluginOptionMenu.ItemIndex <> 0 then ConnMgrItem.PluginID := TVFSPlugin(PluginList[PluginOptionMenu.ItemIndex - 1]).VFSName; end; ConnectionMgrList.Add(ConnMgrItem); Item := ListView.Items.Add; Item.SetValue(0, ConnMgrItem); Item.SetValue(1, ConnMgrItem.ConnectionName); Item.SetValue(2, FConnectionProperties.MakeURI(True)); end; finally FConnectionProperties.Free; end; end; procedure TFConnectionManager.EditButtonClick(Sender: TObject); var Item: TGTKListItem; ConnMgrItem: TConnMgrItem; i: integer; begin try Item := ListView.Selected; if Item = nil then Exit; ConnMgrItem := Item.AsPointer(0); if ConnMgrItem = nil then Exit; FConnectionProperties := TFConnectionProperties.Create(Self); FConnectionProperties.NameEntry.Text := ConnMgrItem.ConnectionName; FConnectionProperties.URIEntry.Text := ConstructURI(True, False, ConnMgrItem.ServiceType, ConnMgrItem.Server, ConnMgrItem.Username, ConnMgrItem.Password, ConnMgrItem.TargetDir); FConnectionProperties.ServiceTypeOptionMenuChanged(Sender); // Find the plugin by PluginID for i := 0 to PluginList.Count - 1 do if TVFSPlugin(PluginList[i]).VFSName = ConnMgrItem.PluginID then begin FConnectionProperties.PluginOptionMenu.ItemIndex := i + 1; Break; end; if FConnectionProperties.Run = mbOK then begin with FConnectionProperties do begin ConnMgrItem.ConnectionName := NameEntry.Text; ConnMgrItem.ServiceType := GetService; ConnMgrItem.Server := ServerEntry.Text; ConnMgrItem.Username := UserNameEntry.Text; ConnMgrItem.Password := PasswordEntry.Text; ConnMgrItem.TargetDir := TargetDirEntry.Text; ConnMgrItem.PluginID := ''; if PluginOptionMenu.ItemIndex <> 0 then ConnMgrItem.PluginID := TVFSPlugin(PluginList[PluginOptionMenu.ItemIndex - 1]).VFSName; end; Item.SetValue(1, ConnMgrItem.ConnectionName); Item.SetValue(2, FConnectionProperties.MakeURI(True)); end; finally FConnectionProperties.Free; end; end; procedure TFConnectionManager.RemoveButtonClick(Sender: TObject); var Item: TGTKListItem; ConnMgrItem: TConnMgrItem; i: integer; begin Item := ListView.Selected; if Item = nil then Exit; ConnMgrItem := Item.AsPointer(0); if ConnMgrItem = nil then Exit; if Application.MessageBox(Format(LANGConnMgr_DoYouWantDelete, [Item.AsString(1)]), [mbYes, mbNo], mbQuestion, mbYes, mbNo) = mbYes then begin if ConnectionMgrList.Count > 0 then for i := 0 to ConnectionMgrList.Count - 1 do if ConnectionMgrList[i] = ConnMgrItem then begin ConnectionMgrList.Delete(i); Break; end; ConnMgrItem.Free; ListView.Items.Delete(Item.Index); end; end; procedure TFConnectionManager.FillList; 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]; Item := ListView.Items.Add; Item.SetValue(0, ConnMgrItem); Item.SetValue(1, ConnMgrItem.ConnectionName); Item.SetValue(2, ConstructURI(True, True, ConnMgrItem.ServiceType, ConnMgrItem.Server, ConnMgrItem.Username, ConnMgrItem.Password, ConnMgrItem.TargetDir)); end; if (ConfConnMgrActiveItem < 0) or (ConfConnMgrActiveItem > ConnectionMgrList.Count) then ConfConnMgrActiveItem := 0; end; (********************************************************************************************************************************) procedure TFConnectionManager.CloseButtonClick(Sender: TObject); begin ModalResult := mbCancel; end; procedure TFConnectionManager.ConnectButtonClick(Sender: TObject); 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; 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; 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 ConnMgr: TFConnectionManager; DialogParent: PGtkWidget; 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; flags := flags or VFS_ASK_PASSWORD_SAVE_INTERNAL; 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) { and ConfGnomeKeyringEnabled} then password_save^ := VFS_PASSWORD_SAVE_PERMANENTLY; end else begin // Show password dialog and continue in loop Result := HandleVFSAskPasswordCallback(DialogParent, AMessage, default_user, default_domain, 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) then begin DebugMsg(['(II) vfs_ask_password_callback: Saving password to Connection Manager']); ConnMgr.FActiveConnInfo.Password := password^; end; end; end; end; procedure TFConnectionManager.DoConnect; var Engine: TVFSEngine; i, j: integer; VFSPlugin: TVFSPlugin; b: boolean; begin FSilenceError := False; FActiveConnInfo := nil; if ListView.Selected <> nil then FActiveConnInfo := ListView.Selected.AsPointer(0); if FActiveConnInfo = nil then Exit; 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 Length(FActiveConnInfo.PluginID) > 0 then for i := 0 to PluginList.Count - 1 do if TVFSPlugin(PluginList[i]).VFSName = FActiveConnInfo.PluginID then begin VFSPlugin := PluginList[i]; Break; end; if VFSPlugin = nil then begin 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], FActiveConnInfo.ServiceType) = 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; // Construct the VFS Engine and try to open the connection Engine := TVFSEngine.Create(VFSPlugin); Engine.ParentEngine := SourcePanelEngine; Engine.SavePath := SourcePanelEngine.Path; 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, Self); AFRemoteWait.Free; AFRemoteWait := nil; if not b then begin if not FSilenceError then Application.MessageBox(PGtkWindow(FWidget), LANGCouldntOpenURI, [mbOK], mbError, mbOK, mbOK); Exit; end; ConnectedEngine := Engine; ModalResult := mbOK; end; end.