#include "FileZilla.h" #include "sitemanager.h" #include "Options.h" #include "../tinyxml/tinyxml.h" #include "xmlfunctions.h" #include "filezillaapp.h" #include "ipcmutex.h" #include "wrapengine.h" #include "conditionaldialog.h" std::map CSiteManager::m_idMap; BEGIN_EVENT_TABLE(CSiteManager, wxDialogEx) EVT_BUTTON(XRCID("wxID_OK"), CSiteManager::OnOK) EVT_BUTTON(XRCID("wxID_CANCEL"), CSiteManager::OnCancel) EVT_BUTTON(XRCID("ID_CONNECT"), CSiteManager::OnConnect) EVT_BUTTON(XRCID("ID_NEWSITE"), CSiteManager::OnNewSite) EVT_BUTTON(XRCID("ID_NEWFOLDER"), CSiteManager::OnNewFolder) EVT_BUTTON(XRCID("ID_RENAME"), CSiteManager::OnRename) EVT_BUTTON(XRCID("ID_DELETE"), CSiteManager::OnDelete) EVT_TREE_BEGIN_LABEL_EDIT(XRCID("ID_SITETREE"), CSiteManager::OnBeginLabelEdit) EVT_TREE_END_LABEL_EDIT(XRCID("ID_SITETREE"), CSiteManager::OnEndLabelEdit) EVT_TREE_SEL_CHANGING(XRCID("ID_SITETREE"), CSiteManager::OnSelChanging) EVT_TREE_SEL_CHANGED(XRCID("ID_SITETREE"), CSiteManager::OnSelChanged) EVT_CHOICE(XRCID("ID_LOGONTYPE"), CSiteManager::OnLogontypeSelChanged) EVT_BUTTON(XRCID("ID_BROWSE"), CSiteManager::OnRemoteDirBrowse) EVT_TREE_ITEM_ACTIVATED(XRCID("ID_SITETREE"), CSiteManager::OnItemActivated) EVT_CHECKBOX(XRCID("ID_LIMITMULTIPLE"), CSiteManager::OnLimitMultipleConnectionsChanged) EVT_RADIOBUTTON(XRCID("ID_CHARSET_AUTO"), CSiteManager::OnCharsetChange) EVT_RADIOBUTTON(XRCID("ID_CHARSET_UTF8"), CSiteManager::OnCharsetChange) EVT_RADIOBUTTON(XRCID("ID_CHARSET_CUSTOM"), CSiteManager::OnCharsetChange) EVT_CHOICE(XRCID("ID_PROTOCOL"), CSiteManager::OnProtocolSelChanged) EVT_BUTTON(XRCID("ID_COPY"), CSiteManager::OnCopySite) #ifdef __WXGTK__ EVT_SPINCTRL(-1, CSiteManager::OnTimezoneOffsetChanged) #endif END_EVENT_TABLE() CSiteManager::CSiteManager() { m_pSiteManagerMutex = 0; #ifdef __WXGTK__ m_timezoneOffsetHoursChanged; m_timezoneOffsetMinutesChanged; m_limitConnectionsChanged; #endif } CSiteManager::~CSiteManager() { delete m_pSiteManagerMutex; } bool CSiteManager::Create(wxWindow* parent) { m_pSiteManagerMutex = new CInterProcessMutex(MUTEX_SITEMANAGERGLOBAL, false); if (!m_pSiteManagerMutex->TryLock()) { int answer = wxMessageBox(_("The Site Manager is opened in another instance of FileZilla 3.\nDo you want to continue? Any changes made in the Site Manager won't be saved then."), _("Site Manager already open"), wxYES_NO | wxICON_QUESTION); if (answer != wxYES) return false; delete m_pSiteManagerMutex; m_pSiteManagerMutex = 0; } SetExtraStyle(wxWS_EX_BLOCK_EVENTS); SetParent(parent); CreateControls(); // Now create the imagelist for the site tree wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return false; wxImageList* pImageList = new wxImageList(16, 16); pImageList->Add(wxArtProvider::GetBitmap(_T("ART_FOLDERCLOSED"), wxART_OTHER, wxSize(16, 16))); pImageList->Add(wxArtProvider::GetBitmap(_T("ART_FOLDER"), wxART_OTHER, wxSize(16, 16))); pImageList->Add(wxArtProvider::GetBitmap(_T("ART_SERVER"), wxART_OTHER, wxSize(16, 16))); pTree->AssignImageList(pImageList); Layout(); wxGetApp().GetWrapEngine()->WrapRecursive(this, 1.33, "Site Manager"); wxSize minSize = GetSizer()->GetMinSize(); wxSize size = GetSize(); wxSize clientSize = GetClientSize(); SetMinSize(GetSizer()->GetMinSize() + size - clientSize); SetClientSize(minSize); Load(); XRCCTRL(*this, "ID_TRANSFERMODE_DEFAULT", wxRadioButton)->Update(); XRCCTRL(*this, "ID_TRANSFERMODE_ACTIVE", wxRadioButton)->Update(); XRCCTRL(*this, "ID_TRANSFERMODE_PASSIVE", wxRadioButton)->Update(); SetCtrlState(); return true; } void CSiteManager::CreateControls() { wxXmlResource::Get()->LoadDialog(this, GetParent(), _T("ID_SITEMANAGER")); wxChoice *pProtocol = XRCCTRL(*this, "ID_PROTOCOL", wxChoice); pProtocol->Append(CServer::GetProtocolName(FTP)); pProtocol->Append(CServer::GetProtocolName(SFTP)); pProtocol->Append(CServer::GetProtocolName(FTPS)); pProtocol->Append(CServer::GetProtocolName(FTPES)); wxChoice *pChoice = XRCCTRL(*this, "ID_SERVERTYPE", wxChoice); wxASSERT(pChoice); pChoice->Append(_T("Unix")); pChoice->Append(_T("VMS")); pChoice->Append(_T("MVS")); pChoice->Append(_T("Dos")); pChoice->Append(_T("VxWorks")); } void CSiteManager::OnOK(wxCommandEvent& event) { if (!Verify()) return; UpdateServer(); Save(); EndModal(wxID_OK); } void CSiteManager::OnCancel(wxCommandEvent& event) { EndModal(wxID_CANCEL); } void CSiteManager::OnConnect(wxCommandEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk()) return; CSiteManagerItemData* data = reinterpret_cast(pTree->GetItemData(item)); if (!data) { wxBell(); return; } if (!Verify()) { wxBell(); return; } UpdateServer(); Save(); EndModal(wxID_YES); } class CSiteManagerXmlHandler { public: virtual ~CSiteManagerXmlHandler() {}; // Adds a folder and descents virtual bool AddFolder(const wxString& name) = 0; virtual bool AddSite(const wxString& name, CSiteManagerItemData* data) = 0; // Go up a level virtual bool LevelUp() = 0; // *Ding* }; class CSiteManagerXmlHandler_Tree : public CSiteManagerXmlHandler { public: CSiteManagerXmlHandler_Tree(wxTreeCtrl* pTree, wxTreeItemId root) : m_pTree(pTree), m_item(root) { } virtual ~CSiteManagerXmlHandler_Tree() { m_pTree->SortChildren(m_item); m_pTree->Expand(m_item); } virtual bool AddFolder(const wxString& name) { wxTreeItemId newItem = m_pTree->AppendItem(m_item, name, 0, 0); m_pTree->SetItemImage(newItem, 1, wxTreeItemIcon_Expanded); m_pTree->SetItemImage(newItem, 1, wxTreeItemIcon_SelectedExpanded); m_item = newItem; return true; } virtual bool AddSite(const wxString& name, CSiteManagerItemData* data) { m_pTree->AppendItem(m_item, name, 2, 2, data); return true; } virtual bool LevelUp() { m_pTree->SortChildren(m_item); m_pTree->Expand(m_item); wxTreeItemId parent = m_pTree->GetItemParent(m_item); if (!parent) return false; m_item = parent; return true; } protected: wxTreeCtrl* m_pTree; wxTreeItemId m_item; }; bool CSiteManager::Load() { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return false; pTree->DeleteAllItems(); // We have to synchronize access to sitemanager.xml so that multiple processed don't write // to the same file or one is reading while the other one writes. CInterProcessMutex mutex(MUTEX_SITEMANAGER); // Load default sites bool hasDefaultSites = LoadDefaultSites(); if (hasDefaultSites) m_ownSites = pTree->AppendItem(pTree->GetRootItem(), _("My Sites"), 0, 0); else m_ownSites = pTree->AddRoot(_("My Sites"), 0, 0); wxTreeItemId treeId = m_ownSites; pTree->SelectItem(treeId); pTree->SetItemImage(treeId, 1, wxTreeItemIcon_Expanded); pTree->SetItemImage(treeId, 1, wxTreeItemIcon_SelectedExpanded); CXmlFile file(_T("sitemanager")); TiXmlElement* pDocument = file.Load(); if (!pDocument) { wxString msg = wxString::Format(_("Could not load \"%s\", please make sure the file is valid and can be accessed.\nAny changes made in the Site Manager will not be saved."), file.GetFileName().GetFullPath().c_str()); wxMessageBox(msg, _("Error loading xml file"), wxICON_ERROR); return false; } TiXmlElement* pElement = pDocument->FirstChildElement("Servers"); if (!pElement) return true; CSiteManagerXmlHandler_Tree handler(pTree, treeId); bool res = Load(pElement, &handler); pTree->SortChildren(treeId); pTree->Expand(treeId); pTree->SelectItem(treeId); return res; } bool CSiteManager::Load(TiXmlElement *pElement, CSiteManagerXmlHandler* pHandler) { wxASSERT(pElement); wxASSERT(pHandler); for (TiXmlElement* pChild = pElement->FirstChildElement(); pChild; pChild = pChild->NextSiblingElement()) { TiXmlNode* pNode = pChild->FirstChild(); while (pNode && !pNode->ToText()) pNode = pNode->NextSibling(); if (!pNode) continue; wxString name = ConvLocal(pNode->ToText()->Value()); name.Trim(true); name.Trim(false); if (!strcmp(pChild->Value(), "Folder")) { if (!pHandler->AddFolder(name)) return false; Load(pChild, pHandler); if (!pHandler->LevelUp()) return false; } else if (!strcmp(pChild->Value(), "Server")) { CSiteManagerItemData* data = ReadServerElement(pChild); if (data) pHandler->AddSite(name, data); } } return true; } CSiteManagerItemData* CSiteManager::ReadServerElement(TiXmlElement *pElement) { CServer server; if (!::GetServer(pElement, server)) return 0; CSiteManagerItemData* data = new CSiteManagerItemData(server); TiXmlHandle handle(pElement); TiXmlText* comments = handle.FirstChildElement("Comments").FirstChild().Text(); if (comments) data->m_comments = ConvLocal(comments->Value()); TiXmlText* localDir = handle.FirstChildElement("LocalDir").FirstChild().Text(); if (localDir) data->m_localDir = ConvLocal(localDir->Value()); TiXmlText* remoteDir = handle.FirstChildElement("RemoteDir").FirstChild().Text(); if (remoteDir) data->m_remoteDir.SetSafePath(ConvLocal(remoteDir->Value())); return data; } bool CSiteManager::Save(TiXmlElement *pElement /*=0*/, wxTreeItemId treeId /*=wxTreeItemId()*/) { if (!m_pSiteManagerMutex) return false; wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return false; if (!pElement || !treeId) { // We have to synchronize access to sitemanager.xml so that multiple processed don't write // to the same file or one is reading while the other one writes. CInterProcessMutex mutex(MUTEX_SITEMANAGER); wxFileName file(wxGetApp().GetSettingsDir(), _T("sitemanager.xml")); TiXmlElement* pDocument = GetXmlFile(file); if (!pDocument) { wxString msg = wxString::Format(_("Could not load \"%s\", please make sure the file is valid and can be accessed.\nAny changes made in the Site Manager could not be saved."), file.GetFullPath().c_str()); wxMessageBox(msg, _("Error loading xml file"), wxICON_ERROR); return false; } TiXmlElement *pServers = pDocument->FirstChildElement("Servers"); while (pServers) { pDocument->RemoveChild(pServers); pServers = pDocument->FirstChildElement("Servers"); } pElement = pDocument->InsertEndChild(TiXmlElement("Servers"))->ToElement(); if (!pElement) { delete pDocument->GetDocument(); return true; } bool res = Save(pElement, m_ownSites); wxString error; if (!SaveXmlFile(file, pDocument, &error)) { wxString msg = wxString::Format(_("Could not write \"%s\", any changes to the Site Manager could not be saved: %s"), file.GetFullPath().c_str(), error.c_str()); wxMessageBox(msg, _("Error writing xml file"), wxICON_ERROR); } delete pDocument->GetDocument(); return res; } wxTreeItemId child; wxTreeItemIdValue cookie; child = pTree->GetFirstChild(treeId, cookie); while (child.IsOk()) { wxString name = pTree->GetItemText(child); char* utf8 = ConvUTF8(name); CSiteManagerItemData* data = reinterpret_cast(pTree->GetItemData(child)); if (!data) { TiXmlNode* pNode = pElement->InsertEndChild(TiXmlElement("Folder")); pNode->InsertEndChild(TiXmlText(utf8)); delete [] utf8; Save(pNode->ToElement(), child); } else { TiXmlNode* pNode = pElement->InsertEndChild(TiXmlElement("Server")); SetServer(pNode->ToElement(), data->m_server); TiXmlNode* sub; // Save comments sub = pNode->InsertEndChild(TiXmlElement("Comments")); char* comments = ConvUTF8(data->m_comments); sub->InsertEndChild(TiXmlText(comments)); delete [] comments; // Save local dir sub = pNode->InsertEndChild(TiXmlElement("LocalDir")); char* localDir = ConvUTF8(data->m_localDir); sub->InsertEndChild(TiXmlText(localDir)); delete [] localDir; // Save remote dir sub = pNode->InsertEndChild(TiXmlElement("RemoteDir")); char* remoteDir = ConvUTF8(data->m_remoteDir.GetSafePath()); sub->InsertEndChild(TiXmlText(remoteDir)); delete [] remoteDir; pNode->InsertEndChild(TiXmlText(utf8)); delete [] utf8; } child = pTree->GetNextChild(treeId, cookie); } return false; } void CSiteManager::OnNewFolder(wxCommandEvent&event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk()) return; if (pTree->GetItemData(item)) return; if (!Verify()) return; wxString newName = _("New folder"); int index = 2; while (true) { wxTreeItemId child; wxTreeItemIdValue cookie; child = pTree->GetFirstChild(item, cookie); bool found = false; while (child.IsOk()) { wxString name = pTree->GetItemText(child); int cmp = name.CmpNoCase(newName); if (!cmp) { found = true; break; } child = pTree->GetNextChild(item, cookie); } if (!found) break; newName = _("New folder") + wxString::Format(_T(" %d"), index++); } wxTreeItemId newItem = pTree->AppendItem(item, newName, 0, 0); pTree->SetItemImage(newItem, 1, wxTreeItemIcon_Expanded); pTree->SetItemImage(newItem, 1, wxTreeItemIcon_SelectedExpanded); pTree->SortChildren(item); pTree->SelectItem(newItem); pTree->Expand(item); pTree->EditLabel(newItem); } bool CSiteManager::Verify() { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return true; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk()) return true; wxTreeItemData* data = pTree->GetItemData(item); if (!data) return true; const wxString& host = XRCCTRL(*this, "ID_HOST", wxTextCtrl)->GetValue(); if (host == _T("")) { XRCCTRL(*this, "ID_HOST", wxTextCtrl)->SetFocus(); wxMessageBox(_("You have to enter a hostname.")); return false; } wxString protocolName = XRCCTRL(*this, "ID_PROTOCOL", wxChoice)->GetStringSelection(); enum ServerProtocol protocol = CServer::GetProtocolFromName(protocolName); if (protocol == SFTP && XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->GetStringSelection() == _("Account")) { XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetFocus(); wxMessageBox(_("'Account' logontype not supported by selected protocol")); return false; } CServer server; if (protocol != UNKNOWN) server.SetProtocol(protocol); unsigned long port; XRCCTRL(*this, "ID_PORT", wxTextCtrl)->GetValue().ToULong(&port); CServerPath path; wxString error; if (!server.ParseUrl(host, port, _T(""), _T(""), error, path)) { XRCCTRL(*this, "ID_HOST", wxTextCtrl)->SetFocus(); wxMessageBox(error); return false; } XRCCTRL(*this, "ID_HOST", wxTextCtrl)->SetValue(server.GetHost()); XRCCTRL(*this, "ID_PORT", wxTextCtrl)->SetValue(wxString::Format(_T("%d"), server.GetPort())); protocolName = CServer::GetProtocolName(server.GetProtocol()); if (protocolName == _T("")) CServer::GetProtocolName(FTP); XRCCTRL(*this, "ID_PROTOCOL", wxChoice)->SetStringSelection(protocolName); if (XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxRadioButton)->GetValue()) { if (XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->GetValue() == _T("")) { XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->SetFocus(); wxMessageBox(_("Need to specify a character encoding")); return false; } } // Require username for non-anonymous logon type if (XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->GetStringSelection() != _("Anonymous") && XRCCTRL(*this, "ID_USER", wxTextCtrl)->GetValue() == _T("")) { XRCCTRL(*this, "ID_USER", wxTextCtrl)->SetFocus(); wxMessageBox(_("You have to specify a user name")); return false; } // Require account for account logon type if (XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->GetStringSelection() == _("Account") && XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->GetValue() == _T("")) { XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->SetFocus(); wxMessageBox(_("You have to enter an account name")); return false; } return true; } void CSiteManager::OnBeginLabelEdit(wxTreeEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) { event.Veto(); return; } if (event.GetItem() != pTree->GetSelection()) { if (!Verify()) { event.Veto(); return; } } wxTreeItemId item = event.GetItem(); if (!item.IsOk() || item == pTree->GetRootItem() || item == m_ownSites || IsPredefinedItem(item)) { event.Veto(); return; } } void CSiteManager::OnEndLabelEdit(wxTreeEvent& event) { if (event.IsEditCancelled()) return; wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) { event.Veto(); return; } wxTreeItemId item = event.GetItem(); if (item != pTree->GetSelection()) { if (!Verify()) { event.Veto(); return; } } if (!item.IsOk() || item == pTree->GetRootItem() || item == m_ownSites || IsPredefinedItem(item)) { event.Veto(); return; } wxString name = event.GetLabel(); wxTreeItemId parent = pTree->GetItemParent(item); wxTreeItemId child; wxTreeItemIdValue cookie; for (wxTreeItemId child = pTree->GetFirstChild(parent, cookie); child.IsOk(); child = pTree->GetNextChild(parent, cookie)) { if (child == item) continue; if (!name.CmpNoCase(pTree->GetItemText(child))) { wxMessageBox(_("Name already exists"), _("Cannot rename entry"), wxICON_EXCLAMATION, this); event.Veto(); return; } } pTree->SortChildren(parent); } void CSiteManager::OnRename(wxCommandEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk() || item == pTree->GetRootItem() || item == m_ownSites || IsPredefinedItem(item)) return; pTree->EditLabel(item); } void CSiteManager::OnDelete(wxCommandEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk() || item == pTree->GetRootItem() || item == m_ownSites || IsPredefinedItem(item)) return; CConditionalDialog dlg(this, CConditionalDialog::sitemanager_confirmdelete, CConditionalDialog::yesno); dlg.SetTitle(_("Delete Site Manager entry")); dlg.AddText(_("Do you really want to delete selected entry?")); if (!dlg.Run()) return; pTree->Delete(item); } void CSiteManager::OnSelChanging(wxTreeEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; if (!Verify()) event.Veto(); UpdateServer(); } void CSiteManager::OnSelChanged(wxTreeEvent& event) { SetCtrlState(); } void CSiteManager::OnNewSite(wxCommandEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk() || IsPredefinedItem(item)) return; if (pTree->GetItemData(item)) return; if (!Verify()) return; wxString newName = _("New site"); int index = 2; while (true) { wxTreeItemId child; wxTreeItemIdValue cookie; child = pTree->GetFirstChild(item, cookie); bool found = false; while (child.IsOk()) { wxString name = pTree->GetItemText(child); int cmp = name.CmpNoCase(newName); if (!cmp) { found = true; break; } child = pTree->GetNextChild(item, cookie); } if (!found) break; newName = _("New site") + wxString::Format(_T(" %d"), index++); } wxTreeItemId newItem = pTree->AppendItem(item, newName, 2, 2, new CSiteManagerItemData()); pTree->SortChildren(item); pTree->SelectItem(newItem); pTree->Expand(item); pTree->EditLabel(newItem); } void CSiteManager::OnLogontypeSelChanged(wxCommandEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk()) return; CSiteManagerItemData* data = reinterpret_cast(pTree->GetItemData(item)); if (!data) return; XRCCTRL(*this, "ID_USER", wxTextCtrl)->Enable(event.GetString() != _("Anonymous")); XRCCTRL(*this, "ID_PASS", wxTextCtrl)->Enable(event.GetString() == _("Normal") || event.GetString() == _("Account")); XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->Enable(event.GetString() == _("Account")); } bool CSiteManager::UpdateServer() { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return false; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk()) return false; CSiteManagerItemData* data = reinterpret_cast(pTree->GetItemData(item)); if (!data) return false; if (IsPredefinedItem(item)) return true; unsigned long port; XRCCTRL(*this, "ID_PORT", wxTextCtrl)->GetValue().ToULong(&port); data->m_server.SetHost(XRCCTRL(*this, "ID_HOST", wxTextCtrl)->GetValue(), port); const wxString& protocolName = XRCCTRL(*this, "ID_PROTOCOL", wxChoice)->GetStringSelection(); const enum ServerProtocol protocol = CServer::GetProtocolFromName(protocolName); if (protocol != UNKNOWN) data->m_server.SetProtocol(protocol); else data->m_server.SetProtocol(FTP); wxString logonType = XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->GetStringSelection(); if (logonType == _("Normal")) data->m_server.SetLogonType(NORMAL); else if (logonType == _("Ask for password")) data->m_server.SetLogonType(ASK); else if (logonType == _("Interactive")) data->m_server.SetLogonType(INTERACTIVE); else if (logonType == _("Account")) data->m_server.SetLogonType(ACCOUNT); else data->m_server.SetLogonType(ANONYMOUS); data->m_server.SetUser(XRCCTRL(*this, "ID_USER", wxTextCtrl)->GetValue(), XRCCTRL(*this, "ID_PASS", wxTextCtrl)->GetValue()); data->m_server.SetAccount(XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->GetValue()); data->m_comments = XRCCTRL(*this, "ID_COMMENTS", wxTextCtrl)->GetValue(); wxString serverType = XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->GetStringSelection(); if (serverType == _T("Unix")) data->m_server.SetType(UNIX); else if (serverType == _T("Dos")) data->m_server.SetType(DOS); else if (serverType == _T("VMS")) data->m_server.SetType(VMS); else if (serverType == _T("MVS")) data->m_server.SetType(MVS); else if (serverType == _T("VxWorks")) data->m_server.SetType(VXWORKS); else data->m_server.SetType(DEFAULT); data->m_localDir = XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->GetValue(); data->m_remoteDir = CServerPath(); data->m_remoteDir.SetType(data->m_server.GetType()); data->m_remoteDir.SetPath(XRCCTRL(*this, "ID_REMOTEDIR", wxTextCtrl)->GetValue()); int hours, minutes; #ifdef __WXGTK__ if (!m_timezoneOffsetHoursChanged) hours = data->m_server.GetTimezoneOffset() / 60; else #endif hours = XRCCTRL(*this, "ID_TIMEZONE_HOURS", wxSpinCtrl)->GetValue(); #ifdef __WXGTK__ if (!m_timezoneOffsetMinutesChanged) minutes = data->m_server.GetTimezoneOffset() % 60; else #endif minutes = XRCCTRL(*this, "ID_TIMEZONE_MINUTES", wxSpinCtrl)->GetValue(); data->m_server.SetTimezoneOffset(hours * 60 + minutes); if (XRCCTRL(*this, "ID_TRANSFERMODE_ACTIVE", wxRadioButton)->GetValue()) data->m_server.SetPasvMode(MODE_ACTIVE); else if (XRCCTRL(*this, "ID_TRANSFERMODE_PASSIVE", wxRadioButton)->GetValue()) data->m_server.SetPasvMode(MODE_PASSIVE); else data->m_server.SetPasvMode(MODE_DEFAULT); if (XRCCTRL(*this, "ID_LIMITMULTIPLE", wxCheckBox)->GetValue()) { #ifdef __WXGTK__ if (m_limitConnectionsChanged) #endif data->m_server.MaximumMultipleConnections(XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->GetValue()); } else data->m_server.MaximumMultipleConnections(0); if (XRCCTRL(*this, "ID_CHARSET_UTF8", wxRadioButton)->GetValue()) data->m_server.SetEncodingType(ENCODING_UTF8); else if (XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxRadioButton)->GetValue()) { wxString encoding = XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->GetValue(); data->m_server.SetEncodingType(ENCODING_CUSTOM, encoding); } else data->m_server.SetEncodingType(ENCODING_AUTO); return true; } bool CSiteManager::GetServer(CSiteManagerItemData& data) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return false; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk()) return false; CSiteManagerItemData* pData = reinterpret_cast(pTree->GetItemData(item)); if (!pData) return false; data = *pData; data.m_name = pTree->GetItemText(item); return true; } void CSiteManager::OnRemoteDirBrowse(wxCommandEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk()) return; CSiteManagerItemData* data = reinterpret_cast(pTree->GetItemData(item)); if (!data) return; wxDirDialog dlg(this, _("Choose the default local directory"), XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->GetValue(), wxDD_NEW_DIR_BUTTON); if (dlg.ShowModal() == wxID_OK) { XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->SetValue(dlg.GetPath()); } } void CSiteManager::OnItemActivated(wxTreeEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk()) return; CSiteManagerItemData* data = reinterpret_cast(pTree->GetItemData(item)); if (!data) return; wxCommandEvent cmdEvent; OnConnect(cmdEvent); } void CSiteManager::OnLimitMultipleConnectionsChanged(wxCommandEvent& event) { XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->Enable(event.IsChecked()); } void CSiteManager::SetCtrlState() { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); const bool predefined = IsPredefinedItem(item); CSiteManagerItemData* data = 0; if (item.IsOk()) data = reinterpret_cast(pTree->GetItemData(item)); if (!data) { // Set the control states according if it's possible to use the control const bool root_or_predefined = (item == pTree->GetRootItem() || item == m_ownSites || predefined); XRCCTRL(*this, "ID_RENAME", wxWindow)->Enable(!root_or_predefined); XRCCTRL(*this, "ID_DELETE", wxWindow)->Enable(!root_or_predefined); XRCCTRL(*this, "ID_COPY", wxWindow)->Enable(false); XRCCTRL(*this, "ID_NOTEBOOK", wxWindow)->Enable(false); XRCCTRL(*this, "ID_NEWFOLDER", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_NEWSITE", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_CONNECT", wxWindow)->Enable(false); // Empty all site information XRCCTRL(*this, "ID_HOST", wxTextCtrl)->SetValue(_T("")); XRCCTRL(*this, "ID_PORT", wxTextCtrl)->SetValue(_T("")); XRCCTRL(*this, "ID_PROTOCOL", wxChoice)->SetStringSelection(_("FTP")); XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(_("Anonymous")); XRCCTRL(*this, "ID_USER", wxTextCtrl)->SetValue(_T("")); XRCCTRL(*this, "ID_PASS", wxTextCtrl)->SetValue(_T("")); XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->SetValue(_T("")); XRCCTRL(*this, "ID_COMMENTS", wxTextCtrl)->SetValue(_T("")); XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->SetStringSelection(_("Default")); XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->SetValue(_T("")); XRCCTRL(*this, "ID_REMOTEDIR", wxTextCtrl)->SetValue(_T("")); XRCCTRL(*this, "ID_TIMEZONE_HOURS", wxSpinCtrl)->SetValue(0); XRCCTRL(*this, "ID_TIMEZONE_MINUTES", wxSpinCtrl)->SetValue(0); XRCCTRL(*this, "ID_TRANSFERMODE_DEFAULT", wxRadioButton)->SetValue(true); XRCCTRL(*this, "ID_LIMITMULTIPLE", wxCheckBox)->SetValue(false); XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->SetValue(1); XRCCTRL(*this, "ID_CHARSET_AUTO", wxRadioButton)->SetValue(true); XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->SetValue(_T("")); } else { // Set the control states according if it's possible to use the control XRCCTRL(*this, "ID_RENAME", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_DELETE", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_COPY", wxWindow)->Enable(true); XRCCTRL(*this, "ID_NOTEBOOK", wxWindow)->Enable(true); XRCCTRL(*this, "ID_NEWFOLDER", wxWindow)->Enable(false); XRCCTRL(*this, "ID_NEWSITE", wxWindow)->Enable(false); XRCCTRL(*this, "ID_CONNECT", wxWindow)->Enable(true); XRCCTRL(*this, "ID_HOST", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_HOST", wxTextCtrl)->SetValue(data->m_server.GetHost()); unsigned int port = data->m_server.GetPort(); if (port != CServer::GetDefaultPort(data->m_server.GetProtocol())) XRCCTRL(*this, "ID_PORT", wxTextCtrl)->SetValue(wxString::Format(_T("%d"), port)); else XRCCTRL(*this, "ID_PORT", wxTextCtrl)->SetValue(_T("")); XRCCTRL(*this, "ID_PORT", wxWindow)->Enable(!predefined); const wxString& protocolName = CServer::GetProtocolName(data->m_server.GetProtocol()); if (protocolName != _T("")) XRCCTRL(*this, "ID_PROTOCOL", wxChoice)->SetStringSelection(protocolName); else XRCCTRL(*this, "ID_PROTOCOL", wxChoice)->SetStringSelection(CServer::GetProtocolName(FTP)); XRCCTRL(*this, "ID_PROTOCOL", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_USER", wxTextCtrl)->Enable(!predefined && data->m_server.GetLogonType() != ANONYMOUS); XRCCTRL(*this, "ID_PASS", wxTextCtrl)->Enable(!predefined && data->m_server.GetLogonType() == NORMAL || data->m_server.GetLogonType() == ACCOUNT); XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->Enable(!predefined && data->m_server.GetLogonType() == ACCOUNT); switch (data->m_server.GetLogonType()) { case NORMAL: XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(_("Normal")); break; case ASK: XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(_("Ask for password")); break; case INTERACTIVE: XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(_("Interactive")); break; case ACCOUNT: XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(_("Account")); break; default: XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(_("Anonymous")); break; } XRCCTRL(*this, "ID_LOGONTYPE", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_USER", wxTextCtrl)->SetValue(data->m_server.GetUser()); XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->SetValue(data->m_server.GetAccount()); XRCCTRL(*this, "ID_PASS", wxTextCtrl)->SetValue(data->m_server.GetPass()); XRCCTRL(*this, "ID_COMMENTS", wxTextCtrl)->SetValue(data->m_comments); XRCCTRL(*this, "ID_COMMENTS", wxWindow)->Enable(!predefined); switch (data->m_server.GetType()) { case UNIX: XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->SetStringSelection(_T("Unix")); break; case DOS: XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->SetStringSelection(_T("Dos")); break; case MVS: XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->SetStringSelection(_T("MVS")); break; case VMS: XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->SetStringSelection(_T("VMS")); break; case VXWORKS: XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->SetStringSelection(_T("VxWorks")); break; default: XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->SetStringSelection(_("Default")); break; } XRCCTRL(*this, "ID_SERVERTYPE", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->SetValue(data->m_localDir); XRCCTRL(*this, "ID_LOCALDIR", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_REMOTEDIR", wxTextCtrl)->SetValue(data->m_remoteDir.GetPath()); XRCCTRL(*this, "ID_REMOTEDIR", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_TIMEZONE_HOURS", wxSpinCtrl)->SetValue(data->m_server.GetTimezoneOffset() / 60); XRCCTRL(*this, "ID_TIMEZONE_HOURS", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_TIMEZONE_MINUTES", wxSpinCtrl)->SetValue(data->m_server.GetTimezoneOffset() % 60); XRCCTRL(*this, "ID_TIMEZONE_MINUTES", wxWindow)->Enable(!predefined); #ifdef __WXGTK__ m_timezoneOffsetHoursChanged = false; m_timezoneOffsetMinutesChanged = false; m_limitConnectionsChanged = false; #endif enum PasvMode pasvMode = data->m_server.GetPasvMode(); if (pasvMode == MODE_ACTIVE) XRCCTRL(*this, "ID_TRANSFERMODE_ACTIVE", wxRadioButton)->SetValue(true); else if (pasvMode == MODE_PASSIVE) XRCCTRL(*this, "ID_TRANSFERMODE_PASSIVE", wxRadioButton)->SetValue(true); else XRCCTRL(*this, "ID_TRANSFERMODE_DEFAULT", wxRadioButton)->SetValue(true); XRCCTRL(*this, "ID_TRANSFERMODE_ACTIVE", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_TRANSFERMODE_PASSIVE", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_TRANSFERMODE_DEFAULT", wxWindow)->Enable(!predefined); int maxMultiple = data->m_server.MaximumMultipleConnections(); XRCCTRL(*this, "ID_LIMITMULTIPLE", wxCheckBox)->SetValue(maxMultiple != 0); XRCCTRL(*this, "ID_LIMITMULTIPLE", wxWindow)->Enable(!predefined); if (maxMultiple != 0) { XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->Enable(!predefined); XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->SetValue(maxMultiple); } else { XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->Enable(false); XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->SetValue(1); } switch (data->m_server.GetEncodingType()) { default: case ENCODING_AUTO: XRCCTRL(*this, "ID_CHARSET_AUTO", wxRadioButton)->SetValue(true); break; case ENCODING_UTF8: XRCCTRL(*this, "ID_CHARSET_UTF8", wxRadioButton)->SetValue(true); break; case ENCODING_CUSTOM: XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxRadioButton)->SetValue(true); break; } XRCCTRL(*this, "ID_CHARSET_AUTO", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_CHARSET_UTF8", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxWindow)->Enable(!predefined); XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->Enable(!predefined && data->m_server.GetEncodingType() == ENCODING_CUSTOM); XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->SetValue(data->m_server.GetCustomEncoding()); } } void CSiteManager::OnCharsetChange(wxCommandEvent& event) { bool checked = XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxRadioButton)->GetValue(); XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->Enable(checked); } void CSiteManager::OnProtocolSelChanged(wxCommandEvent& event) { } void CSiteManager::OnCopySite(wxCommandEvent& event) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return; wxTreeItemId item = pTree->GetSelection(); if (!item.IsOk()) return; CSiteManagerItemData* data = reinterpret_cast(pTree->GetItemData(item)); if (!data) return; if (!Verify()) return; if (!UpdateServer()) return; wxTreeItemId parent; if (IsPredefinedItem(item)) parent = m_ownSites; else parent = pTree->GetItemParent(item); const wxString name = pTree->GetItemText(item); wxString newName = wxString::Format(_("Copy of %s"), name.c_str()); int index = 2; while (true) { wxTreeItemId child; wxTreeItemIdValue cookie; child = pTree->GetFirstChild(parent, cookie); bool found = false; while (child.IsOk()) { wxString name = pTree->GetItemText(child); int cmp = name.CmpNoCase(newName); if (!cmp) { found = true; break; } child = pTree->GetNextChild(parent, cookie); } if (!found) break; newName = wxString::Format(_("Copy (%d) of %s"), index++, name.c_str()); } CSiteManagerItemData* newData = new CSiteManagerItemData(); *newData = *data; wxTreeItemId newItem = pTree->AppendItem(parent, newName, 2, 2, newData); pTree->SortChildren(parent); pTree->SelectItem(newItem); pTree->Expand(parent); pTree->EditLabel(newItem); } bool CSiteManager::LoadDefaultSites() { const wxString& defaultsDir = wxGetApp().GetDefaultsDir(); if (defaultsDir == _T("")) return false; wxFileName name(defaultsDir, _T("fzdefaults.xml")); CXmlFile file(name); TiXmlElement* pDocument = file.Load(); if (!pDocument) return false; TiXmlElement* pElement = pDocument->FirstChildElement("Servers"); if (!pElement) return false; wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); if (!pTree) return false; int style = pTree->GetWindowStyle(); pTree->SetWindowStyle(style | wxTR_HIDE_ROOT); wxTreeItemId root = pTree->AddRoot(_T(""), 0, 0); m_predefinedSites = pTree->AppendItem(root, _("Predefined Sites"), 0, 0); pTree->SetItemImage(m_predefinedSites, 1, wxTreeItemIcon_Expanded); pTree->SetItemImage(m_predefinedSites, 1, wxTreeItemIcon_SelectedExpanded); CSiteManagerXmlHandler_Tree handler(pTree, m_predefinedSites); Load(pElement, &handler); return true; } bool CSiteManager::IsPredefinedItem(wxTreeItemId item) { wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl); wxASSERT(pTree); if (!pTree) return false; while (item) { if (item == m_predefinedSites) return true; item = pTree->GetItemParent(item); } return false; } class CSiteManagerXmlHandler_Menu : public CSiteManagerXmlHandler { public: CSiteManagerXmlHandler_Menu(wxMenu* pMenu, std::map *idMap) : m_pMenu(pMenu), m_idMap(idMap) { } virtual bool AddFolder(const wxString& name) { m_parents.push_back(m_pMenu); m_childNames.push_back(name); m_pMenu = new wxMenu; return true; } virtual bool AddSite(const wxString& name, CSiteManagerItemData* data) { wxMenuItem* pItem = m_pMenu->Append(wxID_ANY, name); (*m_idMap)[pItem->GetId()] = data; return true; } // Go up a level virtual bool LevelUp() { if (m_parents.empty() || m_childNames.empty()) return false; wxMenu* pChild = m_pMenu; m_pMenu = m_parents.back(); if (pChild->GetMenuItemCount()) m_pMenu->AppendSubMenu(pChild, m_childNames.back()); else delete pChild; m_childNames.pop_back(); m_parents.pop_back(); return true; } protected: wxMenu* m_pMenu; std::map *m_idMap; std::list m_parents; std::list m_childNames; }; wxMenu* CSiteManager::GetSitesMenu() { ClearIdMap(); // We have to synchronize access to sitemanager.xml so that multiple processed don't write // to the same file or one is reading while the other one writes. CInterProcessMutex mutex(MUTEX_SITEMANAGER); wxMenu* predefinedSites = GetSitesMenu_Predefied(m_idMap); CXmlFile file(_T("sitemanager")); TiXmlElement* pDocument = file.Load(); if (!pDocument) { wxString msg = wxString::Format(_("Could not load \"%s\", please make sure the file is valid and can be accessed.\nAny changes made in the Site Manager will not be saved."), file.GetFileName().GetFullPath().c_str()); wxMessageBox(msg, _("Error loading xml file"), wxICON_ERROR); if (!predefinedSites) return predefinedSites; wxMenu *pMenu = new wxMenu; wxMenuItem* pItem = pMenu->Append(wxID_ANY, _("No sites available")); pItem->Enable(false); return pMenu; } TiXmlElement* pElement = pDocument->FirstChildElement("Servers"); if (!pElement) { if (!predefinedSites) return predefinedSites; wxMenu *pMenu = new wxMenu; wxMenuItem* pItem = pMenu->Append(wxID_ANY, _("No sites available")); pItem->Enable(false); return pMenu; } wxMenu* pMenu = new wxMenu; CSiteManagerXmlHandler_Menu handler(pMenu, &m_idMap); bool res = Load(pElement, &handler); if (!res || !pMenu->GetMenuItemCount()) { delete pMenu; pMenu = 0; } if (pMenu) { if (!predefinedSites) return pMenu; wxMenu* pRootMenu = new wxMenu; pRootMenu->AppendSubMenu(predefinedSites, _("Predefined Sites")); pRootMenu->AppendSubMenu(pMenu, _("My Sites")); return pRootMenu; } if (predefinedSites) return predefinedSites; pMenu = new wxMenu; wxMenuItem* pItem = pMenu->Append(wxID_ANY, _("No sites available")); pItem->Enable(false); return pMenu; } void CSiteManager::ClearIdMap() { for (std::map::iterator iter = m_idMap.begin(); iter != m_idMap.end(); iter++) delete iter->second; m_idMap.clear(); } wxMenu* CSiteManager::GetSitesMenu_Predefied(std::map &idMap) { const wxString& defaultsDir = wxGetApp().GetDefaultsDir(); if (defaultsDir == _T("")) return 0; wxFileName name(defaultsDir, _T("fzdefaults.xml")); CXmlFile file(name); TiXmlElement* pDocument = file.Load(); if (!pDocument) return 0; TiXmlElement* pElement = pDocument->FirstChildElement("Servers"); if (!pElement) return 0; wxMenu* pMenu = new wxMenu; CSiteManagerXmlHandler_Menu handler(pMenu, &idMap); if (!Load(pElement, &handler)) { delete pMenu; return 0; } if (!pMenu->GetMenuItemCount()) { delete pMenu; return 0; } return pMenu; } CSiteManagerItemData* CSiteManager::GetSiteById(int id) { std::map::iterator iter = m_idMap.find(id); CSiteManagerItemData *pData; if (iter != m_idMap.end()) { pData = iter->second; iter->second = 0; } else pData = 0; ClearIdMap(); return pData; } #ifdef __WXGTK__ void CSiteManager::OnTimezoneOffsetChanged(wxSpinEvent& event) { if (event.GetId() == XRCID("ID_LIMITMULTIPLE")) m_limitConnectionsChanged = true; else if (event.GetId() == XRCID("ID_TIMEZONE_HOURS")) m_timezoneOffsetHoursChanged = true; else m_timezoneOffsetMinutesChanged = true; event.Skip(); } #endif