source: filezilla/trunk/fuentes/src/interface/sitemanager_dialog.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 3 years ago

First release to xenial

File size: 71.3 KB
Line 
1#include <filezilla.h>
2#include "sitemanager_dialog.h"
3
4#include "buildinfo.h"
5#include "conditionaldialog.h"
6#include "drop_target_ex.h"
7#include "filezillaapp.h"
8#include "ipcmutex.h"
9#include "Options.h"
10#include "themeprovider.h"
11#include "treectrlex.h"
12#include "window_state_manager.h"
13#include "wrapengine.h"
14#include "xmlfunctions.h"
15#include "fzputtygen_interface.h"
16#include "xrc_helper.h"
17
18#include <wx/dcclient.h>
19#include <wx/dnd.h>
20#include <wx/file.h>
21#include <wx/gbsizer.h>
22
23#include <algorithm>
24
25#ifdef __WXMSW__
26#include "commctrl.h"
27#endif
28
29BEGIN_EVENT_TABLE(CSiteManagerDialog, wxDialogEx)
30EVT_BUTTON(XRCID("wxID_OK"), CSiteManagerDialog::OnOK)
31EVT_BUTTON(XRCID("wxID_CANCEL"), CSiteManagerDialog::OnCancel)
32EVT_BUTTON(XRCID("ID_CONNECT"), CSiteManagerDialog::OnConnect)
33EVT_BUTTON(XRCID("ID_NEWSITE"), CSiteManagerDialog::OnNewSite)
34EVT_BUTTON(XRCID("ID_NEWFOLDER"), CSiteManagerDialog::OnNewFolder)
35EVT_BUTTON(XRCID("ID_RENAME"), CSiteManagerDialog::OnRename)
36EVT_BUTTON(XRCID("ID_DELETE"), CSiteManagerDialog::OnDelete)
37EVT_TREE_BEGIN_LABEL_EDIT(XRCID("ID_SITETREE"), CSiteManagerDialog::OnBeginLabelEdit)
38EVT_TREE_END_LABEL_EDIT(XRCID("ID_SITETREE"), CSiteManagerDialog::OnEndLabelEdit)
39EVT_TREE_SEL_CHANGING(XRCID("ID_SITETREE"), CSiteManagerDialog::OnSelChanging)
40EVT_TREE_SEL_CHANGED(XRCID("ID_SITETREE"), CSiteManagerDialog::OnSelChanged)
41EVT_CHOICE(XRCID("ID_LOGONTYPE"), CSiteManagerDialog::OnLogontypeSelChanged)
42EVT_BUTTON(XRCID("ID_BROWSE"), CSiteManagerDialog::OnRemoteDirBrowse)
43EVT_BUTTON(XRCID("ID_KEYFILE_BROWSE"), CSiteManagerDialog::OnKeyFileBrowse)
44EVT_TREE_ITEM_ACTIVATED(XRCID("ID_SITETREE"), CSiteManagerDialog::OnItemActivated)
45EVT_CHECKBOX(XRCID("ID_LIMITMULTIPLE"), CSiteManagerDialog::OnLimitMultipleConnectionsChanged)
46EVT_RADIOBUTTON(XRCID("ID_CHARSET_AUTO"), CSiteManagerDialog::OnCharsetChange)
47EVT_RADIOBUTTON(XRCID("ID_CHARSET_UTF8"), CSiteManagerDialog::OnCharsetChange)
48EVT_RADIOBUTTON(XRCID("ID_CHARSET_CUSTOM"), CSiteManagerDialog::OnCharsetChange)
49EVT_CHOICE(XRCID("ID_PROTOCOL"), CSiteManagerDialog::OnProtocolSelChanged)
50EVT_BUTTON(XRCID("ID_COPY"), CSiteManagerDialog::OnCopySite)
51EVT_TREE_BEGIN_DRAG(XRCID("ID_SITETREE"), CSiteManagerDialog::OnBeginDrag)
52EVT_CHAR(CSiteManagerDialog::OnChar)
53EVT_TREE_ITEM_MENU(XRCID("ID_SITETREE"), CSiteManagerDialog::OnContextMenu)
54EVT_MENU(XRCID("ID_EXPORT"), CSiteManagerDialog::OnExportSelected)
55EVT_BUTTON(XRCID("ID_NEWBOOKMARK"), CSiteManagerDialog::OnNewBookmark)
56EVT_BUTTON(XRCID("ID_BOOKMARK_BROWSE"), CSiteManagerDialog::OnBookmarkBrowse)
57END_EVENT_TABLE()
58
59class CSiteManagerDialogDataObject : public wxDataObjectSimple
60{
61public:
62        CSiteManagerDialogDataObject()
63                : wxDataObjectSimple(wxDataFormat(_T("FileZilla3SiteManagerObject")))
64        {
65        }
66
67        // GTK doesn't like data size of 0
68        virtual size_t GetDataSize() const { return 1; }
69
70        virtual bool GetDataHere(void *buf) const { memset(buf, 0, 1); return true; }
71
72        virtual bool SetData(size_t, const void *) { return true; }
73};
74
75class CSiteManagerDropTarget : public CScrollableDropTarget<wxTreeCtrlEx>
76{
77public:
78        CSiteManagerDropTarget(CSiteManagerDialog* pSiteManager)
79                : CScrollableDropTarget<wxTreeCtrlEx>(XRCCTRL(*pSiteManager, "ID_SITETREE", wxTreeCtrlEx))
80        {
81                SetDataObject(new CSiteManagerDialogDataObject());
82                m_pSiteManager = pSiteManager;
83        }
84
85        virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
86        {
87                ClearDropHighlight();
88                if (def == wxDragError ||
89                        def == wxDragNone ||
90                        def == wxDragCancel)
91                {
92                        return def;
93                }
94
95                wxTreeItemId hit = GetHit(wxPoint(x, y));
96                if (!hit)
97                        return wxDragNone;
98                if (hit == m_pSiteManager->m_dropSource)
99                        return wxDragNone;
100
101                const bool predefined = m_pSiteManager->IsPredefinedItem(hit);
102                if (predefined)
103                        return wxDragNone;
104
105                wxTreeCtrl *pTree = XRCCTRL(*m_pSiteManager, "ID_SITETREE", wxTreeCtrl);
106                CSiteManagerItemData *pData = (CSiteManagerItemData *)pTree->GetItemData(hit);
107                CSiteManagerItemData *pSourceData = (CSiteManagerItemData *)pTree->GetItemData(m_pSiteManager->m_dropSource);
108                if (pData)
109                {
110                        if (pData->m_type == CSiteManagerItemData::BOOKMARK)
111                                return wxDragNone;
112                        if (!pSourceData || pSourceData->m_type != CSiteManagerItemData::BOOKMARK)
113                                return wxDragNone;
114                }
115                else if (pSourceData && pSourceData->m_type == CSiteManagerItemData::BOOKMARK)
116                        return wxDragNone;
117
118                wxTreeItemId item = hit;
119                while (item != pTree->GetRootItem())
120                {
121                        if (item == m_pSiteManager->m_dropSource)
122                        {
123                                ClearDropHighlight();
124                                return wxDragNone;
125                        }
126                        item = pTree->GetItemParent(item);
127                }
128
129                if (def == wxDragMove && pTree->GetItemParent(m_pSiteManager->m_dropSource) == hit)
130                        return wxDragNone;
131
132                if (!m_pSiteManager->MoveItems(m_pSiteManager->m_dropSource, hit, def == wxDragCopy))
133                        return wxDragNone;
134
135                return def;
136        }
137
138        virtual bool OnDrop(wxCoord x, wxCoord y)
139        {
140                CScrollableDropTarget<wxTreeCtrlEx>::OnDrop(x, y);
141                ClearDropHighlight();
142
143                wxTreeItemId hit = GetHit(wxPoint(x, y));
144                if (!hit)
145                        return false;
146                if (hit == m_pSiteManager->m_dropSource)
147                        return false;
148
149                const bool predefined = m_pSiteManager->IsPredefinedItem(hit);
150                if (predefined)
151                        return false;
152
153                wxTreeCtrl *pTree = XRCCTRL(*m_pSiteManager, "ID_SITETREE", wxTreeCtrl);
154                CSiteManagerItemData *pData = (CSiteManagerItemData *)pTree->GetItemData(hit);
155                CSiteManagerItemData *pSourceData = (CSiteManagerItemData *)pTree->GetItemData(m_pSiteManager->m_dropSource);
156                if (pData)
157                {
158                        if (pData->m_type == CSiteManagerItemData::BOOKMARK)
159                                return false;
160                        if (!pSourceData || pSourceData->m_type != CSiteManagerItemData::BOOKMARK)
161                                return false;
162                }
163                else if (pSourceData && pSourceData->m_type == CSiteManagerItemData::BOOKMARK)
164                        return false;
165
166                wxTreeItemId item = hit;
167                while (item != pTree->GetRootItem())
168                {
169                        if (item == m_pSiteManager->m_dropSource)
170                        {
171                                ClearDropHighlight();
172                                return false;
173                        }
174                        item = pTree->GetItemParent(item);
175                }
176
177                return true;
178        }
179
180        virtual void OnLeave()
181        {
182                CScrollableDropTarget<wxTreeCtrlEx>::OnLeave();
183                ClearDropHighlight();
184        }
185
186        virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def)
187        {
188                def = CScrollableDropTarget<wxTreeCtrlEx>::OnEnter(x, y, def);
189                return OnDragOver(x, y, def);
190        }
191
192        wxTreeItemId GetHit(const wxPoint& point)
193        {
194                int flags = 0;
195
196                wxTreeCtrl *pTree = XRCCTRL(*m_pSiteManager, "ID_SITETREE", wxTreeCtrl);
197                wxTreeItemId hit = pTree->HitTest(point, flags);
198
199                if (flags & (wxTREE_HITTEST_ABOVE | wxTREE_HITTEST_BELOW | wxTREE_HITTEST_NOWHERE | wxTREE_HITTEST_TOLEFT | wxTREE_HITTEST_TORIGHT))
200                        return wxTreeItemId();
201
202                return hit;
203        }
204
205        virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def)
206        {
207                def = CScrollableDropTarget<wxTreeCtrlEx>::OnDragOver(x, y, def);
208
209                if (def == wxDragError ||
210                        def == wxDragNone ||
211                        def == wxDragCancel)
212                {
213                        ClearDropHighlight();
214                        return def;
215                }
216
217                wxTreeItemId hit = GetHit(wxPoint(x, y));
218                if (!hit)
219                {
220                        ClearDropHighlight();
221                        return wxDragNone;
222                }
223                if (hit == m_pSiteManager->m_dropSource)
224                {
225                        ClearDropHighlight();
226                        return wxDragNone;
227                }
228
229                const bool predefined = m_pSiteManager->IsPredefinedItem(hit);
230                if (predefined)
231                {
232                        ClearDropHighlight();
233                        return wxDragNone;
234                }
235
236                wxTreeCtrl *pTree = XRCCTRL(*m_pSiteManager, "ID_SITETREE", wxTreeCtrl);
237                CSiteManagerItemData *pData = static_cast<CSiteManagerItemData *>(pTree->GetItemData(hit));
238                CSiteManagerItemData *pSourceData = (CSiteManagerItemData *)pTree->GetItemData(m_pSiteManager->m_dropSource);
239                if (pData)
240                {
241                        if (pData->m_type == CSiteManagerItemData::BOOKMARK)
242                        {
243                                ClearDropHighlight();
244                                return wxDragNone;
245                        }
246                        if (!pSourceData || pSourceData->m_type != CSiteManagerItemData::BOOKMARK)
247                        {
248                                ClearDropHighlight();
249                                return wxDragNone;
250                        }
251                }
252                else if (pSourceData && pSourceData->m_type == CSiteManagerItemData::BOOKMARK)
253                {
254                        ClearDropHighlight();
255                        return wxDragNone;
256                }
257
258                wxTreeItemId item = hit;
259                while (item != pTree->GetRootItem())
260                {
261                        if (item == m_pSiteManager->m_dropSource)
262                        {
263                                ClearDropHighlight();
264                                return wxDragNone;
265                        }
266                        item = pTree->GetItemParent(item);
267                }
268
269                if (def == wxDragMove && pTree->GetItemParent(m_pSiteManager->m_dropSource) == hit)
270                {
271                        ClearDropHighlight();
272                        return wxDragNone;
273                }
274
275                DisplayDropHighlight(wxPoint(x, y));
276
277                return def;
278        }
279
280        void ClearDropHighlight()
281        {
282                if (m_dropHighlight == wxTreeItemId())
283                        return;
284
285                wxTreeCtrl *pTree = XRCCTRL(*m_pSiteManager, "ID_SITETREE", wxTreeCtrl);
286                pTree->SetItemDropHighlight(m_dropHighlight, false);
287                m_dropHighlight = wxTreeItemId();
288        }
289
290        wxTreeItemId DisplayDropHighlight(wxPoint p)
291        {
292                ClearDropHighlight();
293
294                wxTreeItemId hit = GetHit(p);
295                if (hit.IsOk()) {
296                        wxTreeCtrl *pTree = XRCCTRL(*m_pSiteManager, "ID_SITETREE", wxTreeCtrl);
297                        pTree->SetItemDropHighlight(hit, true);
298                        m_dropHighlight = hit;
299                }
300
301                return hit;
302        }
303
304protected:
305        CSiteManagerDialog* m_pSiteManager;
306        wxTreeItemId m_dropHighlight;
307};
308
309CSiteManagerDialog::CSiteManagerDialog()
310{
311}
312
313CSiteManagerDialog::~CSiteManagerDialog()
314{
315        delete m_pSiteManagerMutex;
316
317        if (m_pWindowStateManager)
318        {
319                m_pWindowStateManager->Remember(OPTION_SITEMANAGER_POSITION);
320                delete m_pWindowStateManager;
321        }
322}
323
324bool CSiteManagerDialog::Create(wxWindow* parent, std::vector<_connected_site> *connected_sites, const CServer* pServer /*=0*/)
325{
326        m_pSiteManagerMutex = new CInterProcessMutex(MUTEX_SITEMANAGERGLOBAL, false);
327        if (m_pSiteManagerMutex->TryLock() == 0) {
328                int answer = wxMessageBoxEx(_("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."),
329                                                                  _("Site Manager already open"), wxYES_NO | wxICON_QUESTION);
330                if (answer != wxYES)
331                        return false;
332
333                delete m_pSiteManagerMutex;
334                m_pSiteManagerMutex = 0;
335        }
336        CreateControls(parent);
337
338        // Now create the imagelist for the site tree
339        wxTreeCtrlEx *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrlEx);
340        if (!pTree)
341                return false;
342
343        wxSize s = CThemeProvider::GetIconSize(iconSizeSmall);
344        wxImageList* pImageList = new wxImageList(s.x, s.y);
345
346        pImageList->Add(wxArtProvider::GetBitmap(_T("ART_FOLDERCLOSED"), wxART_OTHER, s));
347        pImageList->Add(wxArtProvider::GetBitmap(_T("ART_FOLDER"), wxART_OTHER, s));
348        pImageList->Add(wxArtProvider::GetBitmap(_T("ART_SERVER"), wxART_OTHER, s));
349        pImageList->Add(wxArtProvider::GetBitmap(_T("ART_BOOKMARK"), wxART_OTHER, s));
350
351        pTree->AssignImageList(pImageList);
352
353        m_pNotebook_Site = XRCCTRL(*this, "ID_NOTEBOOK", wxNotebook);
354
355#ifdef __WXMSW__
356        // Make pages at least wide enough to fit all tabs
357        HWND hWnd = (HWND)m_pNotebook_Site->GetHandle();
358
359        int width = 4;
360        for (unsigned int i = 0; i < m_pNotebook_Site->GetPageCount(); ++i) {
361                RECT tab_rect{};
362                if (TabCtrl_GetItemRect(hWnd, i, &tab_rect)) {
363                        width += tab_rect.right - tab_rect.left;
364                }
365        }
366        int margin = m_pNotebook_Site->GetSize().x - m_pNotebook_Site->GetPage(0)->GetSize().x;
367        m_pNotebook_Site->GetPage(0)->GetSizer()->SetMinSize(wxSize(width - margin, 0));
368#else
369        // Make pages at least wide enough to fit all tabs
370        int width = 10; // Guessed
371        wxClientDC dc(m_pNotebook_Site);
372        for (unsigned int i = 0; i < m_pNotebook_Site->GetPageCount(); ++i)
373        {
374                wxCoord w, h;
375                dc.GetTextExtent(m_pNotebook_Site->GetPageText(i), &w, &h);
376
377                width += w;
378#ifdef __WXMAC__
379                width += 20; // Guessed
380#else
381                width += 20;
382#endif
383        }
384
385        wxSize page_min_size = m_pNotebook_Site->GetPage(0)->GetSizer()->GetMinSize();
386        if (page_min_size.x < width)
387        {
388                page_min_size.x = width;
389                m_pNotebook_Site->GetPage(0)->GetSizer()->SetMinSize(page_min_size);
390        }
391#endif
392
393        Layout();
394        wxGetApp().GetWrapEngine()->WrapRecursive(this, 1.33, "Site Manager");
395
396        wxSize minSize = GetSizer()->GetMinSize();
397
398        wxSize size = GetSize();
399        wxSize clientSize = GetClientSize();
400        SetMinSize(GetSizer()->GetMinSize() + size - clientSize);
401        SetClientSize(minSize);
402
403        // Set min height of general page sizer
404        auto generalSizer = static_cast<wxGridBagSizer*>(xrc_call(*this, "ID_PROTOCOL", &wxWindow::GetContainingSizer));
405        generalSizer->SetMinSize(generalSizer->GetMinSize());
406        generalSizer->SetEmptyCellSize(wxSize(-5, -5));
407
408        // Set min height of encryption row
409        wxSize descSize = XRCCTRL(*this, "ID_ENCRYPTION_DESC", wxWindow)->GetSize();
410        wxSize encSize = XRCCTRL(*this, "ID_ENCRYPTION", wxWindow)->GetSize();
411        xrc_call(*this, "ID_ENCRYPTION", &wxWindow::GetContainingSizer)->GetItem(1)->SetMinSize(0, std::max(descSize.GetHeight(), encSize.GetHeight()));
412
413        // Load bookmark notebook
414        m_pNotebook_Bookmark = new wxNotebook(m_pNotebook_Site->GetParent(), -1);
415        wxPanel* pPanel = new wxPanel;
416        wxXmlResource::Get()->LoadPanel(pPanel, m_pNotebook_Bookmark, _T("ID_SITEMANAGER_BOOKMARK_PANEL"));
417        m_pNotebook_Bookmark->Hide();
418        m_pNotebook_Bookmark->AddPage(pPanel, _("Bookmark"));
419        wxSizer *pSizer = m_pNotebook_Site->GetContainingSizer();
420        pSizer->Add(m_pNotebook_Bookmark, 1, wxGROW);
421        pSizer->SetItemMinSize(1, pSizer->GetItem((size_t)0)->GetMinSize().GetWidth(), -1);
422
423        if (!Load()) {
424                return false;
425        }
426
427        XRCCTRL(*this, "ID_TRANSFERMODE_DEFAULT", wxRadioButton)->Update();
428        XRCCTRL(*this, "ID_TRANSFERMODE_ACTIVE", wxRadioButton)->Update();
429        XRCCTRL(*this, "ID_TRANSFERMODE_PASSIVE", wxRadioButton)->Update();
430
431        wxTreeItemId item = pTree->GetSelection();
432        if (!item.IsOk())
433                pTree->SafeSelectItem(m_ownSites);
434        SetCtrlState();
435
436        m_pWindowStateManager = new CWindowStateManager(this);
437        m_pWindowStateManager->Restore(OPTION_SITEMANAGER_POSITION);
438
439        pTree->SetDropTarget(new CSiteManagerDropTarget(this));
440
441#ifdef __WXGTK__
442        {
443                CSiteManagerItemData* data = 0;
444                wxTreeItemId item = pTree->GetSelection();
445                if (item.IsOk())
446                        data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
447                if (!data)
448                        XRCCTRL(*this, "wxID_OK", wxButton)->SetFocus();
449        }
450#endif
451
452        m_connected_sites = connected_sites;
453        MarkConnectedSites();
454
455        if (pServer)
456                CopyAddServer(*pServer);
457
458        return true;
459}
460
461void CSiteManagerDialog::MarkConnectedSites()
462{
463        for (int i = 0; i < (int)m_connected_sites->size(); ++i)
464                MarkConnectedSite(i);
465}
466
467void CSiteManagerDialog::MarkConnectedSite(int connected_site)
468{
469        wxString connected_site_path = (*m_connected_sites)[connected_site].old_path;
470
471        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
472        if (!pTree)
473                return;
474
475        if (connected_site_path.Left(1) == _T("1"))
476        {
477                // Default sites never change
478                (*m_connected_sites)[connected_site].new_path = (*m_connected_sites)[connected_site].old_path;
479                return;
480        }
481
482        if (connected_site_path.Left(1) != _T("0"))
483                return;
484
485        std::list<wxString> segments;
486        if (!CSiteManager::UnescapeSitePath(connected_site_path.Mid(1), segments))
487                return;
488
489        wxTreeItemId current = m_ownSites;
490        while (!segments.empty())
491        {
492                wxTreeItemIdValue c;
493                wxTreeItemId child = pTree->GetFirstChild(current, c);
494                while (child)
495                {
496                        if (pTree->GetItemText(child) == segments.front())
497                                break;
498
499                        child = pTree->GetNextChild(current, c);
500                }
501                if (!child)
502                        return;
503
504                segments.pop_front();
505                current = child;
506        }
507
508        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(current));
509        if (!data || data->m_type != CSiteManagerItemData::SITE)
510                return;
511
512        CSiteManagerItemData_Site *site_data = static_cast<CSiteManagerItemData_Site* >(data);
513        wxASSERT(site_data->connected_item == -1);
514        site_data->connected_item = connected_site;
515}
516
517void CSiteManagerDialog::CreateControls(wxWindow* parent)
518{
519        if( !wxDialogEx::Load(parent, _T("ID_SITEMANAGER"))) {
520                return;
521        }
522
523        wxChoice *pProtocol = XRCCTRL(*this, "ID_PROTOCOL", wxChoice);
524        pProtocol->Append(_("FTP - File Transfer Protocol"));
525        pProtocol->Append(CServer::GetProtocolName(SFTP));
526
527        wxChoice *pChoice = XRCCTRL(*this, "ID_SERVERTYPE", wxChoice);
528        wxASSERT(pChoice);
529        for (int i = 0; i < SERVERTYPE_MAX; ++i)
530                pChoice->Append(CServer::GetNameFromServerType((enum ServerType)i));
531
532        pChoice = XRCCTRL(*this, "ID_LOGONTYPE", wxChoice);
533        wxASSERT(pChoice);
534        for (int i = 0; i < LOGONTYPE_MAX; ++i)
535                pChoice->Append(CServer::GetNameFromLogonType((enum LogonType)i));
536
537        wxChoice* pEncryption = XRCCTRL(*this, "ID_ENCRYPTION", wxChoice);
538        pEncryption->Append(_("Use explicit FTP over TLS if available"));
539        pEncryption->Append(_("Require explicit FTP over TLS"));
540        pEncryption->Append(_("Require implicit FTP over TLS"));
541        pEncryption->Append(_("Only use plain FTP (insecure)"));
542        pEncryption->SetSelection(0);
543}
544
545void CSiteManagerDialog::OnOK(wxCommandEvent&)
546{
547        if (!Verify())
548                return;
549
550        UpdateItem();
551
552        Save();
553
554        RememberLastSelected();
555
556        EndModal(wxID_OK);
557}
558
559void CSiteManagerDialog::OnCancel(wxCommandEvent&)
560{
561        EndModal(wxID_CANCEL);
562}
563
564void CSiteManagerDialog::OnConnect(wxCommandEvent&)
565{
566        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
567        if (!pTree)
568                return;
569
570        wxTreeItemId item = pTree->GetSelection();
571        if (!item.IsOk())
572                return;
573
574        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
575        if (!data)
576        {
577                wxBell();
578                return;
579        }
580
581        if (!Verify())
582        {
583                wxBell();
584                return;
585        }
586
587        UpdateItem();
588
589        Save();
590
591        RememberLastSelected();
592
593        EndModal(wxID_YES);
594}
595
596class CSiteManagerXmlHandler_Tree : public CSiteManagerXmlHandler
597{
598public:
599        CSiteManagerXmlHandler_Tree(wxTreeCtrlEx* pTree, wxTreeItemId root, const wxString& lastSelection, bool predefined)
600                : m_pTree(pTree), m_item(root), m_predefined(predefined)
601        {
602                if (!CSiteManager::UnescapeSitePath(lastSelection, m_lastSelection))
603                        m_lastSelection.clear();
604                m_wrong_sel_depth = 0;
605                m_kiosk = COptions::Get()->GetOptionVal(OPTION_DEFAULT_KIOSKMODE);
606        }
607
608        virtual ~CSiteManagerXmlHandler_Tree()
609        {
610                m_pTree->SortChildren(m_item);
611                m_pTree->Expand(m_item);
612        }
613
614        virtual bool AddFolder(const wxString& name, bool expanded)
615        {
616                wxTreeItemId newItem = m_pTree->AppendItem(m_item, name, 0, 0);
617                m_pTree->SetItemImage(newItem, 1, wxTreeItemIcon_Expanded);
618                m_pTree->SetItemImage(newItem, 1, wxTreeItemIcon_SelectedExpanded);
619
620                m_item = newItem;
621                m_expand.push_back(expanded);
622
623                if (!m_wrong_sel_depth && !m_lastSelection.empty())
624                {
625                        const wxString& first = m_lastSelection.front();
626                        if (first == name)
627                        {
628                                m_lastSelection.pop_front();
629                                if (m_lastSelection.empty())
630                                        m_pTree->SafeSelectItem(newItem);
631                        }
632                        else
633                                ++m_wrong_sel_depth;
634                }
635                else
636                        ++m_wrong_sel_depth;
637
638                return true;
639        }
640
641        virtual bool AddSite(std::unique_ptr<CSiteManagerItemData_Site> data)
642        {
643                if (m_kiosk && !m_predefined &&
644                        data->m_server.GetLogonType() == NORMAL)
645                {
646                        // Clear saved password
647                        data->m_server.SetLogonType(ASK);
648                        data->m_server.SetUser(data->m_server.GetUser());
649                }
650
651                const wxString name(data->m_server.GetName());
652
653                wxTreeItemId newItem = m_pTree->AppendItem(m_item, name, 2, 2, data.release());
654
655                m_item = newItem;
656                m_expand.push_back(true);
657
658                if (!m_wrong_sel_depth && !m_lastSelection.empty()) {
659                        const wxString& first = m_lastSelection.front();
660                        if (first == name) {
661                                m_lastSelection.pop_front();
662                                if (m_lastSelection.empty())
663                                        m_pTree->SafeSelectItem(newItem);
664                        }
665                        else
666                                ++m_wrong_sel_depth;
667                }
668                else
669                        ++m_wrong_sel_depth;
670
671                return true;
672        }
673
674        virtual bool AddBookmark(const wxString& name, std::unique_ptr<CSiteManagerItemData> data)
675        {
676                wxTreeItemId newItem = m_pTree->AppendItem(m_item, name, 3, 3, data.release());
677
678                if (!m_wrong_sel_depth && !m_lastSelection.empty()) {
679                        const wxString& first = m_lastSelection.front();
680                        if (first == name) {
681                                m_lastSelection.clear();
682                                m_pTree->SafeSelectItem(newItem);
683                        }
684                }
685
686                return true;
687        }
688
689        virtual bool LevelUp()
690        {
691                if (m_wrong_sel_depth)
692                        m_wrong_sel_depth--;
693
694                if (!m_expand.empty())
695                {
696                        const bool expand = m_expand.back();
697                        m_expand.pop_back();
698                        if (expand)
699                                m_pTree->Expand(m_item);
700                }
701                m_pTree->SortChildren(m_item);
702
703                wxTreeItemId parent = m_pTree->GetItemParent(m_item);
704                if (!parent)
705                        return false;
706
707                m_item = parent;
708                return true;
709        }
710
711protected:
712        wxTreeCtrlEx* m_pTree;
713        wxTreeItemId m_item;
714
715        std::list<wxString> m_lastSelection;
716        int m_wrong_sel_depth;
717
718        std::list<bool> m_expand;
719
720        bool m_predefined;
721        int m_kiosk;
722};
723
724bool CSiteManagerDialog::Load()
725{
726        wxTreeCtrlEx *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrlEx);
727        if (!pTree)
728                return false;
729
730        pTree->DeleteAllItems();
731
732        // We have to synchronize access to sitemanager.xml so that multiple processed don't write
733        // to the same file or one is reading while the other one writes.
734        CInterProcessMutex mutex(MUTEX_SITEMANAGER);
735
736        // Load default sites
737        bool hasDefaultSites = LoadDefaultSites();
738        if (hasDefaultSites)
739                m_ownSites = pTree->AppendItem(pTree->GetRootItem(), _("My Sites"), 0, 0);
740        else
741                m_ownSites = pTree->AddRoot(_("My Sites"), 0, 0);
742
743        wxTreeItemId treeId = m_ownSites;
744        pTree->SetItemImage(treeId, 1, wxTreeItemIcon_Expanded);
745        pTree->SetItemImage(treeId, 1, wxTreeItemIcon_SelectedExpanded);
746
747        CXmlFile file(wxGetApp().GetSettingsFile(_T("sitemanager")));
748        auto document = file.Load();
749        if (!document) {
750                wxString msg = file.GetError() + _T("\n") + _("The Site Manager cannot be used unless the file gets repaired.");
751                wxMessageBoxEx(msg, _("Error loading xml file"), wxICON_ERROR);
752
753                return false;
754        }
755
756        if (file.IsFromFutureVersion()) {
757                wxString msg = wxString::Format(_("The file '%s' has been created by a more recent version of FileZilla.\nLoading files created by newer versions can result in loss of data.\nDo you want to continue?"), file.GetFileName());
758                if (wxMessageBoxEx(msg, _("Detected newer version of FileZilla"), wxICON_QUESTION | wxYES_NO) != wxYES) {
759                        return false;
760                }
761        }
762
763        auto element = document.child("Servers");
764        if (!element)
765                return true;
766
767        wxString lastSelection = COptions::Get()->GetOption(OPTION_SITEMANAGER_LASTSELECTED);
768        if (!lastSelection.empty() && lastSelection[0] == '0') {
769                if (lastSelection == _T("0"))
770                        pTree->SafeSelectItem(treeId);
771                else
772                        lastSelection = lastSelection.Mid(1);
773        }
774        else
775                lastSelection.clear();
776        CSiteManagerXmlHandler_Tree handler(pTree, treeId, lastSelection, false);
777
778        bool res = CSiteManager::Load(element, handler);
779
780        pTree->SortChildren(treeId);
781        pTree->Expand(treeId);
782        if (!pTree->GetSelection())
783                pTree->SafeSelectItem(treeId);
784
785        pTree->EnsureVisible(pTree->GetSelection());
786
787        return res;
788}
789
790bool CSiteManagerDialog::Save(pugi::xml_node element /*=0*/, wxTreeItemId treeId /*=wxTreeItemId()*/)
791{
792        if (!m_pSiteManagerMutex)
793                return false;
794
795        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
796        if (!pTree)
797                return false;
798
799        if (!element || !treeId) {
800                // We have to synchronize access to sitemanager.xml so that multiple processed don't write
801                // to the same file or one is reading while the other one writes.
802                CInterProcessMutex mutex(MUTEX_SITEMANAGER);
803
804                CXmlFile xml(wxGetApp().GetSettingsFile(_T("sitemanager")));
805
806                auto document = xml.Load();
807                if (!document) {
808                        wxString msg = xml.GetError() + _T("\n") + _("Any changes made in the Site Manager could not be saved.");
809                        wxMessageBoxEx(msg, _("Error loading xml file"), wxICON_ERROR);
810
811                        return false;
812                }
813
814                auto servers = document.child("Servers");
815                while (servers) {
816                        document.remove_child(servers);
817                        servers = document.child("Servers");
818                }
819                element = document.append_child("Servers");
820
821                if (!element)
822                        return true;
823
824                bool res = Save(element, m_ownSites);
825
826                if (!xml.Save(false)) {
827                        if (COptions::Get()->GetOptionVal(OPTION_DEFAULT_KIOSKMODE) == 2)
828                                return res;
829                        wxString msg = wxString::Format(_("Could not write \"%s\", any changes to the Site Manager could not be saved: %s"), xml.GetFileName(), xml.GetError());
830                        wxMessageBoxEx(msg, _("Error writing xml file"), wxICON_ERROR);
831                }
832
833                return res;
834        }
835
836        wxTreeItemId child;
837        wxTreeItemIdValue cookie;
838        child = pTree->GetFirstChild(treeId, cookie);
839        while (child.IsOk()) {
840                SaveChild(element, child);
841
842                child = pTree->GetNextChild(treeId, cookie);
843        }
844
845        return false;
846}
847
848bool CSiteManagerDialog::SaveChild(pugi::xml_node element, wxTreeItemId child)
849{
850        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
851        if (!pTree)
852                return false;
853
854        wxString const name = pTree->GetItemText(child);
855
856        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(child));
857        if (!data) {
858                auto node = element.append_child("Folder");
859                const bool expanded = pTree->IsExpanded(child);
860                SetTextAttribute(node, "expanded", expanded ? _T("1") : _T("0"));
861                AddTextElement(node, name);
862                Save(node, child);
863        }
864        else if (data->m_type == CSiteManagerItemData::SITE) {
865                CSiteManagerItemData_Site *site_data = static_cast<CSiteManagerItemData_Site* >(data);
866                auto node = element.append_child("Server");
867                SetServer(node, site_data->m_server);
868
869                // Save comments
870                AddTextElement(node, "Comments", site_data->m_comments);
871
872                // Save local dir
873                AddTextElement(node, "LocalDir", data->m_localDir);
874
875                // Save remote dir
876                AddTextElement(node, "RemoteDir", data->m_remoteDir.GetSafePath());
877
878                AddTextElementRaw(node, "SyncBrowsing", data->m_sync ? "1" : "0");
879                AddTextElementRaw(node, "DirectoryComparison", data->m_comparison ? "1" : "0");
880                AddTextElement(node, name);
881
882                Save(node, child);
883
884                if (site_data->connected_item != -1) {
885                        if ((*m_connected_sites)[site_data->connected_item].server == site_data->m_server) {
886                                (*m_connected_sites)[site_data->connected_item].new_path = GetSitePath(child);
887                                (*m_connected_sites)[site_data->connected_item].server = site_data->m_server;
888                        }
889                }
890        }
891        else {
892                auto node = element.append_child("Bookmark");
893
894                AddTextElement(node, "Name", name);
895
896                // Save local dir
897                AddTextElement(node, "LocalDir", data->m_localDir);
898
899                // Save remote dir
900                AddTextElement(node, "RemoteDir", data->m_remoteDir.GetSafePath());
901
902                AddTextElementRaw(node, "SyncBrowsing", data->m_sync ? "1" : "0");
903                AddTextElementRaw(node, "DirectoryComparison", data->m_comparison ? "1" : "0");
904        }
905
906        return true;
907}
908
909void CSiteManagerDialog::OnNewFolder(wxCommandEvent&event)
910{
911        wxTreeCtrlEx *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrlEx);
912        if (!pTree)
913                return;
914
915        wxTreeItemId item = pTree->GetSelection();
916        if (!item.IsOk())
917                return;
918
919        while (pTree->GetItemData(item))
920                item = pTree->GetItemParent(item);
921
922        if (!Verify())
923                return;
924
925        wxString name = FindFirstFreeName(item, _("New folder"));
926
927        wxTreeItemId newItem = pTree->AppendItem(item, name, 0, 0);
928        pTree->SetItemImage(newItem, 1, wxTreeItemIcon_Expanded);
929        pTree->SetItemImage(newItem, 1, wxTreeItemIcon_SelectedExpanded);
930        pTree->SortChildren(item);
931        pTree->EnsureVisible(newItem);
932        pTree->SafeSelectItem(newItem);
933        pTree->EditLabel(newItem);
934}
935
936bool CSiteManagerDialog::Verify()
937{
938        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
939        if (!pTree)
940                return true;
941
942        wxTreeItemId item = pTree->GetSelection();
943        if (!item.IsOk())
944                return true;
945
946        CSiteManagerItemData* data = (CSiteManagerItemData *)pTree->GetItemData(item);
947        if (!data)
948                return true;
949
950        if (data->m_type == CSiteManagerItemData::SITE) {
951                const wxString& host = XRCCTRL(*this, "ID_HOST", wxTextCtrl)->GetValue();
952                if (host.empty()) {
953                        XRCCTRL(*this, "ID_HOST", wxTextCtrl)->SetFocus();
954                        wxMessageBoxEx(_("You have to enter a hostname."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
955                        return false;
956                }
957
958                auto logon_type = GetLogonType();
959
960                ServerProtocol protocol = GetProtocol();
961                wxASSERT(protocol != UNKNOWN);
962
963                if (protocol == SFTP &&
964                        logon_type == ACCOUNT)
965                {
966                        XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetFocus();
967                        wxMessageBoxEx(_("'Account' logontype not supported by selected protocol"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
968                        return false;
969                }
970
971                if (COptions::Get()->GetOptionVal(OPTION_DEFAULT_KIOSKMODE) != 0 &&
972                        !IsPredefinedItem(item) &&
973                        (logon_type == ACCOUNT || logon_type == NORMAL))
974                {
975                        XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetFocus();
976                        wxString msg;
977                        if (COptions::Get()->OptionFromFzDefaultsXml(OPTION_DEFAULT_KIOSKMODE))
978                                msg = _("Saving of password has been disabled by your system administrator.");
979                        else
980                                msg = _("Saving of passwords has been disabled by you.");
981                        msg += _T("\n");
982                        msg += _("'Normal' and 'Account' logontypes are not available. Your entry has been changed to 'Ask for password'.");
983                        XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(CServer::GetNameFromLogonType(ASK));
984                        XRCCTRL(*this, "ID_PASS", wxTextCtrl)->ChangeValue(wxString());
985                        logon_type = ASK;
986                        wxMessageBoxEx(msg, _("Site Manager - Cannot remember password"), wxICON_INFORMATION, this);
987                }
988
989                // Set selected type
990                CServer server;
991                server.SetLogonType(logon_type);
992                server.SetProtocol(protocol);
993
994                wxString port = XRCCTRL(*this, "ID_PORT", wxTextCtrl)->GetValue();
995                CServerPath path;
996                wxString error;
997                if (!server.ParseUrl(host, port, wxString(), wxString(), error, path)) {
998                        XRCCTRL(*this, "ID_HOST", wxTextCtrl)->SetFocus();
999                        wxMessageBoxEx(error, _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1000                        return false;
1001                }
1002
1003                XRCCTRL(*this, "ID_HOST", wxTextCtrl)->ChangeValue(server.FormatHost(true));
1004                if (server.GetPort() != CServer::GetDefaultPort(server.GetProtocol())) {
1005                        XRCCTRL(*this, "ID_PORT", wxTextCtrl)->ChangeValue(wxString::Format(_T("%d"), server.GetPort()));
1006                }
1007                else {
1008                        XRCCTRL(*this, "ID_PORT", wxTextCtrl)->ChangeValue(wxString());
1009                }
1010
1011                SetProtocol(server.GetProtocol());
1012
1013                if (XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxRadioButton)->GetValue()) {
1014                        if (XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->GetValue().empty()) {
1015                                XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->SetFocus();
1016                                wxMessageBoxEx(_("Need to specify a character encoding"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1017                                return false;
1018                        }
1019                }
1020
1021                // Require username for non-anonymous, non-ask logon type
1022                const wxString user = XRCCTRL(*this, "ID_USER", wxTextCtrl)->GetValue();
1023                if (logon_type != ANONYMOUS &&
1024                        logon_type != ASK &&
1025                        logon_type != INTERACTIVE &&
1026                        user.empty())
1027                {
1028                        XRCCTRL(*this, "ID_USER", wxTextCtrl)->SetFocus();
1029                        wxMessageBoxEx(_("You have to specify a user name"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1030                        return false;
1031                }
1032
1033                // We don't allow username of only spaces, confuses both users and XML libraries
1034                if (!user.empty()) {
1035                        bool space_only = true;
1036                        for (unsigned int i = 0; i < user.Len(); ++i) {
1037                                if (user[i] != ' ') {
1038                                        space_only = false;
1039                                        break;
1040                                }
1041                        }
1042                        if (space_only) {
1043                                XRCCTRL(*this, "ID_USER", wxTextCtrl)->SetFocus();
1044                                wxMessageBoxEx(_("Username cannot be a series of spaces"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1045                                return false;
1046                        }
1047                }
1048
1049                // Require account for account logon type
1050                if (logon_type == ACCOUNT &&
1051                        XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->GetValue().empty())
1052                {
1053                        XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->SetFocus();
1054                        wxMessageBoxEx(_("You have to enter an account name"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1055                        return false;
1056                }
1057
1058                // In key file logon type, check that the provided key file exists
1059                wxString keyFile, keyFileComment, keyFileData;
1060                if (logon_type == KEY) {
1061                        keyFile = XRCCTRL(*this, "ID_KEYFILE", wxTextCtrl)->GetValue();
1062                        if (keyFile.empty()) {
1063                                wxMessageBox(_("You have to enter a key file path"), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1064                                xrc_call(*this, "ID_KEYFILE", &wxWindow::SetFocus);
1065                                return false;
1066                        }
1067
1068                        // Check (again) that the key file is in the correct format since it might have been introduced manually
1069                        CFZPuttyGenInterface cfzg(this);
1070                        if (cfzg.LoadKeyFile(keyFile, false, keyFileComment, keyFileData))
1071                                XRCCTRL(*this, "ID_KEYFILE", wxTextCtrl)->ChangeValue(keyFile);
1072                        else {
1073                                xrc_call(*this, "ID_KEYFILE", &wxWindow::SetFocus);
1074                                return false;
1075                        }
1076                }
1077
1078                const wxString remotePathRaw = XRCCTRL(*this, "ID_REMOTEDIR", wxTextCtrl)->GetValue();
1079                if (!remotePathRaw.empty()) {
1080                        const wxString serverType = XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->GetStringSelection();
1081
1082                        CServerPath remotePath;
1083                        remotePath.SetType(CServer::GetServerTypeFromName(serverType));
1084                        if (!remotePath.SetPath(remotePathRaw))
1085                        {
1086                                XRCCTRL(*this, "ID_REMOTEDIR", wxTextCtrl)->SetFocus();
1087                                wxMessageBoxEx(_("Default remote path cannot be parsed. Make sure it is a valid absolute path for the selected server type."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1088                                return false;
1089                        }
1090                }
1091
1092                const wxString localPath = XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->GetValue();
1093                if (XRCCTRL(*this, "ID_SYNC", wxCheckBox)->GetValue()) {
1094                        if (remotePathRaw.empty() || localPath.empty()) {
1095                                XRCCTRL(*this, "ID_SYNC", wxCheckBox)->SetFocus();
1096                                wxMessageBoxEx(_("You need to enter both a local and a remote path to enable synchronized browsing for this site."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1097                                return false;
1098                        }
1099                }
1100        }
1101        else {
1102                wxTreeItemId parent = pTree->GetItemParent(item);
1103                CSiteManagerItemData_Site* pServer = static_cast<CSiteManagerItemData_Site* >(pTree->GetItemData(parent));
1104                if (!pServer)
1105                        return false;
1106
1107                const wxString remotePathRaw = XRCCTRL(*this, "ID_BOOKMARK_REMOTEDIR", wxTextCtrl)->GetValue();
1108                if (!remotePathRaw.empty()) {
1109                        CServerPath remotePath;
1110                        remotePath.SetType(pServer->m_server.GetType());
1111                        if (!remotePath.SetPath(remotePathRaw)) {
1112                                XRCCTRL(*this, "ID_BOOKMARK_REMOTEDIR", wxTextCtrl)->SetFocus();
1113                                wxString msg;
1114                                if (pServer->m_server.GetType() != DEFAULT)
1115                                        msg = wxString::Format(_("Remote path cannot be parsed. Make sure it is a valid absolute path and is supported by the servertype (%s) selected on the parent site."), CServer::GetNameFromServerType(pServer->m_server.GetType()));
1116                                else
1117                                        msg = _("Remote path cannot be parsed. Make sure it is a valid absolute path.");
1118                                wxMessageBoxEx(msg, _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1119                                return false;
1120                        }
1121                }
1122
1123                const wxString localPath = XRCCTRL(*this, "ID_BOOKMARK_LOCALDIR", wxTextCtrl)->GetValue();
1124
1125                if (remotePathRaw.empty() && localPath.empty()) {
1126                        XRCCTRL(*this, "ID_BOOKMARK_LOCALDIR", wxTextCtrl)->SetFocus();
1127                        wxMessageBoxEx(_("You need to enter at least one path, empty bookmarks are not supported."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1128                        return false;
1129                }
1130
1131                if (XRCCTRL(*this, "ID_BOOKMARK_SYNC", wxCheckBox)->GetValue()) {
1132                        if (remotePathRaw.empty() || localPath.empty()) {
1133                                XRCCTRL(*this, "ID_BOOKMARK_SYNC", wxCheckBox)->SetFocus();
1134                                wxMessageBoxEx(_("You need to enter both a local and a remote path to enable synchronized browsing for this bookmark."), _("Site Manager - Invalid data"), wxICON_EXCLAMATION, this);
1135                                return false;
1136                        }
1137                }
1138        }
1139
1140        return true;
1141}
1142
1143void CSiteManagerDialog::OnBeginLabelEdit(wxTreeEvent& event)
1144{
1145        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1146        if (!pTree) {
1147                event.Veto();
1148                return;
1149        }
1150
1151        if (event.GetItem() != pTree->GetSelection()) {
1152                if (!Verify()) {
1153                        event.Veto();
1154                        return;
1155                }
1156        }
1157
1158        wxTreeItemId item = event.GetItem();
1159        if (!item.IsOk() || item == pTree->GetRootItem() || item == m_ownSites || IsPredefinedItem(item)) {
1160                event.Veto();
1161                return;
1162        }
1163}
1164
1165void CSiteManagerDialog::OnEndLabelEdit(wxTreeEvent& event)
1166{
1167        if (event.IsEditCancelled())
1168                return;
1169
1170        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1171        if (!pTree) {
1172                event.Veto();
1173                return;
1174        }
1175
1176        wxTreeItemId item = event.GetItem();
1177        if (item != pTree->GetSelection()) {
1178                if (!Verify()) {
1179                        event.Veto();
1180                        return;
1181                }
1182        }
1183
1184        if (!item.IsOk() || item == pTree->GetRootItem() || item == m_ownSites || IsPredefinedItem(item)) {
1185                event.Veto();
1186                return;
1187        }
1188
1189        wxString name = event.GetLabel();
1190
1191        wxTreeItemId parent = pTree->GetItemParent(item);
1192
1193        wxTreeItemIdValue cookie;
1194        for (wxTreeItemId child = pTree->GetFirstChild(parent, cookie); child.IsOk(); child = pTree->GetNextChild(parent, cookie)) {
1195                if (child == item)
1196                        continue;
1197                if (!name.CmpNoCase(pTree->GetItemText(child))) {
1198                        wxMessageBoxEx(_("Name already exists"), _("Cannot rename entry"), wxICON_EXCLAMATION, this);
1199                        event.Veto();
1200                        return;
1201                }
1202        }
1203
1204        pTree->SortChildren(parent);
1205}
1206
1207void CSiteManagerDialog::OnRename(wxCommandEvent& event)
1208{
1209        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1210        if (!pTree)
1211                return;
1212
1213        wxTreeItemId item = pTree->GetSelection();
1214        if (!item.IsOk() || item == pTree->GetRootItem() || item == m_ownSites || IsPredefinedItem(item))
1215                return;
1216
1217        pTree->EditLabel(item);
1218}
1219
1220void CSiteManagerDialog::OnDelete(wxCommandEvent& event)
1221{
1222        wxTreeCtrlEx *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrlEx);
1223        if (!pTree)
1224                return;
1225
1226        wxTreeItemId item = pTree->GetSelection();
1227        if (!item.IsOk() || item == pTree->GetRootItem() || item == m_ownSites || IsPredefinedItem(item))
1228                return;
1229
1230        CConditionalDialog dlg(this, CConditionalDialog::sitemanager_confirmdelete, CConditionalDialog::yesno);
1231        dlg.SetTitle(_("Delete Site Manager entry"));
1232
1233        dlg.AddText(_("Do you really want to delete selected entry?"));
1234
1235        if (!dlg.Run())
1236                return;
1237
1238        wxTreeItemId parent = pTree->GetItemParent(item);
1239        if (pTree->GetChildrenCount(parent) == 1)
1240                pTree->Collapse(parent);
1241
1242        m_is_deleting = true;
1243
1244        pTree->Delete(item);
1245        pTree->SafeSelectItem(parent);
1246
1247        m_is_deleting = false;
1248
1249        SetCtrlState();
1250}
1251
1252void CSiteManagerDialog::OnSelChanging(wxTreeEvent& event)
1253{
1254        if (m_is_deleting)
1255                return;
1256
1257        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1258        if (!pTree)
1259                return;
1260
1261        if (!Verify())
1262                event.Veto();
1263
1264        UpdateItem();
1265}
1266
1267void CSiteManagerDialog::OnSelChanged(wxTreeEvent& event)
1268{
1269        SetCtrlState();
1270}
1271
1272void CSiteManagerDialog::OnNewSite(wxCommandEvent& event)
1273{
1274        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1275        if (!pTree)
1276                return;
1277
1278        wxTreeItemId item = pTree->GetSelection();
1279        if (!item.IsOk() || IsPredefinedItem(item))
1280                return;
1281
1282        while (pTree->GetItemData(item))
1283                item = pTree->GetItemParent(item);
1284
1285        if (!Verify())
1286                return;
1287
1288        CServer server;
1289        AddNewSite(item, server);
1290}
1291
1292void CSiteManagerDialog::OnLogontypeSelChanged(wxCommandEvent& event)
1293{
1294        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1295        if (!pTree)
1296                return;
1297
1298        wxTreeItemId item = pTree->GetSelection();
1299        if (!item.IsOk())
1300                return;
1301
1302        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
1303        if (!data)
1304                return;
1305
1306        SetControlVisibility(GetProtocol(), GetLogonType());
1307
1308        XRCCTRL(*this, "ID_USER", wxTextCtrl)->Enable(event.GetString() != _("Anonymous"));
1309        XRCCTRL(*this, "ID_PASS", wxTextCtrl)->Enable(event.GetString() == _("Normal") || event.GetString() == _("Account"));
1310        XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->Enable(event.GetString() == _("Account"));
1311        XRCCTRL(*this, "ID_KEYFILE", wxTextCtrl)->Enable(event.GetString() == _("Key file"));
1312        XRCCTRL(*this, "ID_KEYFILE_BROWSE", wxButton)->Enable(event.GetString() == _("Key file"));
1313}
1314
1315bool CSiteManagerDialog::UpdateItem()
1316{
1317        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1318        if (!pTree)
1319                return false;
1320
1321        wxTreeItemId item = pTree->GetSelection();
1322        if (!item.IsOk())
1323                return false;
1324
1325        if (IsPredefinedItem(item))
1326                return true;
1327
1328        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
1329        if (!data)
1330                return false;
1331
1332        if (data->m_type == CSiteManagerItemData::SITE)
1333                return UpdateServer(*(CSiteManagerItemData_Site *)data, pTree->GetItemText(item));
1334        else
1335        {
1336                wxTreeItemId parent = pTree->GetItemParent(item);
1337                CSiteManagerItemData_Site* pServer = static_cast<CSiteManagerItemData_Site* >(pTree->GetItemData(parent));
1338                if (!pServer)
1339                        return false;
1340                return UpdateBookmark(*data, pServer->m_server);
1341        }
1342}
1343
1344bool CSiteManagerDialog::UpdateBookmark(CSiteManagerItemData &bookmark, const CServer& server)
1345{
1346        bookmark.m_localDir = xrc_call(*this, "ID_BOOKMARK_LOCALDIR", &wxTextCtrl::GetValue);
1347        bookmark.m_remoteDir = CServerPath();
1348        bookmark.m_remoteDir.SetType(server.GetType());
1349        bookmark.m_remoteDir.SetPath(xrc_call(*this, "ID_BOOKMARK_REMOTEDIR", &wxTextCtrl::GetValue));
1350        bookmark.m_sync = xrc_call(*this, "ID_BOOKMARK_SYNC", &wxCheckBox::GetValue);
1351        bookmark.m_comparison = xrc_call(*this, "ID_BOOKMARK_COMPARISON", &wxCheckBox::GetValue);
1352
1353        return true;
1354}
1355
1356bool CSiteManagerDialog::UpdateServer(CSiteManagerItemData_Site &server, const wxString &name)
1357{
1358        ServerProtocol const protocol = GetProtocol();
1359        wxASSERT(protocol != UNKNOWN);
1360        server.m_server.SetProtocol(protocol);
1361
1362        unsigned long port;
1363        if (!xrc_call(*this, "ID_PORT", &wxTextCtrl::GetValue).ToULong(&port) || !port || port > 65535) {
1364                port = CServer::GetDefaultPort(protocol);
1365        }
1366        wxString host = xrc_call(*this, "ID_HOST", &wxTextCtrl::GetValue);
1367        // SetHost does not accept URL syntax
1368        if (!host.empty() && host[0] == '[') {
1369                host.RemoveLast();
1370                host = host.Mid(1);
1371        }
1372        server.m_server.SetHost(host, port);
1373
1374        auto logon_type = GetLogonType();
1375        server.m_server.SetLogonType(logon_type);
1376
1377        server.m_server.SetUser(xrc_call(*this, "ID_USER", &wxTextCtrl::GetValue),
1378                                                        xrc_call(*this, "ID_PASS", &wxTextCtrl::GetValue));
1379        server.m_server.SetAccount(xrc_call(*this, "ID_ACCOUNT", &wxTextCtrl::GetValue));
1380
1381        server.m_server.SetKeyFile(XRCCTRL(*this, "ID_KEYFILE", wxTextCtrl)->GetValue());
1382
1383        server.m_comments = xrc_call(*this, "ID_COMMENTS", &wxTextCtrl::GetValue);
1384
1385        const wxString serverType = xrc_call(*this, "ID_SERVERTYPE", &wxChoice::GetStringSelection);
1386        server.m_server.SetType(CServer::GetServerTypeFromName(serverType));
1387
1388        server.m_localDir = xrc_call(*this, "ID_LOCALDIR", &wxTextCtrl::GetValue);
1389        server.m_remoteDir = CServerPath();
1390        server.m_remoteDir.SetType(server.m_server.GetType());
1391        server.m_remoteDir.SetPath(xrc_call(*this, "ID_REMOTEDIR", &wxTextCtrl::GetValue));
1392        server.m_sync = xrc_call(*this, "ID_SYNC", &wxCheckBox::GetValue);
1393        server.m_comparison = xrc_call(*this, "ID_COMPARISON", &wxCheckBox::GetValue);
1394
1395        int hours = xrc_call(*this, "ID_TIMEZONE_HOURS", &wxSpinCtrl::GetValue);
1396        int minutes = xrc_call(*this, "ID_TIMEZONE_MINUTES", &wxSpinCtrl::GetValue);
1397
1398        server.m_server.SetTimezoneOffset(hours * 60 + minutes);
1399
1400        if (xrc_call(*this, "ID_TRANSFERMODE_ACTIVE", &wxRadioButton::GetValue))
1401                server.m_server.SetPasvMode(MODE_ACTIVE);
1402        else if (xrc_call(*this, "ID_TRANSFERMODE_PASSIVE", &wxRadioButton::GetValue))
1403                server.m_server.SetPasvMode(MODE_PASSIVE);
1404        else
1405                server.m_server.SetPasvMode(MODE_DEFAULT);
1406
1407        if (xrc_call(*this, "ID_LIMITMULTIPLE", &wxCheckBox::GetValue)) {
1408                server.m_server.MaximumMultipleConnections(xrc_call(*this, "ID_MAXMULTIPLE", &wxSpinCtrl::GetValue));
1409        }
1410        else
1411                server.m_server.MaximumMultipleConnections(0);
1412
1413        if (xrc_call(*this, "ID_CHARSET_UTF8", &wxRadioButton::GetValue))
1414                server.m_server.SetEncodingType(ENCODING_UTF8);
1415        else if (xrc_call(*this, "ID_CHARSET_CUSTOM", &wxRadioButton::GetValue)) {
1416                wxString encoding = xrc_call(*this, "ID_ENCODING", &wxTextCtrl::GetValue);
1417                server.m_server.SetEncodingType(ENCODING_CUSTOM, encoding);
1418        }
1419        else
1420                server.m_server.SetEncodingType(ENCODING_AUTO);
1421
1422        if (xrc_call(*this, "ID_BYPASSPROXY", &wxCheckBox::GetValue))
1423                server.m_server.SetBypassProxy(true);
1424        else
1425                server.m_server.SetBypassProxy(false);
1426
1427        server.m_server.SetName(name);
1428
1429        return true;
1430}
1431
1432bool CSiteManagerDialog::GetServer(CSiteManagerItemData_Site& data)
1433{
1434        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1435        if (!pTree)
1436                return false;
1437
1438        wxTreeItemId item = pTree->GetSelection();
1439        if (!item.IsOk())
1440                return false;
1441
1442        CSiteManagerItemData* pData = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
1443        if (!pData)
1444                return false;
1445
1446        if (pData->m_type == CSiteManagerItemData::BOOKMARK) {
1447                item = pTree->GetItemParent(item);
1448                CSiteManagerItemData_Site* pSiteData = static_cast<CSiteManagerItemData_Site* >(pTree->GetItemData(item));
1449
1450                data = *pSiteData;
1451                if (!pData->m_localDir.empty())
1452                        data.m_localDir = pData->m_localDir;
1453                if (!pData->m_remoteDir.empty())
1454                        data.m_remoteDir = pData->m_remoteDir;
1455                if (data.m_localDir.empty() || data.m_remoteDir.empty())
1456                        data.m_sync = false;
1457                else
1458                        data.m_sync = pData->m_sync;
1459        }
1460        else
1461                data = *(CSiteManagerItemData_Site *)pData;
1462
1463        data.m_path = GetSitePath(item);
1464
1465        return true;
1466}
1467
1468void CSiteManagerDialog::OnKeyFileBrowse(wxCommandEvent& event)
1469{
1470        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1471        if (!pTree)
1472                return;
1473
1474        wxTreeItemId item = pTree->GetSelection();
1475        if (!item.IsOk())
1476                return;
1477
1478        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
1479        if (!data)
1480                return;
1481
1482        wxString wildcards(_T("PPK files|*.ppk|PEM files|*.pem|All files|*.*"));
1483        wxFileDialog dlg(this, _("Choose a key file"), wxString(), wxString(), wildcards, wxFD_OPEN|wxFD_FILE_MUST_EXIST);
1484
1485        if (dlg.ShowModal() == wxID_OK) {
1486                wxString keyFilePath = dlg.GetPath();
1487                // If the selected file was a PEM file, LoadKeyFile() will automatically convert it to PPK
1488                // and tell us the new location.
1489                CFZPuttyGenInterface fzpg(this);
1490
1491                wxString keyFileComment, keyFileData;
1492                if (fzpg.LoadKeyFile(keyFilePath, false, keyFileComment, keyFileData)) {
1493                        XRCCTRL(*this, "ID_KEYFILE", wxTextCtrl)->ChangeValue(keyFilePath);
1494                }
1495                else {
1496                        xrc_call(*this, "ID_KEYFILE", &wxWindow::SetFocus);
1497                }
1498        }
1499}
1500void CSiteManagerDialog::OnRemoteDirBrowse(wxCommandEvent& event)
1501{
1502        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1503        if (!pTree)
1504                return;
1505
1506        wxTreeItemId item = pTree->GetSelection();
1507        if (!item.IsOk())
1508                return;
1509
1510        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
1511        if (!data)
1512                return;
1513
1514        wxDirDialog dlg(this, _("Choose the default local directory"), XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->GetValue(), wxDD_NEW_DIR_BUTTON);
1515        if (dlg.ShowModal() == wxID_OK) {
1516                XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->ChangeValue(dlg.GetPath());
1517        }
1518}
1519
1520void CSiteManagerDialog::OnItemActivated(wxTreeEvent& event)
1521{
1522        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1523        if (!pTree)
1524                return;
1525
1526        wxTreeItemId item = pTree->GetSelection();
1527        if (!item.IsOk())
1528                return;
1529
1530        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
1531        if (!data)
1532                return;
1533
1534        wxCommandEvent cmdEvent;
1535        OnConnect(cmdEvent);
1536}
1537
1538void CSiteManagerDialog::OnLimitMultipleConnectionsChanged(wxCommandEvent& event)
1539{
1540        XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->Enable(event.IsChecked());
1541}
1542
1543void CSiteManagerDialog::SetCtrlState()
1544{
1545        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1546        if (!pTree)
1547                return;
1548
1549        wxTreeItemId item = pTree->GetSelection();
1550
1551        const bool predefined = IsPredefinedItem(item);
1552
1553#ifdef __WXGTK__
1554        wxWindow* pFocus = FindFocus();
1555#endif
1556
1557        CSiteManagerItemData* data = 0;
1558        if (item.IsOk())
1559                data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
1560        if (!data) {
1561                m_pNotebook_Site->Show();
1562                m_pNotebook_Bookmark->Hide();
1563                m_pNotebook_Site->GetContainingSizer()->Layout();
1564
1565                // Set the control states according if it's possible to use the control
1566                const bool root_or_predefined = (item == pTree->GetRootItem() || item == m_ownSites || predefined);
1567
1568                XRCCTRL(*this, "ID_RENAME", wxWindow)->Enable(!root_or_predefined);
1569                XRCCTRL(*this, "ID_DELETE", wxWindow)->Enable(!root_or_predefined);
1570                XRCCTRL(*this, "ID_COPY", wxWindow)->Enable(false);
1571                m_pNotebook_Site->Enable(false);
1572                XRCCTRL(*this, "ID_NEWFOLDER", wxWindow)->Enable(!predefined);
1573                XRCCTRL(*this, "ID_NEWSITE", wxWindow)->Enable(!predefined);
1574                XRCCTRL(*this, "ID_NEWBOOKMARK", wxWindow)->Enable(false);
1575                XRCCTRL(*this, "ID_CONNECT", wxButton)->Enable(false);
1576
1577                // Empty all site information
1578                XRCCTRL(*this, "ID_HOST", wxTextCtrl)->ChangeValue(wxString());
1579                XRCCTRL(*this, "ID_PORT", wxTextCtrl)->ChangeValue(wxString());
1580                SetProtocol(FTP);
1581                XRCCTRL(*this, "ID_BYPASSPROXY", wxCheckBox)->SetValue(false);
1582                XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(_("Anonymous"));
1583                XRCCTRL(*this, "ID_USER", wxTextCtrl)->ChangeValue(wxString());
1584                XRCCTRL(*this, "ID_PASS", wxTextCtrl)->ChangeValue(wxString());
1585                XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->ChangeValue(wxString());
1586                XRCCTRL(*this, "ID_KEYFILE", wxTextCtrl)->ChangeValue(wxString());
1587                XRCCTRL(*this, "ID_COMMENTS", wxTextCtrl)->ChangeValue(wxString());
1588
1589                SetControlVisibility(FTP, ANONYMOUS);
1590
1591                XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->SetSelection(0);
1592                XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->ChangeValue(wxString());
1593                XRCCTRL(*this, "ID_REMOTEDIR", wxTextCtrl)->ChangeValue(wxString());
1594                XRCCTRL(*this, "ID_SYNC", wxCheckBox)->SetValue(false);
1595                XRCCTRL(*this, "ID_TIMEZONE_HOURS", wxSpinCtrl)->SetValue(0);
1596                XRCCTRL(*this, "ID_TIMEZONE_MINUTES", wxSpinCtrl)->SetValue(0);
1597
1598                XRCCTRL(*this, "ID_TRANSFERMODE_DEFAULT", wxRadioButton)->SetValue(true);
1599                XRCCTRL(*this, "ID_LIMITMULTIPLE", wxCheckBox)->SetValue(false);
1600                XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->SetValue(1);
1601
1602                XRCCTRL(*this, "ID_CHARSET_AUTO", wxRadioButton)->SetValue(true);
1603                XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->ChangeValue(wxString());
1604#ifdef __WXGTK__
1605                XRCCTRL(*this, "wxID_OK", wxButton)->SetDefault();
1606#endif
1607        }
1608        else if (data->m_type == CSiteManagerItemData::SITE) {
1609                m_pNotebook_Site->Show();
1610                m_pNotebook_Bookmark->Hide();
1611                m_pNotebook_Site->GetContainingSizer()->Layout();
1612
1613                CSiteManagerItemData_Site* site_data = (CSiteManagerItemData_Site *)data;
1614
1615                // Set the control states according if it's possible to use the control
1616                XRCCTRL(*this, "ID_RENAME", wxWindow)->Enable(!predefined);
1617                XRCCTRL(*this, "ID_DELETE", wxWindow)->Enable(!predefined);
1618                XRCCTRL(*this, "ID_COPY", wxWindow)->Enable(true);
1619                m_pNotebook_Site->Enable(true);
1620                XRCCTRL(*this, "ID_NEWFOLDER", wxWindow)->Enable(!predefined);
1621                XRCCTRL(*this, "ID_NEWSITE", wxWindow)->Enable(!predefined);
1622                XRCCTRL(*this, "ID_NEWBOOKMARK", wxWindow)->Enable(!predefined);
1623                XRCCTRL(*this, "ID_CONNECT", wxButton)->Enable(true);
1624
1625                XRCCTRL(*this, "ID_HOST", wxWindow)->Enable(!predefined);
1626                XRCCTRL(*this, "ID_HOST", wxTextCtrl)->ChangeValue(site_data->m_server.FormatHost(true));
1627                unsigned int port = site_data->m_server.GetPort();
1628
1629                if (port != CServer::GetDefaultPort(site_data->m_server.GetProtocol()))
1630                        XRCCTRL(*this, "ID_PORT", wxTextCtrl)->ChangeValue(wxString::Format(_T("%d"), port));
1631                else
1632                        XRCCTRL(*this, "ID_PORT", wxTextCtrl)->ChangeValue(wxString());
1633                XRCCTRL(*this, "ID_PORT", wxWindow)->Enable(!predefined);
1634
1635                SetProtocol(site_data->m_server.GetProtocol());
1636                XRCCTRL(*this, "ID_PROTOCOL", wxWindow)->Enable(!predefined);
1637                XRCCTRL(*this, "ID_ENCRYPTION", wxWindow)->Enable(!predefined);
1638                XRCCTRL(*this, "ID_BYPASSPROXY", wxCheckBox)->SetValue(site_data->m_server.GetBypassProxy());
1639
1640                XRCCTRL(*this, "ID_USER", wxTextCtrl)->Enable(!predefined && site_data->m_server.GetLogonType() != ANONYMOUS);
1641                XRCCTRL(*this, "ID_PASS", wxTextCtrl)->Enable(!predefined && (site_data->m_server.GetLogonType() == NORMAL || site_data->m_server.GetLogonType() == ACCOUNT));
1642                XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->Enable(!predefined && site_data->m_server.GetLogonType() == ACCOUNT);
1643                XRCCTRL(*this, "ID_KEYFILE", wxTextCtrl)->Enable(!predefined && site_data->m_server.GetLogonType() == KEY);
1644                XRCCTRL(*this, "ID_KEYFILE_BROWSE", wxButton)->Enable(!predefined && site_data->m_server.GetLogonType() == KEY);
1645
1646                SetControlVisibility(site_data->m_server.GetProtocol(), site_data->m_server.GetLogonType());
1647
1648                XRCCTRL(*this, "ID_LOGONTYPE", wxChoice)->SetStringSelection(CServer::GetNameFromLogonType(site_data->m_server.GetLogonType()));
1649                XRCCTRL(*this, "ID_LOGONTYPE", wxWindow)->Enable(!predefined);
1650
1651                XRCCTRL(*this, "ID_USER", wxTextCtrl)->ChangeValue(site_data->m_server.GetUser());
1652                XRCCTRL(*this, "ID_ACCOUNT", wxTextCtrl)->ChangeValue(site_data->m_server.GetAccount());
1653                XRCCTRL(*this, "ID_PASS", wxTextCtrl)->ChangeValue(site_data->m_server.GetPass());
1654                XRCCTRL(*this, "ID_KEYFILE", wxTextCtrl)->ChangeValue(site_data->m_server.GetKeyFile());
1655                XRCCTRL(*this, "ID_COMMENTS", wxTextCtrl)->ChangeValue(site_data->m_comments);
1656                XRCCTRL(*this, "ID_COMMENTS", wxWindow)->Enable(!predefined);
1657
1658                XRCCTRL(*this, "ID_SERVERTYPE", wxChoice)->SetSelection(site_data->m_server.GetType());
1659                XRCCTRL(*this, "ID_SERVERTYPE", wxWindow)->Enable(!predefined);
1660                XRCCTRL(*this, "ID_LOCALDIR", wxTextCtrl)->ChangeValue(site_data->m_localDir);
1661                XRCCTRL(*this, "ID_LOCALDIR", wxWindow)->Enable(!predefined);
1662                XRCCTRL(*this, "ID_REMOTEDIR", wxTextCtrl)->ChangeValue(site_data->m_remoteDir.GetPath());
1663                XRCCTRL(*this, "ID_REMOTEDIR", wxWindow)->Enable(!predefined);
1664                XRCCTRL(*this, "ID_SYNC", wxCheckBox)->Enable(!predefined);
1665                XRCCTRL(*this, "ID_SYNC", wxCheckBox)->SetValue(site_data->m_sync);
1666                XRCCTRL(*this, "ID_COMPARISON", wxCheckBox)->Enable(!predefined);
1667                XRCCTRL(*this, "ID_COMPARISON", wxCheckBox)->SetValue(site_data->m_comparison);
1668                XRCCTRL(*this, "ID_TIMEZONE_HOURS", wxSpinCtrl)->SetValue(site_data->m_server.GetTimezoneOffset() / 60);
1669                XRCCTRL(*this, "ID_TIMEZONE_HOURS", wxWindow)->Enable(!predefined);
1670                XRCCTRL(*this, "ID_TIMEZONE_MINUTES", wxSpinCtrl)->SetValue(site_data->m_server.GetTimezoneOffset() % 60);
1671                XRCCTRL(*this, "ID_TIMEZONE_MINUTES", wxWindow)->Enable(!predefined);
1672
1673                enum PasvMode pasvMode = site_data->m_server.GetPasvMode();
1674                if (pasvMode == MODE_ACTIVE)
1675                        XRCCTRL(*this, "ID_TRANSFERMODE_ACTIVE", wxRadioButton)->SetValue(true);
1676                else if (pasvMode == MODE_PASSIVE)
1677                        XRCCTRL(*this, "ID_TRANSFERMODE_PASSIVE", wxRadioButton)->SetValue(true);
1678                else
1679                        XRCCTRL(*this, "ID_TRANSFERMODE_DEFAULT", wxRadioButton)->SetValue(true);
1680                XRCCTRL(*this, "ID_TRANSFERMODE_ACTIVE", wxWindow)->Enable(!predefined);
1681                XRCCTRL(*this, "ID_TRANSFERMODE_PASSIVE", wxWindow)->Enable(!predefined);
1682                XRCCTRL(*this, "ID_TRANSFERMODE_DEFAULT", wxWindow)->Enable(!predefined);
1683
1684                int maxMultiple = site_data->m_server.MaximumMultipleConnections();
1685                XRCCTRL(*this, "ID_LIMITMULTIPLE", wxCheckBox)->SetValue(maxMultiple != 0);
1686                XRCCTRL(*this, "ID_LIMITMULTIPLE", wxWindow)->Enable(!predefined);
1687                if (maxMultiple != 0) {
1688                        XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->Enable(!predefined);
1689                        XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->SetValue(maxMultiple);
1690                }
1691                else {
1692                        XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->Enable(false);
1693                        XRCCTRL(*this, "ID_MAXMULTIPLE", wxSpinCtrl)->SetValue(1);
1694                }
1695
1696                switch (site_data->m_server.GetEncodingType()) {
1697                default:
1698                case ENCODING_AUTO:
1699                        XRCCTRL(*this, "ID_CHARSET_AUTO", wxRadioButton)->SetValue(true);
1700                        break;
1701                case ENCODING_UTF8:
1702                        XRCCTRL(*this, "ID_CHARSET_UTF8", wxRadioButton)->SetValue(true);
1703                        break;
1704                case ENCODING_CUSTOM:
1705                        XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxRadioButton)->SetValue(true);
1706                        break;
1707                }
1708                XRCCTRL(*this, "ID_CHARSET_AUTO", wxWindow)->Enable(!predefined);
1709                XRCCTRL(*this, "ID_CHARSET_UTF8", wxWindow)->Enable(!predefined);
1710                XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxWindow)->Enable(!predefined);
1711                XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->Enable(!predefined && site_data->m_server.GetEncodingType() == ENCODING_CUSTOM);
1712                XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->ChangeValue(site_data->m_server.GetCustomEncoding());
1713#ifdef __WXGTK__
1714                XRCCTRL(*this, "ID_CONNECT", wxButton)->SetDefault();
1715#endif
1716        }
1717        else {
1718                m_pNotebook_Site->Hide();
1719                m_pNotebook_Bookmark->Show();
1720                m_pNotebook_Site->GetContainingSizer()->Layout();
1721
1722                // Set the control states according if it's possible to use the control
1723                XRCCTRL(*this, "ID_RENAME", wxWindow)->Enable(!predefined);
1724                XRCCTRL(*this, "ID_DELETE", wxWindow)->Enable(!predefined);
1725                XRCCTRL(*this, "ID_COPY", wxWindow)->Enable(true);
1726                XRCCTRL(*this, "ID_NEWFOLDER", wxWindow)->Enable(!predefined);
1727                XRCCTRL(*this, "ID_NEWSITE", wxWindow)->Enable(!predefined);
1728                XRCCTRL(*this, "ID_NEWBOOKMARK", wxWindow)->Enable(!predefined);
1729                XRCCTRL(*this, "ID_CONNECT", wxButton)->Enable(true);
1730
1731                XRCCTRL(*this, "ID_BOOKMARK_LOCALDIR", wxTextCtrl)->ChangeValue(data->m_localDir);
1732                XRCCTRL(*this, "ID_BOOKMARK_LOCALDIR", wxWindow)->Enable(!predefined);
1733                XRCCTRL(*this, "ID_BOOKMARK_REMOTEDIR", wxTextCtrl)->ChangeValue(data->m_remoteDir.GetPath());
1734                XRCCTRL(*this, "ID_BOOKMARK_REMOTEDIR", wxWindow)->Enable(!predefined);
1735
1736                XRCCTRL(*this, "ID_BOOKMARK_SYNC", wxCheckBox)->Enable(!predefined);
1737                XRCCTRL(*this, "ID_BOOKMARK_SYNC", wxCheckBox)->SetValue(data->m_sync);
1738                XRCCTRL(*this, "ID_BOOKMARK_COMPARISON", wxCheckBox)->Enable(!predefined);
1739                XRCCTRL(*this, "ID_BOOKMARK_COMPARISON", wxCheckBox)->SetValue(data->m_comparison);
1740        }
1741#ifdef __WXGTK__
1742        if (pFocus && !pFocus->IsEnabled()) {
1743                for (wxWindow* pParent = pFocus->GetParent(); pParent; pParent = pParent->GetParent()) {
1744                        if (pParent == this) {
1745                                XRCCTRL(*this, "wxID_OK", wxButton)->SetFocus();
1746                                break;
1747                        }
1748                }
1749        }
1750#endif
1751}
1752
1753void CSiteManagerDialog::OnCharsetChange(wxCommandEvent& event)
1754{
1755        bool checked = XRCCTRL(*this, "ID_CHARSET_CUSTOM", wxRadioButton)->GetValue();
1756        XRCCTRL(*this, "ID_ENCODING", wxTextCtrl)->Enable(checked);
1757}
1758
1759void CSiteManagerDialog::OnProtocolSelChanged(wxCommandEvent& event)
1760{
1761        SetControlVisibility(GetProtocol(), GetLogonType());
1762}
1763
1764void CSiteManagerDialog::SetControlVisibility(ServerProtocol protocol, LogonType type)
1765{
1766        xrc_call(*this, "ID_ENCRYPTION_DESC", &wxStaticText::Show, protocol != SFTP);
1767        xrc_call(*this, "ID_ENCRYPTION", &wxChoice::Show, protocol != SFTP);
1768
1769        auto choice = XRCCTRL(*this, "ID_LOGONTYPE", wxChoice);
1770
1771        // Disallow invalid combinations
1772        if (protocol == SFTP && type == ACCOUNT) {
1773                type = NORMAL;
1774                choice->Select(choice->FindString(CServer::GetNameFromLogonType(type)));
1775        }
1776        else if (protocol != SFTP && type == KEY) {
1777                type = NORMAL;
1778                choice->Select(choice->FindString(CServer::GetNameFromLogonType(type)));
1779        }
1780
1781        int toDelete = choice->FindString(CServer::GetNameFromLogonType(protocol == SFTP ? ACCOUNT : KEY));
1782        int toAdd    = choice->FindString(CServer::GetNameFromLogonType(protocol != SFTP ? ACCOUNT : KEY));
1783        if (toDelete != -1) {
1784                choice->Delete(toDelete);
1785        }
1786        if (toAdd == -1) {
1787                choice->Append(CServer::GetNameFromLogonType(protocol != SFTP ? ACCOUNT : KEY));
1788        }
1789
1790        xrc_call(*this, "ID_ACCOUNT_DESC", &wxStaticText::Show, protocol != SFTP && type == ACCOUNT);
1791        xrc_call(*this, "ID_ACCOUNT", &wxTextCtrl::Show, protocol != SFTP && type == ACCOUNT);
1792        xrc_call(*this, "ID_PASS_DESC", &wxStaticText::Show, protocol != SFTP || type != KEY);
1793        xrc_call(*this, "ID_PASS", &wxTextCtrl::Show, protocol != SFTP || type != KEY);
1794        xrc_call(*this, "ID_KEYFILE_DESC", &wxStaticText::Show, protocol == SFTP && type == KEY);
1795        xrc_call(*this, "ID_KEYFILE", &wxTextCtrl::Show, protocol == SFTP && type == KEY);
1796        xrc_call(*this, "ID_KEYFILE_BROWSE", &wxButton::Show, protocol == SFTP && type == KEY);
1797
1798        xrc_call(*this, "ID_KEYFILE_DESC", &wxStaticText::GetContainingSizer)->CalcMin();
1799        xrc_call(*this, "ID_KEYFILE_DESC", &wxStaticText::GetContainingSizer)->Layout();
1800}
1801
1802void CSiteManagerDialog::OnCopySite(wxCommandEvent& event)
1803{
1804        wxTreeCtrlEx *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrlEx);
1805        if (!pTree)
1806                return;
1807
1808        wxTreeItemId item = pTree->GetSelection();
1809        if (!item.IsOk())
1810                return;
1811
1812        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
1813        if (!data)
1814                return;
1815
1816        if (!Verify())
1817                return;
1818
1819        if (!UpdateItem())
1820                return;
1821
1822        wxTreeItemId parent;
1823        if (IsPredefinedItem(item))
1824                parent = m_ownSites;
1825        else
1826                parent = pTree->GetItemParent(item);
1827
1828        wxString const name = pTree->GetItemText(item);
1829        wxString newName = wxString::Format(_("Copy of %s"), name);
1830        int index = 2;
1831        for (;;) {
1832                wxTreeItemId child;
1833                wxTreeItemIdValue cookie;
1834                child = pTree->GetFirstChild(parent, cookie);
1835                bool found = false;
1836                while (child.IsOk()) {
1837                        wxString const child_name = pTree->GetItemText(child);
1838                        int cmp = child_name.CmpNoCase(newName);
1839                        if (!cmp) {
1840                                found = true;
1841                                break;
1842                        }
1843
1844                        child = pTree->GetNextChild(parent, cookie);
1845                }
1846                if (!found)
1847                        break;
1848
1849                newName = wxString::Format(_("Copy (%d) of %s"), ++index, name);
1850        }
1851
1852        wxTreeItemId newItem;
1853        if (data->m_type == CSiteManagerItemData::SITE) {
1854                CSiteManagerItemData_Site* newData = new CSiteManagerItemData_Site(*(CSiteManagerItemData_Site *)data);
1855                newData->connected_item = -1;
1856                newItem = pTree->AppendItem(parent, newName, 2, 2, newData);
1857
1858                wxTreeItemIdValue cookie;
1859                for (wxTreeItemId child = pTree->GetFirstChild(item, cookie); child.IsOk(); child = pTree->GetNextChild(item, cookie))
1860                {
1861                        CSiteManagerItemData* pData = new CSiteManagerItemData(*(CSiteManagerItemData *)pTree->GetItemData(child));
1862                        pTree->AppendItem(newItem, pTree->GetItemText(child), 3, 3, pData);
1863                }
1864                if (pTree->IsExpanded(item))
1865                        pTree->Expand(newItem);
1866        }
1867        else {
1868                CSiteManagerItemData* newData = new CSiteManagerItemData(*data);
1869                newItem = pTree->AppendItem(parent, newName, 3, 3, newData);
1870        }
1871        pTree->SortChildren(parent);
1872        pTree->EnsureVisible(newItem);
1873        pTree->SafeSelectItem(newItem);
1874        pTree->EditLabel(newItem);
1875}
1876
1877bool CSiteManagerDialog::LoadDefaultSites()
1878{
1879        CLocalPath const defaultsDir = wxGetApp().GetDefaultsDir();
1880        if (defaultsDir.empty())
1881                return false;
1882
1883        CXmlFile file(defaultsDir.GetPath() + _T("fzdefaults.xml"));
1884
1885        auto document = file.Load();
1886        if (!document)
1887                return false;
1888
1889        auto element = document.child("Servers");
1890        if (!element)
1891                return false;
1892
1893        wxTreeCtrlEx *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrlEx);
1894        if (!pTree)
1895                return false;
1896
1897        int style = pTree->GetWindowStyle();
1898        pTree->SetWindowStyle(style | wxTR_HIDE_ROOT);
1899        wxTreeItemId root = pTree->AddRoot(wxString(), 0, 0);
1900
1901        m_predefinedSites = pTree->AppendItem(root, _("Predefined Sites"), 0, 0);
1902        pTree->SetItemImage(m_predefinedSites, 1, wxTreeItemIcon_Expanded);
1903        pTree->SetItemImage(m_predefinedSites, 1, wxTreeItemIcon_SelectedExpanded);
1904
1905        wxString lastSelection = COptions::Get()->GetOption(OPTION_SITEMANAGER_LASTSELECTED);
1906        if (!lastSelection.empty() && lastSelection[0] == '1') {
1907                if (lastSelection == _T("1"))
1908                        pTree->SafeSelectItem(m_predefinedSites);
1909                else
1910                        lastSelection = lastSelection.Mid(1);
1911        }
1912        else
1913                lastSelection.clear();
1914        CSiteManagerXmlHandler_Tree handler(pTree, m_predefinedSites, lastSelection, true);
1915
1916        CSiteManager::Load(element, handler);
1917
1918        return true;
1919}
1920
1921bool CSiteManagerDialog::IsPredefinedItem(wxTreeItemId item)
1922{
1923        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1924        wxASSERT(pTree);
1925        if (!pTree)
1926                return false;
1927
1928        while (item)
1929        {
1930                if (item == m_predefinedSites)
1931                        return true;
1932                item = pTree->GetItemParent(item);
1933        }
1934
1935        return false;
1936}
1937
1938void CSiteManagerDialog::OnBeginDrag(wxTreeEvent& event)
1939{
1940        if (!Verify())
1941        {
1942                event.Veto();
1943                return;
1944        }
1945        UpdateItem();
1946
1947        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
1948        if (!pTree)
1949        {
1950                event.Veto();
1951                return;
1952        }
1953
1954        wxTreeItemId item = event.GetItem();
1955        if (!item.IsOk())
1956        {
1957                event.Veto();
1958                return;
1959        }
1960
1961        const bool predefined = IsPredefinedItem(item);
1962        const bool root = item == pTree->GetRootItem() || item == m_ownSites;
1963        if (root)
1964        {
1965                event.Veto();
1966                return;
1967        }
1968
1969        CSiteManagerDialogDataObject obj;
1970
1971        wxDropSource source(this);
1972        source.SetData(obj);
1973
1974        m_dropSource = item;
1975
1976        source.DoDragDrop(predefined ? wxDrag_CopyOnly : wxDrag_DefaultMove);
1977
1978        m_dropSource = wxTreeItemId();
1979
1980        SetCtrlState();
1981}
1982
1983struct itempair
1984{
1985        wxTreeItemId source;
1986        wxTreeItemId target;
1987};
1988
1989bool CSiteManagerDialog::MoveItems(wxTreeItemId source, wxTreeItemId target, bool copy)
1990{
1991        if (source == target)
1992                return false;
1993
1994        if (IsPredefinedItem(target))
1995                return false;
1996
1997        if (IsPredefinedItem(source) && !copy)
1998                return false;
1999
2000        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
2001
2002        CSiteManagerItemData *pTargetData = (CSiteManagerItemData *)pTree->GetItemData(target);
2003        CSiteManagerItemData *pSourceData = (CSiteManagerItemData *)pTree->GetItemData(source);
2004        if (pTargetData) {
2005                if (pTargetData->m_type == CSiteManagerItemData::BOOKMARK)
2006                        return false;
2007                if (!pSourceData || pSourceData->m_type != CSiteManagerItemData::BOOKMARK)
2008                        return false;
2009        }
2010        else if (pSourceData && pSourceData->m_type == CSiteManagerItemData::BOOKMARK)
2011                return false;
2012
2013        wxTreeItemId item = target;
2014        while (item != pTree->GetRootItem()) {
2015                if (item == source)
2016                        return false;
2017                item = pTree->GetItemParent(item);
2018        }
2019
2020        if (!copy && pTree->GetItemParent(source) == target)
2021                return false;
2022
2023        wxString sourceName = pTree->GetItemText(source);
2024
2025       
2026        wxTreeItemIdValue cookie;
2027        for (auto child = pTree->GetFirstChild(target, cookie); child.IsOk(); child = pTree->GetNextChild(target, cookie)) {
2028                wxString const childName = pTree->GetItemText(child);
2029                if (!sourceName.CmpNoCase(childName)) {
2030                        wxMessageBoxEx(_("An item with the same name as the dragged item already exists at the target location."), _("Failed to copy or move sites"), wxICON_INFORMATION);
2031                        return false;
2032                }
2033        }
2034
2035        std::list<itempair> work;
2036        itempair initial;
2037        initial.source = source;
2038        initial.target = target;
2039        work.push_back(initial);
2040
2041        std::list<wxTreeItemId> expand;
2042
2043        while (!work.empty()) {
2044                itempair pair = work.front();
2045                work.pop_front();
2046
2047                wxString name = pTree->GetItemText(pair.source);
2048
2049                CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(pair.source));
2050
2051                wxTreeItemId newItem = pTree->AppendItem(pair.target, name, data ? 2 : 0);
2052                if (!data) {
2053                        pTree->SetItemImage(newItem, 1, wxTreeItemIcon_Expanded);
2054                        pTree->SetItemImage(newItem, 1, wxTreeItemIcon_SelectedExpanded);
2055
2056                        if (pTree->IsExpanded(pair.source))
2057                                expand.push_back(newItem);
2058                }
2059                else if (data->m_type == CSiteManagerItemData::SITE) {
2060                        CSiteManagerItemData_Site* newData = new CSiteManagerItemData_Site(*(CSiteManagerItemData_Site *)data);
2061                        newData->connected_item = -1;
2062                        pTree->SetItemData(newItem, newData);
2063                }
2064                else {
2065                        pTree->SetItemImage(newItem, 3, wxTreeItemIcon_Normal);
2066                        pTree->SetItemImage(newItem, 3, wxTreeItemIcon_Selected);
2067
2068                        CSiteManagerItemData* newData = new CSiteManagerItemData(*data);
2069                        pTree->SetItemData(newItem, newData);
2070                }
2071
2072                wxTreeItemIdValue cookie2;
2073                for (auto child = pTree->GetFirstChild(pair.source, cookie2); child.IsOk(); child = pTree->GetNextChild(pair.source, cookie2)) {
2074                        itempair newPair;
2075                        newPair.source = child;
2076                        newPair.target = newItem;
2077                        work.push_back(newPair);
2078                }
2079
2080                pTree->SortChildren(pair.target);
2081        }
2082
2083        if (!copy) {
2084                wxTreeItemId parent = pTree->GetItemParent(source);
2085                if (pTree->GetChildrenCount(parent) == 1)
2086                        pTree->Collapse(parent);
2087
2088                pTree->Delete(source);
2089        }
2090
2091        for (auto iter = expand.begin(); iter != expand.end(); ++iter)
2092                pTree->Expand(*iter);
2093
2094        pTree->Expand(target);
2095
2096        return true;
2097}
2098
2099void CSiteManagerDialog::OnChar(wxKeyEvent& event)
2100{
2101        if (event.GetKeyCode() != WXK_F2)
2102        {
2103                event.Skip();
2104                return;
2105        }
2106
2107        wxCommandEvent cmdEvent;
2108        OnRename(cmdEvent);
2109}
2110
2111void CSiteManagerDialog::CopyAddServer(const CServer& server)
2112{
2113        if (!Verify())
2114                return;
2115
2116        AddNewSite(m_ownSites, server, true);
2117}
2118
2119wxString CSiteManagerDialog::FindFirstFreeName(const wxTreeItemId &parent, const wxString& name)
2120{
2121        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
2122        wxASSERT(pTree);
2123
2124        wxString newName = name;
2125        int index = 2;
2126        for (;;) {
2127                wxTreeItemId child;
2128                wxTreeItemIdValue cookie;
2129                child = pTree->GetFirstChild(parent, cookie);
2130                bool found = false;
2131                while (child.IsOk()) {
2132                        wxString child_name = pTree->GetItemText(child);
2133                        int cmp = child_name.CmpNoCase(newName);
2134                        if (!cmp) {
2135                                found = true;
2136                                break;
2137                        }
2138
2139                        child = pTree->GetNextChild(parent, cookie);
2140                }
2141                if (!found)
2142                        break;
2143
2144                newName = name + wxString::Format(_T(" %d"), ++index);
2145        }
2146
2147        return newName;
2148}
2149
2150void CSiteManagerDialog::AddNewSite(wxTreeItemId parent, const CServer& server, bool connected /*=false*/)
2151{
2152        wxTreeCtrlEx *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrlEx);
2153        if (!pTree)
2154                return;
2155
2156        wxString name = FindFirstFreeName(parent, _("New site"));
2157
2158        CSiteManagerItemData_Site* pData = new CSiteManagerItemData_Site(server);
2159        if (connected)
2160                pData->connected_item = 0;
2161
2162        wxTreeItemId newItem = pTree->AppendItem(parent, name, 2, 2, pData);
2163        pTree->SortChildren(parent);
2164        pTree->EnsureVisible(newItem);
2165        pTree->SafeSelectItem(newItem);
2166#ifdef __WXMAC__
2167        // Need to trigger dirty processing of generic tree control.
2168        // Else edit control will be hidden behind item
2169        pTree->OnInternalIdle();
2170#endif
2171        pTree->EditLabel(newItem);
2172}
2173
2174void CSiteManagerDialog::AddNewBookmark(wxTreeItemId parent)
2175{
2176        wxTreeCtrlEx *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrlEx);
2177        if (!pTree)
2178                return;
2179
2180        wxString name = FindFirstFreeName(parent, _("New bookmark"));
2181
2182        wxTreeItemId newItem = pTree->AppendItem(parent, name, 3, 3, new CSiteManagerItemData(CSiteManagerItemData::BOOKMARK));
2183        pTree->SortChildren(parent);
2184        pTree->EnsureVisible(newItem);
2185        pTree->SafeSelectItem(newItem);
2186        pTree->EditLabel(newItem);
2187}
2188
2189void CSiteManagerDialog::RememberLastSelected()
2190{
2191        COptions::Get()->SetOption(OPTION_SITEMANAGER_LASTSELECTED, GetSitePath(false));
2192}
2193
2194void CSiteManagerDialog::OnContextMenu(wxTreeEvent& event)
2195{
2196        wxMenu* pMenu = wxXmlResource::Get()->LoadMenu(_T("ID_MENU_SITEMANAGER"));
2197        if (!pMenu)
2198                return;
2199
2200        m_contextMenuItem = event.GetItem();
2201
2202        PopupMenu(pMenu);
2203        delete pMenu;
2204}
2205
2206void CSiteManagerDialog::OnExportSelected(wxCommandEvent&)
2207{
2208        wxFileDialog dlg(this, _("Select file for exported sites"), wxString(),
2209                                        _T("sites.xml"), _T("XML files (*.xml)|*.xml"),
2210                                        wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
2211
2212        if (dlg.ShowModal() != wxID_OK)
2213                return;
2214
2215        CXmlFile xml(dlg.GetPath());
2216
2217        auto exportRoot = xml.CreateEmpty();
2218
2219        auto servers = exportRoot.append_child("Servers");
2220        SaveChild(servers, m_contextMenuItem);
2221
2222        if (!xml.Save(false)) {
2223                wxString msg = wxString::Format(_("Could not write \"%s\", the selected sites could not be exported: %s"), xml.GetFileName(), xml.GetError());
2224                wxMessageBoxEx(msg, _("Error writing xml file"), wxICON_ERROR);
2225        }
2226}
2227
2228void CSiteManagerDialog::OnBookmarkBrowse(wxCommandEvent&)
2229{
2230        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
2231        if (!pTree)
2232                return;
2233
2234        wxTreeItemId item = pTree->GetSelection();
2235        if (!item.IsOk())
2236                return;
2237
2238        CSiteManagerItemData* data = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
2239        if (!data || data->m_type != CSiteManagerItemData::BOOKMARK)
2240                return;
2241
2242        wxDirDialog dlg(this, _("Choose the local directory"), XRCCTRL(*this, "ID_BOOKMARK_LOCALDIR", wxTextCtrl)->GetValue(), wxDD_NEW_DIR_BUTTON);
2243        if (dlg.ShowModal() != wxID_OK)
2244                return;
2245
2246        XRCCTRL(*this, "ID_BOOKMARK_LOCALDIR", wxTextCtrl)->ChangeValue(dlg.GetPath());
2247}
2248
2249void CSiteManagerDialog::OnNewBookmark(wxCommandEvent&)
2250{
2251        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
2252        if (!pTree)
2253                return;
2254
2255        wxTreeItemId item = pTree->GetSelection();
2256        if (!item.IsOk() || IsPredefinedItem(item))
2257                return;
2258
2259        CSiteManagerItemData *pData = (CSiteManagerItemData *)pTree->GetItemData(item);
2260        if (!pData)
2261                return;
2262        if (pData->m_type == CSiteManagerItemData::BOOKMARK)
2263                item = pTree->GetItemParent(item);
2264
2265        if (!Verify())
2266                return;
2267
2268        AddNewBookmark(item);
2269}
2270
2271wxString CSiteManagerDialog::GetSitePath(wxTreeItemId item, bool stripBookmark)
2272{
2273        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
2274        if (!pTree)
2275                return wxString();
2276
2277        CSiteManagerItemData* pData = static_cast<CSiteManagerItemData* >(pTree->GetItemData(item));
2278        if (!pData)
2279                return wxString();
2280
2281        if (stripBookmark && pData->m_type == CSiteManagerItemData::BOOKMARK)
2282                item = pTree->GetItemParent(item);
2283
2284        wxString path;
2285        while (item)
2286        {
2287                if (item == m_predefinedSites)
2288                        return _T("1") + path;
2289                else if (item == m_ownSites)
2290                        return _T("0") + path;
2291                path = _T("/") + CSiteManager::EscapeSegment(pTree->GetItemText(item)) + path;
2292
2293                item = pTree->GetItemParent(item);
2294        }
2295
2296        return _T("0") + path;
2297}
2298
2299wxString CSiteManagerDialog::GetSitePath(bool stripBookmark)
2300{
2301        wxTreeCtrl *pTree = XRCCTRL(*this, "ID_SITETREE", wxTreeCtrl);
2302        if (!pTree)
2303                return wxString();
2304
2305        wxTreeItemId item = pTree->GetSelection();
2306        if (!item.IsOk())
2307                return wxString();
2308
2309        return GetSitePath(item, stripBookmark);
2310}
2311
2312void CSiteManagerDialog::SetProtocol(ServerProtocol protocol)
2313{
2314        wxChoice* pProtocol = XRCCTRL(*this, "ID_PROTOCOL", wxChoice);
2315        wxChoice* pEncryption = XRCCTRL(*this, "ID_ENCRYPTION", wxChoice);
2316        wxStaticText* pEncryptionDesc = XRCCTRL(*this, "ID_ENCRYPTION_DESC", wxStaticText);
2317
2318        if (protocol == SFTP) {
2319                pEncryption->Hide();
2320                pEncryptionDesc->Hide();
2321                pProtocol->SetSelection(1);
2322        }
2323        else {
2324                switch (protocol) {
2325                default:
2326                case FTP:
2327                        pEncryption->SetSelection(0);
2328                        break;
2329                case FTPES:
2330                        pEncryption->SetSelection(1);
2331                        break;
2332                case FTPS:
2333                        pEncryption->SetSelection(2);
2334                        break;
2335                case INSECURE_FTP:
2336                        pEncryption->SetSelection(3);
2337                        break;
2338                }
2339                pEncryption->Show();
2340                pEncryptionDesc->Show();
2341                pProtocol->SetSelection(0);
2342        }
2343}
2344
2345
2346ServerProtocol CSiteManagerDialog::GetProtocol() const
2347{
2348        wxChoice* pProtocol = XRCCTRL(*this, "ID_PROTOCOL", wxChoice);
2349        wxChoice* pEncryption = XRCCTRL(*this, "ID_ENCRYPTION", wxChoice);
2350
2351        if (pProtocol->GetSelection() == 1)
2352                return SFTP;
2353
2354        switch (pEncryption->GetSelection())
2355        {
2356        default:
2357        case 0:
2358                return FTP;
2359        case 1:
2360                return FTPES;
2361        case 2:
2362                return FTPS;
2363        case 3:
2364                return INSECURE_FTP;
2365        }
2366}
2367
2368LogonType CSiteManagerDialog::GetLogonType() const
2369{
2370        return CServer::GetLogonTypeFromName(xrc_call(*this, "ID_LOGONTYPE", &wxChoice::GetStringSelection));
2371}
Note: See TracBrowser for help on using the repository browser.